www.mooseframework.org
PenetrationThread.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 // Moose
11 #include "PenetrationThread.h"
12 #include "ParallelUniqueId.h"
13 #include "FindContactPoint.h"
14 #include "NearestNodeLocator.h"
15 #include "SubProblem.h"
16 #include "MooseVariableFE.h"
17 #include "MooseMesh.h"
18 #include "MooseUtils.h"
19 
20 #include "libmesh/threads.h"
21 
22 #include <algorithm>
23 
24 // Mutex to use when accessing _penetration_info;
25 Threads::spin_mutex pinfo_mutex;
26 
28  SubProblem & subproblem,
29  const MooseMesh & mesh,
30  BoundaryID primary_boundary,
31  BoundaryID secondary_boundary,
32  std::map<dof_id_type, PenetrationInfo *> & penetration_info,
33  bool check_whether_reasonable,
34  bool update_location,
35  Real tangential_tolerance,
36  bool do_normal_smoothing,
37  Real normal_smoothing_distance,
38  PenetrationLocator::NORMAL_SMOOTHING_METHOD normal_smoothing_method,
39  std::vector<std::vector<FEBase *>> & fes,
40  FEType & fe_type,
41  NearestNodeLocator & nearest_node,
42  const std::map<dof_id_type, std::vector<dof_id_type>> & node_to_elem_map,
43  const std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> & bc_tuples)
44  : _subproblem(subproblem),
45  _mesh(mesh),
46  _primary_boundary(primary_boundary),
47  _secondary_boundary(secondary_boundary),
48  _penetration_info(penetration_info),
49  _check_whether_reasonable(check_whether_reasonable),
50  _update_location(update_location),
51  _tangential_tolerance(tangential_tolerance),
52  _do_normal_smoothing(do_normal_smoothing),
53  _normal_smoothing_distance(normal_smoothing_distance),
54  _normal_smoothing_method(normal_smoothing_method),
55  _nodal_normal_x(NULL),
56  _nodal_normal_y(NULL),
57  _nodal_normal_z(NULL),
58  _fes(fes),
59  _fe_type(fe_type),
60  _nearest_node(nearest_node),
61  _node_to_elem_map(node_to_elem_map),
62  _bc_tuples(bc_tuples)
63 {
64 }
65 
66 // Splitting Constructor
68  : _subproblem(x._subproblem),
69  _mesh(x._mesh),
70  _primary_boundary(x._primary_boundary),
71  _secondary_boundary(x._secondary_boundary),
72  _penetration_info(x._penetration_info),
73  _check_whether_reasonable(x._check_whether_reasonable),
74  _update_location(x._update_location),
75  _tangential_tolerance(x._tangential_tolerance),
76  _do_normal_smoothing(x._do_normal_smoothing),
77  _normal_smoothing_distance(x._normal_smoothing_distance),
78  _normal_smoothing_method(x._normal_smoothing_method),
79  _fes(x._fes),
80  _fe_type(x._fe_type),
81  _nearest_node(x._nearest_node),
82  _node_to_elem_map(x._node_to_elem_map),
83  _bc_tuples(x._bc_tuples)
84 {
85 }
86 
87 void
89 {
90  ParallelUniqueId puid;
91  _tid = puid.id;
92 
93  // Must get the variables every time this is run because _tid can change
96  {
100  }
101 
102  for (const auto & node_id : range)
103  {
104  const Node & node = _mesh.nodeRef(node_id);
105 
106  // We're going to get a reference to the pointer for the pinfo for this node
107  // This will allow us to manipulate this pointer without having to go through
108  // the _penetration_info map... meaning this is the only mutex we'll have to do!
109  pinfo_mutex.lock();
110  PenetrationInfo *& info = _penetration_info[node.id()];
111  pinfo_mutex.unlock();
112 
113  std::vector<PenetrationInfo *> p_info;
114  bool info_set(false);
115 
116  // See if we already have info about this node
117  if (info)
118  {
119  FEBase * fe_elem = _fes[_tid][info->_elem->dim()];
120  FEBase * fe_side = _fes[_tid][info->_side->dim()];
121 
122  if (!_update_location && (info->_distance >= 0 || info->isCaptured()))
123  {
124  const Point contact_ref = info->_closest_point_ref;
125  bool contact_point_on_side(false);
126 
127  // Secondary position must be the previous contact point
128  // Use the previous reference coordinates
129  std::vector<Point> points(1);
130  points[0] = contact_ref;
131  const std::vector<Point> & secondary_pos = fe_side->get_xyz();
132 
134  fe_elem,
135  fe_side,
136  _fe_type,
137  secondary_pos[0],
138  false,
140  contact_point_on_side);
141 
142  // Restore the original reference coordinates
143  info->_closest_point_ref = contact_ref;
144  // Just calculated as the distance of the contact point off the surface (0). Set to 0 to
145  // avoid round-off.
146  info->_distance = 0.0;
147  info_set = true;
148  }
149  else
150  {
151  Real old_tangential_distance(info->_tangential_distance);
152  bool contact_point_on_side(false);
153 
155  fe_elem,
156  fe_side,
157  _fe_type,
158  node,
159  false,
161  contact_point_on_side);
162 
163  if (contact_point_on_side)
164  {
165  if (info->_tangential_distance <= 0.0) // on the face
166  {
167  info_set = true;
168  }
169  else if (info->_tangential_distance > 0.0 && old_tangential_distance > 0.0)
170  { // off the face but within tolerance, was that way on the last step too
171  if (info->_side->dim() == 2 && info->_off_edge_nodes.size() < 2)
172  { // Closest point on face is on a node rather than an edge. Another
173  // face might be a better candidate.
174  }
175  else
176  {
177  info_set = true;
178  }
179  }
180  }
181  }
182  }
183 
184  if (!info_set)
185  {
186  const Node * closest_node = _nearest_node.nearestNode(node.id());
187  auto node_to_elem_pair = _node_to_elem_map.find(closest_node->id());
188  mooseAssert(node_to_elem_pair != _node_to_elem_map.end(),
189  "Missing entry in node to elem map");
190  const std::vector<dof_id_type> & closest_elems = node_to_elem_pair->second;
191 
192  for (const auto & elem_id : closest_elems)
193  {
194  const Elem * elem = _mesh.elemPtr(elem_id);
195 
196  std::vector<PenetrationInfo *> thisElemInfo;
197  std::vector<const Node *> nodesThatMustBeOnSide;
198  nodesThatMustBeOnSide.push_back(closest_node);
200  thisElemInfo, p_info, &node, elem, nodesThatMustBeOnSide, _check_whether_reasonable);
201  }
202 
203  if (p_info.size() == 1)
204  {
205  if (p_info[0]->_tangential_distance <= _tangential_tolerance)
206  {
207  switchInfo(info, p_info[0]);
208  info_set = true;
209  }
210  }
211  else if (p_info.size() > 1)
212  {
213 
214  // Loop through all pairs of faces, and check for contact on ridge betweeen each face pair
215  std::vector<RidgeData> ridgeDataVec;
216  for (unsigned int i = 0; i + 1 < p_info.size(); ++i)
217  for (unsigned int j = i + 1; j < p_info.size(); ++j)
218  {
219  Point closest_coor;
220  Real tangential_distance(0.0);
221  const Node * closest_node_on_ridge = NULL;
222  unsigned int index = 0;
223  Point closest_coor_ref;
224  bool found_ridge_contact_point = findRidgeContactPoint(closest_coor,
225  tangential_distance,
226  closest_node_on_ridge,
227  index,
228  closest_coor_ref,
229  p_info,
230  i,
231  j);
232  if (found_ridge_contact_point)
233  {
234  RidgeData rpd;
235  rpd._closest_coor = closest_coor;
236  rpd._tangential_distance = tangential_distance;
237  rpd._closest_node = closest_node_on_ridge;
238  rpd._index = index;
239  rpd._closest_coor_ref = closest_coor_ref;
240  ridgeDataVec.push_back(rpd);
241  }
242  }
243 
244  if (ridgeDataVec.size() > 0) // Either find the ridge pair that is the best or find a peak
245  {
246  // Group together ridges for which we are off the edge of a common node.
247  // Those are peaks.
248  std::vector<RidgeSetData> ridgeSetDataVec;
249  for (unsigned int i = 0; i < ridgeDataVec.size(); ++i)
250  {
251  bool foundSetWithMatchingNode = false;
252  for (unsigned int j = 0; j < ridgeSetDataVec.size(); ++j)
253  {
254  if (ridgeDataVec[i]._closest_node != NULL &&
255  ridgeDataVec[i]._closest_node == ridgeSetDataVec[j]._closest_node)
256  {
257  foundSetWithMatchingNode = true;
258  ridgeSetDataVec[j]._ridge_data_vec.push_back(ridgeDataVec[i]);
259  break;
260  }
261  }
262  if (!foundSetWithMatchingNode)
263  {
264  RidgeSetData rsd;
266  rsd._ridge_data_vec.push_back(ridgeDataVec[i]);
267  rsd._closest_node = ridgeDataVec[i]._closest_node;
268  ridgeSetDataVec.push_back(rsd);
269  }
270  }
271  // Compute distance to each set of ridges
272  for (unsigned int i = 0; i < ridgeSetDataVec.size(); ++i)
273  {
274  if (ridgeSetDataVec[i]._closest_node !=
275  NULL) // Either a peak or off the edge of single ridge
276  {
277  if (ridgeSetDataVec[i]._ridge_data_vec.size() == 1) // off edge of single ridge
278  {
279  if (ridgeSetDataVec[i]._ridge_data_vec[0]._tangential_distance <=
280  _tangential_tolerance) // off within tolerance
281  {
282  ridgeSetDataVec[i]._closest_coor =
283  ridgeSetDataVec[i]._ridge_data_vec[0]._closest_coor;
284  Point contact_point_vec = node - ridgeSetDataVec[i]._closest_coor;
285  ridgeSetDataVec[i]._distance = contact_point_vec.norm();
286  }
287  }
288  else // several ridges join at common node to make peak. The common node is the
289  // contact point
290  {
291  ridgeSetDataVec[i]._closest_coor = *ridgeSetDataVec[i]._closest_node;
292  Point contact_point_vec = node - ridgeSetDataVec[i]._closest_coor;
293  ridgeSetDataVec[i]._distance = contact_point_vec.norm();
294  }
295  }
296  else // on a single ridge
297  {
298  ridgeSetDataVec[i]._closest_coor =
299  ridgeSetDataVec[i]._ridge_data_vec[0]._closest_coor;
300  Point contact_point_vec = node - ridgeSetDataVec[i]._closest_coor;
301  ridgeSetDataVec[i]._distance = contact_point_vec.norm();
302  }
303  }
304  // Find the set of ridges closest to us.
305  unsigned int closest_ridge_set_index(0);
306  Real closest_distance(ridgeSetDataVec[0]._distance);
307  Point closest_point(ridgeSetDataVec[0]._closest_coor);
308  for (unsigned int i = 1; i < ridgeSetDataVec.size(); ++i)
309  {
310  if (ridgeSetDataVec[i]._distance < closest_distance)
311  {
312  closest_ridge_set_index = i;
313  closest_distance = ridgeSetDataVec[i]._distance;
314  closest_point = ridgeSetDataVec[i]._closest_coor;
315  }
316  }
317 
318  if (closest_distance <
319  std::numeric_limits<Real>::max()) // contact point is on the closest ridge set
320  {
321  // find the face in the ridge set with the smallest index, assign that one to the
322  // interaction
323  unsigned int face_index(std::numeric_limits<unsigned int>::max());
324  for (unsigned int i = 0;
325  i < ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec.size();
326  ++i)
327  {
328  if (ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[i]._index < face_index)
329  face_index = ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[i]._index;
330  }
331 
332  mooseAssert(face_index < std::numeric_limits<unsigned int>::max(),
333  "face_index invalid");
334 
335  p_info[face_index]->_closest_point = closest_point;
336  p_info[face_index]->_distance =
337  (p_info[face_index]->_distance >= 0.0 ? 1.0 : -1.0) * closest_distance;
338  // Calculate the normal as the vector from the ridge to the point only if we're not
339  // doing normal
340  // smoothing. Normal smoothing will average out the normals on its own.
342  {
343  Point normal(closest_point - node);
344  const Real len(normal.norm());
345  if (len > 0)
346  {
347  normal /= len;
348  }
349  const Real dot(normal * p_info[face_index]->_normal);
350  if (dot < 0)
351  {
352  normal *= -1;
353  }
354  p_info[face_index]->_normal = normal;
355  }
356  p_info[face_index]->_tangential_distance = 0.0;
357 
358  Point closest_point_ref;
359  if (ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec.size() ==
360  1) // contact with a single ridge rather than a peak
361  {
362  p_info[face_index]->_tangential_distance =
363  ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[0]._tangential_distance;
364  p_info[face_index]->_closest_point_ref =
365  ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[0]._closest_coor_ref;
366  }
367  else
368  { // peak
369  const Node * closest_node_on_face;
370  bool restricted = restrictPointToFace(p_info[face_index]->_closest_point_ref,
371  closest_node_on_face,
372  p_info[face_index]->_side);
373  if (restricted)
374  {
375  if (closest_node_on_face != ridgeSetDataVec[closest_ridge_set_index]._closest_node)
376  {
377  mooseError("Closest node when restricting point to face != closest node from "
378  "RidgeSetData");
379  }
380  }
381  }
382 
383  FEBase * fe = _fes[_tid][p_info[face_index]->_side->dim()];
384  std::vector<Point> points(1);
385  points[0] = p_info[face_index]->_closest_point_ref;
386  fe->reinit(p_info[face_index]->_side, &points);
387  p_info[face_index]->_side_phi = fe->get_phi();
388  p_info[face_index]->_side_grad_phi = fe->get_dphi();
389  p_info[face_index]->_dxyzdxi = fe->get_dxyzdxi();
390  p_info[face_index]->_dxyzdeta = fe->get_dxyzdeta();
391  p_info[face_index]->_d2xyzdxideta = fe->get_d2xyzdxideta();
392 
393  switchInfo(info, p_info[face_index]);
394  info_set = true;
395  }
396  else
397  { // todo:remove invalid ridge cases so they don't mess up individual face competition????
398  }
399  }
400 
401  if (!info_set) // contact wasn't on a ridge -- compete individual interactions
402  {
403  unsigned int best(0), i(1);
404  do
405  {
406  CompeteInteractionResult CIResult = competeInteractions(p_info[best], p_info[i]);
407  if (CIResult == FIRST_WINS)
408  {
409  i++;
410  }
411  else if (CIResult == SECOND_WINS)
412  {
413  best = i;
414  i++;
415  }
416  else if (CIResult == NEITHER_WINS)
417  {
418  best = i + 1;
419  i += 2;
420  }
421  } while (i < p_info.size() && best < p_info.size());
422  if (best < p_info.size())
423  {
424  switchInfo(info, p_info[best]);
425  info_set = true;
426  }
427  }
428  }
429  }
430 
431  if (!info_set)
432  {
433  // If penetration is not detected within the saved patch, it is possible
434  // that the secondary node has moved outside the saved patch. So, the patch
435  // for the secondary nodes saved in _recheck_secondary_nodes has to be updated
436  // and the penetration detection has to be re-run on the updated patch.
437 
438  _recheck_secondary_nodes.push_back(node_id);
439 
440  delete info;
441  info = NULL;
442  }
443  else
444  {
445  smoothNormal(info, p_info, node);
446  FEBase * fe = _fes[_tid][info->_side->dim()];
447  computeSlip(*fe, *info);
448  }
449 
450  for (unsigned int j = 0; j < p_info.size(); ++j)
451  {
452  if (p_info[j])
453  {
454  delete p_info[j];
455  p_info[j] = NULL;
456  }
457  }
458  }
459 }
460 
461 void
463 {
465  other._recheck_secondary_nodes.begin(),
466  other._recheck_secondary_nodes.end());
467 }
468 
469 void
471 {
472  mooseAssert(infoNew != NULL, "infoNew object is null");
473  if (info)
474  {
475  infoNew->_starting_elem = info->_starting_elem;
476  infoNew->_starting_side_num = info->_starting_side_num;
477  infoNew->_starting_closest_point_ref = info->_starting_closest_point_ref;
478  infoNew->_incremental_slip = info->_incremental_slip;
479  infoNew->_accumulated_slip = info->_accumulated_slip;
480  infoNew->_accumulated_slip_old = info->_accumulated_slip_old;
481  infoNew->_frictional_energy = info->_frictional_energy;
482  infoNew->_frictional_energy_old = info->_frictional_energy_old;
483  infoNew->_contact_force = info->_contact_force;
484  infoNew->_contact_force_old = info->_contact_force_old;
485  infoNew->_lagrange_multiplier = info->_lagrange_multiplier;
486  infoNew->_lagrange_multiplier_slip = info->_lagrange_multiplier_slip;
487  infoNew->_locked_this_step = info->_locked_this_step;
488  infoNew->_stick_locked_this_step = info->_stick_locked_this_step;
489  infoNew->_mech_status = info->_mech_status;
490  infoNew->_mech_status_old = info->_mech_status_old;
491  }
492  else
493  {
494  infoNew->_starting_elem = infoNew->_elem;
495  infoNew->_starting_side_num = infoNew->_side_num;
497  }
498  delete info;
499  info = infoNew;
500  infoNew = NULL; // Set this to NULL so that we don't delete it (now owned by _penetration_info).
501 }
502 
505 {
506 
508 
510  pi2->_tangential_distance > _tangential_tolerance) // out of tol on both faces
511  result = NEITHER_WINS;
512 
513  else if (pi1->_tangential_distance == 0.0 &&
514  pi2->_tangential_distance > 0.0) // on face 1, off face 2
515  result = FIRST_WINS;
516 
517  else if (pi2->_tangential_distance == 0.0 &&
518  pi1->_tangential_distance > 0.0) // on face 2, off face 1
519  result = SECOND_WINS;
520 
521  else if (pi1->_tangential_distance <= _tangential_tolerance &&
522  pi2->_tangential_distance > _tangential_tolerance) // in face 1 tol, out of face 2 tol
523  result = FIRST_WINS;
524 
525  else if (pi2->_tangential_distance <= _tangential_tolerance &&
526  pi1->_tangential_distance > _tangential_tolerance) // in face 2 tol, out of face 1 tol
527  result = SECOND_WINS;
528 
529  else if (pi1->_tangential_distance == 0.0 && pi2->_tangential_distance == 0.0) // on both faces
530  result = competeInteractionsBothOnFace(pi1, pi2);
531 
532  else if (pi1->_tangential_distance <= _tangential_tolerance &&
533  pi2->_tangential_distance <= _tangential_tolerance) // off but within tol of both faces
534  {
536  if (cer == COMMON_EDGE || cer == COMMON_NODE) // ridge case.
537  {
538  // We already checked for ridges, and it got rejected, so neither face must be valid
539  result = NEITHER_WINS;
540  // mooseError("Erroneously encountered ridge case");
541  }
542  else if (cer == EDGE_AND_COMMON_NODE) // off side of face, off corner of another face. Favor
543  // the off-side face
544  {
545  if (pi1->_off_edge_nodes.size() == pi2->_off_edge_nodes.size())
546  mooseError("Invalid off_edge_nodes counts");
547 
548  else if (pi1->_off_edge_nodes.size() == 2)
549  result = FIRST_WINS;
550 
551  else if (pi2->_off_edge_nodes.size() == 2)
552  result = SECOND_WINS;
553 
554  else
555  mooseError("Invalid off_edge_nodes counts");
556  }
557  else // The node projects to both faces within tangential tolerance.
558  result = competeInteractionsBothOnFace(pi1, pi2);
559  }
560 
561  return result;
562 }
563 
566 {
568 
569  if (pi1->_distance >= 0.0 && pi2->_distance < 0.0)
570  result = FIRST_WINS; // favor face with positive distance (penetrated) -- first in this case
571 
572  else if (pi2->_distance >= 0.0 && pi1->_distance < 0.0)
573  result = SECOND_WINS; // favor face with positive distance (penetrated) -- second in this case
574 
575  // TODO: This logic below could cause an abrupt jump from one face to the other with small mesh
576  // movement. If there is some way to smooth the transition, we should do it.
578  result = FIRST_WINS; // otherwise, favor the closer face -- first in this case
579 
581  result = SECOND_WINS; // otherwise, favor the closer face -- second in this case
582 
583  else // Equal within tolerance. Favor the one with a smaller element id (for repeatibility)
584  {
585  if (pi1->_elem->id() < pi2->_elem->id())
586  result = FIRST_WINS;
587 
588  else
589  result = SECOND_WINS;
590  }
591 
592  return result;
593 }
594 
597 {
598  CommonEdgeResult common_edge(NO_COMMON);
599  const std::vector<const Node *> & off_edge_nodes1 = pi1->_off_edge_nodes;
600  const std::vector<const Node *> & off_edge_nodes2 = pi2->_off_edge_nodes;
601  const unsigned dim1 = pi1->_side->dim();
602 
603  if (dim1 == 1)
604  {
605  mooseAssert(pi2->_side->dim() == 1, "Incompatible dimensions.");
606  mooseAssert(off_edge_nodes1.size() < 2 && off_edge_nodes2.size() < 2,
607  "off_edge_nodes size should be <2 for 2D contact");
608  if (off_edge_nodes1.size() == 1 && off_edge_nodes2.size() == 1 &&
609  off_edge_nodes1[0] == off_edge_nodes2[0])
610  common_edge = COMMON_EDGE;
611  }
612  else
613  {
614  mooseAssert(dim1 == 2 && pi2->_side->dim() == 2, "Incompatible dimensions.");
615  mooseAssert(off_edge_nodes1.size() < 3 && off_edge_nodes2.size() < 3,
616  "off_edge_nodes size should be <3 for 3D contact");
617  if (off_edge_nodes1.size() == 1)
618  {
619  if (off_edge_nodes2.size() == 1)
620  {
621  if (off_edge_nodes1[0] == off_edge_nodes2[0])
622  common_edge = COMMON_NODE;
623  }
624  else if (off_edge_nodes2.size() == 2)
625  {
626  if (off_edge_nodes1[0] == off_edge_nodes2[0] || off_edge_nodes1[0] == off_edge_nodes2[1])
627  common_edge = EDGE_AND_COMMON_NODE;
628  }
629  }
630  else if (off_edge_nodes1.size() == 2)
631  {
632  if (off_edge_nodes2.size() == 1)
633  {
634  if (off_edge_nodes1[0] == off_edge_nodes2[0] || off_edge_nodes1[1] == off_edge_nodes2[0])
635  common_edge = EDGE_AND_COMMON_NODE;
636  }
637  else if (off_edge_nodes2.size() == 2)
638  {
639  if ((off_edge_nodes1[0] == off_edge_nodes2[0] &&
640  off_edge_nodes1[1] == off_edge_nodes2[1]) ||
641  (off_edge_nodes1[1] == off_edge_nodes2[0] && off_edge_nodes1[0] == off_edge_nodes2[1]))
642  common_edge = COMMON_EDGE;
643  }
644  }
645  }
646  return common_edge;
647 }
648 
649 bool
651  Real & tangential_distance,
652  const Node *& closest_node,
653  unsigned int & index,
654  Point & contact_point_ref,
655  std::vector<PenetrationInfo *> & p_info,
656  const unsigned int index1,
657  const unsigned int index2)
658 {
659  tangential_distance = 0.0;
660  closest_node = NULL;
661  PenetrationInfo * pi1 = p_info[index1];
662  PenetrationInfo * pi2 = p_info[index2];
663  const unsigned sidedim(pi1->_side->dim());
664  mooseAssert(sidedim == pi2->_side->dim(), "Incompatible dimensionalities");
665 
666  // Nodes on faces for the two interactions
667  std::vector<const Node *> side1_nodes;
668  getSideCornerNodes(pi1->_side, side1_nodes);
669  std::vector<const Node *> side2_nodes;
670  getSideCornerNodes(pi2->_side, side2_nodes);
671 
672  std::sort(side1_nodes.begin(), side1_nodes.end());
673  std::sort(side2_nodes.begin(), side2_nodes.end());
674 
675  // Find nodes shared by the two faces
676  std::vector<const Node *> common_nodes;
677  std::set_intersection(side1_nodes.begin(),
678  side1_nodes.end(),
679  side2_nodes.begin(),
680  side2_nodes.end(),
681  std::inserter(common_nodes, common_nodes.end()));
682 
683  if (common_nodes.size() != sidedim)
684  return false;
685 
686  bool found_point1, found_point2;
687  Point closest_coor_ref1(pi1->_closest_point_ref);
688  const Node * closest_node1;
689  found_point1 = restrictPointToSpecifiedEdgeOfFace(
690  closest_coor_ref1, closest_node1, pi1->_side, common_nodes);
691 
692  Point closest_coor_ref2(pi2->_closest_point_ref);
693  const Node * closest_node2;
694  found_point2 = restrictPointToSpecifiedEdgeOfFace(
695  closest_coor_ref2, closest_node2, pi2->_side, common_nodes);
696 
697  if (!found_point1 || !found_point2)
698  return false;
699 
700  // if (sidedim == 2)
701  // {
702  // TODO:
703  // We have the parametric coordinates of the closest intersection point for both faces.
704  // We need to find a point somewhere in the middle of them so there's not an abrupt jump.
705  // Find that point by taking dot products of vector from contact point to secondary node point
706  // with face normal vectors to see which face we're closer to.
707  // }
708 
709  FEBase * fe = NULL;
710  std::vector<Point> points(1);
711 
712  // We have to pick one of the two faces to own the contact point. It doesn't really matter
713  // which one we pick. For repeatibility, pick the face with the lowest index.
714  if (index1 < index2)
715  {
716  fe = _fes[_tid][pi1->_side->dim()];
717  contact_point_ref = closest_coor_ref1;
718  points[0] = closest_coor_ref1;
719  fe->reinit(pi1->_side, &points);
720  index = index1;
721  }
722  else
723  {
724  fe = _fes[_tid][pi2->_side->dim()];
725  contact_point_ref = closest_coor_ref2;
726  points[0] = closest_coor_ref2;
727  fe->reinit(pi2->_side, &points);
728  index = index2;
729  }
730 
731  contact_point = fe->get_xyz()[0];
732 
733  if (sidedim == 2)
734  {
735  if (closest_node1) // point is off the ridge between the two elements
736  {
737  mooseAssert((closest_node1 == closest_node2 || closest_node2 == NULL),
738  "If off edge of ridge, closest node must be the same on both elements");
739  closest_node = closest_node1;
740 
741  RealGradient off_face = *closest_node1 - contact_point;
742  tangential_distance = off_face.norm();
743  }
744  }
745 
746  return true;
747 }
748 
749 void
750 PenetrationThread::getSideCornerNodes(const Elem * side, std::vector<const Node *> & corner_nodes)
751 {
752  const ElemType t(side->type());
753  corner_nodes.clear();
754 
755  corner_nodes.push_back(side->node_ptr(0));
756  corner_nodes.push_back(side->node_ptr(1));
757  switch (t)
758  {
759  case EDGE2:
760  case EDGE3:
761  case EDGE4:
762  {
763  break;
764  }
765 
766  case TRI3:
767  case TRI6:
768  case TRI7:
769  {
770  corner_nodes.push_back(side->node_ptr(2));
771  break;
772  }
773 
774  case QUAD4:
775  case QUAD8:
776  case QUAD9:
777  {
778  corner_nodes.push_back(side->node_ptr(2));
779  corner_nodes.push_back(side->node_ptr(3));
780  break;
781  }
782 
783  default:
784  {
785  mooseError("Unsupported face type: ", t);
786  break;
787  }
788  }
789 }
790 
791 bool
793  const Node *& closest_node,
794  const Elem * side,
795  const std::vector<const Node *> & edge_nodes)
796 {
797  const ElemType t = side->type();
798  Real & xi = p(0);
799  Real & eta = p(1);
800  closest_node = NULL;
801 
802  std::vector<unsigned int> local_node_indices;
803  for (const auto & edge_node : edge_nodes)
804  {
805  unsigned int local_index = side->get_node_index(edge_node);
806  if (local_index == libMesh::invalid_uint)
807  mooseError("Side does not contain node");
808  local_node_indices.push_back(local_index);
809  }
810  mooseAssert(local_node_indices.size() == side->dim(),
811  "Number of edge nodes must match side dimensionality");
812  std::sort(local_node_indices.begin(), local_node_indices.end());
813 
814  bool off_of_this_edge = false;
815 
816  switch (t)
817  {
818  case EDGE2:
819  case EDGE3:
820  case EDGE4:
821  {
822  if (local_node_indices[0] == 0)
823  {
824  if (xi <= -1.0)
825  {
826  xi = -1.0;
827  off_of_this_edge = true;
828  closest_node = side->node_ptr(0);
829  }
830  }
831  else if (local_node_indices[0] == 1)
832  {
833  if (xi >= 1.0)
834  {
835  xi = 1.0;
836  off_of_this_edge = true;
837  closest_node = side->node_ptr(1);
838  }
839  }
840  else
841  {
842  mooseError("Invalid local node indices");
843  }
844  break;
845  }
846 
847  case TRI3:
848  case TRI6:
849  case TRI7:
850  {
851  if ((local_node_indices[0] == 0) && (local_node_indices[1] == 1))
852  {
853  if (eta <= 0.0)
854  {
855  eta = 0.0;
856  off_of_this_edge = true;
857  if (xi < 0.0)
858  closest_node = side->node_ptr(0);
859  else if (xi > 1.0)
860  closest_node = side->node_ptr(1);
861  }
862  }
863  else if ((local_node_indices[0] == 1) && (local_node_indices[1] == 2))
864  {
865  if ((xi + eta) > 1.0)
866  {
867  Real delta = (xi + eta - 1.0) / 2.0;
868  xi -= delta;
869  eta -= delta;
870  off_of_this_edge = true;
871  if (xi > 1.0)
872  closest_node = side->node_ptr(1);
873  else if (xi < 0.0)
874  closest_node = side->node_ptr(2);
875  }
876  }
877  else if ((local_node_indices[0] == 0) && (local_node_indices[1] == 2))
878  {
879  if (xi <= 0.0)
880  {
881  xi = 0.0;
882  off_of_this_edge = true;
883  if (eta > 1.0)
884  closest_node = side->node_ptr(2);
885  else if (eta < 0.0)
886  closest_node = side->node_ptr(0);
887  }
888  }
889  else
890  {
891  mooseError("Invalid local node indices");
892  }
893 
894  break;
895  }
896 
897  case QUAD4:
898  case QUAD8:
899  case QUAD9:
900  {
901  if ((local_node_indices[0] == 0) && (local_node_indices[1] == 1))
902  {
903  if (eta <= -1.0)
904  {
905  eta = -1.0;
906  off_of_this_edge = true;
907  if (xi < -1.0)
908  closest_node = side->node_ptr(0);
909  else if (xi > 1.0)
910  closest_node = side->node_ptr(1);
911  }
912  }
913  else if ((local_node_indices[0] == 1) && (local_node_indices[1] == 2))
914  {
915  if (xi >= 1.0)
916  {
917  xi = 1.0;
918  off_of_this_edge = true;
919  if (eta < -1.0)
920  closest_node = side->node_ptr(1);
921  else if (eta > 1.0)
922  closest_node = side->node_ptr(2);
923  }
924  }
925  else if ((local_node_indices[0] == 2) && (local_node_indices[1] == 3))
926  {
927  if (eta >= 1.0)
928  {
929  eta = 1.0;
930  off_of_this_edge = true;
931  if (xi < -1.0)
932  closest_node = side->node_ptr(3);
933  else if (xi > 1.0)
934  closest_node = side->node_ptr(2);
935  }
936  }
937  else if ((local_node_indices[0] == 0) && (local_node_indices[1] == 3))
938  {
939  if (xi <= -1.0)
940  {
941  xi = -1.0;
942  off_of_this_edge = true;
943  if (eta < -1.0)
944  closest_node = side->node_ptr(0);
945  else if (eta > 1.0)
946  closest_node = side->node_ptr(3);
947  }
948  }
949  else
950  {
951  mooseError("Invalid local node indices");
952  }
953  break;
954  }
955 
956  default:
957  {
958  mooseError("Unsupported face type: ", t);
959  break;
960  }
961  }
962  return off_of_this_edge;
963 }
964 
965 bool
966 PenetrationThread::restrictPointToFace(Point & p, const Node *& closest_node, const Elem * side)
967 {
968  const ElemType t(side->type());
969  Real & xi = p(0);
970  Real & eta = p(1);
971  closest_node = NULL;
972 
973  bool off_of_this_face(false);
974 
975  switch (t)
976  {
977  case EDGE2:
978  case EDGE3:
979  case EDGE4:
980  {
981  if (xi < -1.0)
982  {
983  xi = -1.0;
984  off_of_this_face = true;
985  closest_node = side->node_ptr(0);
986  }
987  else if (xi > 1.0)
988  {
989  xi = 1.0;
990  off_of_this_face = true;
991  closest_node = side->node_ptr(1);
992  }
993  break;
994  }
995 
996  case TRI3:
997  case TRI6:
998  case TRI7:
999  {
1000  if (eta < 0.0)
1001  {
1002  eta = 0.0;
1003  off_of_this_face = true;
1004  if (xi < 0.5)
1005  {
1006  closest_node = side->node_ptr(0);
1007  if (xi < 0.0)
1008  xi = 0.0;
1009  }
1010  else
1011  {
1012  closest_node = side->node_ptr(1);
1013  if (xi > 1.0)
1014  xi = 1.0;
1015  }
1016  }
1017  else if ((xi + eta) > 1.0)
1018  {
1019  Real delta = (xi + eta - 1.0) / 2.0;
1020  xi -= delta;
1021  eta -= delta;
1022  off_of_this_face = true;
1023  if (xi > 0.5)
1024  {
1025  closest_node = side->node_ptr(1);
1026  if (xi > 1.0)
1027  {
1028  xi = 1.0;
1029  eta = 0.0;
1030  }
1031  }
1032  else
1033  {
1034  closest_node = side->node_ptr(2);
1035  if (xi < 0.0)
1036  {
1037  xi = 0.0;
1038  eta = 1.0;
1039  }
1040  }
1041  }
1042  else if (xi < 0.0)
1043  {
1044  xi = 0.0;
1045  off_of_this_face = true;
1046  if (eta > 0.5)
1047  {
1048  closest_node = side->node_ptr(2);
1049  if (eta > 1.0)
1050  eta = 1.0;
1051  }
1052  else
1053  {
1054  closest_node = side->node_ptr(0);
1055  if (eta < 0.0)
1056  eta = 0.0;
1057  }
1058  }
1059  break;
1060  }
1061 
1062  case QUAD4:
1063  case QUAD8:
1064  case QUAD9:
1065  {
1066  if (eta < -1.0)
1067  {
1068  eta = -1.0;
1069  off_of_this_face = true;
1070  if (xi < 0.0)
1071  {
1072  closest_node = side->node_ptr(0);
1073  if (xi < -1.0)
1074  xi = -1.0;
1075  }
1076  else
1077  {
1078  closest_node = side->node_ptr(1);
1079  if (xi > 1.0)
1080  xi = 1.0;
1081  }
1082  }
1083  else if (xi > 1.0)
1084  {
1085  xi = 1.0;
1086  off_of_this_face = true;
1087  if (eta < 0.0)
1088  {
1089  closest_node = side->node_ptr(1);
1090  if (eta < -1.0)
1091  eta = -1.0;
1092  }
1093  else
1094  {
1095  closest_node = side->node_ptr(2);
1096  if (eta > 1.0)
1097  eta = 1.0;
1098  }
1099  }
1100  else if (eta > 1.0)
1101  {
1102  eta = 1.0;
1103  off_of_this_face = true;
1104  if (xi < 0.0)
1105  {
1106  closest_node = side->node_ptr(3);
1107  if (xi < -1.0)
1108  xi = -1.0;
1109  }
1110  else
1111  {
1112  closest_node = side->node_ptr(2);
1113  if (xi > 1.0)
1114  xi = 1.0;
1115  }
1116  }
1117  else if (xi < -1.0)
1118  {
1119  xi = -1.0;
1120  off_of_this_face = true;
1121  if (eta < 0.0)
1122  {
1123  closest_node = side->node_ptr(0);
1124  if (eta < -1.0)
1125  eta = -1.0;
1126  }
1127  else
1128  {
1129  closest_node = side->node_ptr(3);
1130  if (eta > 1.0)
1131  eta = 1.0;
1132  }
1133  }
1134  break;
1135  }
1136 
1137  default:
1138  {
1139  mooseError("Unsupported face type: ", t);
1140  break;
1141  }
1142  }
1143  return off_of_this_face;
1144 }
1145 
1146 bool
1148  const Elem * side,
1149  FEBase * fe,
1150  const Point * secondary_point,
1151  const Real tangential_tolerance)
1152 {
1153  unsigned int dim = primary_elem->dim();
1154 
1155  const std::vector<Point> & phys_point = fe->get_xyz();
1156 
1157  const std::vector<RealGradient> & dxyz_dxi = fe->get_dxyzdxi();
1158  const std::vector<RealGradient> & dxyz_deta = fe->get_dxyzdeta();
1159 
1160  Point ref_point;
1161 
1162  std::vector<Point> points(1); // Default constructor gives us a point at 0,0,0
1163 
1164  fe->reinit(side, &points);
1165 
1166  RealGradient d = *secondary_point - phys_point[0];
1167 
1168  const Real twosqrt2 = 2.8284; // way more precision than we actually need here
1169  Real max_face_length = side->hmax() + twosqrt2 * tangential_tolerance;
1170 
1171  RealVectorValue normal;
1172  if (dim - 1 == 2)
1173  {
1174  normal = dxyz_dxi[0].cross(dxyz_deta[0]);
1175  }
1176  else if (dim - 1 == 1)
1177  {
1178  const Node * const * elem_nodes = primary_elem->get_nodes();
1179  const Point in_plane_vector1 = *elem_nodes[1] - *elem_nodes[0];
1180  const Point in_plane_vector2 = *elem_nodes[2] - *elem_nodes[0];
1181 
1182  Point out_of_plane_normal = in_plane_vector1.cross(in_plane_vector2);
1183  out_of_plane_normal /= out_of_plane_normal.norm();
1184 
1185  normal = dxyz_dxi[0].cross(out_of_plane_normal);
1186  }
1187  else
1188  {
1189  return true;
1190  }
1191  normal /= normal.norm();
1192 
1193  const Real dot(d * normal);
1194 
1195  const RealGradient normcomp = dot * normal;
1196  const RealGradient tangcomp = d - normcomp;
1197 
1198  const Real tangdist = tangcomp.norm();
1199 
1200  // Increase the size of the zone that we consider if the vector from the face
1201  // to the node has a larger normal component
1202  const Real faceExpansionFactor = 2.0 * (1.0 + normcomp.norm() / d.norm());
1203 
1204  bool isReasonableCandidate = true;
1205  if (tangdist > faceExpansionFactor * max_face_length)
1206  {
1207  isReasonableCandidate = false;
1208  }
1209  return isReasonableCandidate;
1210 }
1211 
1212 void
1214 {
1215  // Slip is current projected position of secondary node minus
1216  // original projected position of secondary node
1217  std::vector<Point> points(1);
1218  points[0] = info._starting_closest_point_ref;
1219  const auto & side = _elem_side_builder(*info._starting_elem, info._starting_side_num);
1220  fe.reinit(&side, &points);
1221  const std::vector<Point> & starting_point = fe.get_xyz();
1222  info._incremental_slip = info._closest_point - starting_point[0];
1223  if (info.isCaptured())
1224  {
1225  info._frictional_energy =
1226  info._frictional_energy_old + info._contact_force * info._incremental_slip;
1227  info._accumulated_slip = info._accumulated_slip_old + info._incremental_slip.norm();
1228  }
1229 }
1230 
1231 void
1233  std::vector<PenetrationInfo *> & p_info,
1234  const Node & node)
1235 {
1237  {
1239  {
1240  // If we are within the smoothing distance of any edges or corners, find the
1241  // corner nodes for those edges/corners, and weights from distance to edge/corner
1242  std::vector<Real> edge_face_weights;
1243  std::vector<PenetrationInfo *> edge_face_info;
1244 
1245  getSmoothingFacesAndWeights(info, edge_face_info, edge_face_weights, p_info, node);
1246 
1247  mooseAssert(edge_face_info.size() == edge_face_weights.size(),
1248  "edge_face_info.size() != edge_face_weights.size()");
1249 
1250  if (edge_face_info.size() > 0)
1251  {
1252  // Smooth the normal using the weighting functions for all participating faces.
1253  RealVectorValue new_normal;
1254  Real this_face_weight = 1.0;
1255 
1256  for (unsigned int efwi = 0; efwi < edge_face_weights.size(); ++efwi)
1257  {
1258  PenetrationInfo * npi = edge_face_info[efwi];
1259  if (npi)
1260  new_normal += npi->_normal * edge_face_weights[efwi];
1261 
1262  this_face_weight -= edge_face_weights[efwi];
1263  }
1264  mooseAssert(this_face_weight >= (0.25 - 1e-8),
1265  "Sum of weights of other faces shouldn't exceed 0.75");
1266  new_normal += info->_normal * this_face_weight;
1267 
1268  const Real len = new_normal.norm();
1269  if (len > 0)
1270  new_normal /= len;
1271 
1272  info->_normal = new_normal;
1273  }
1274  }
1276  {
1277  // params.addParam<VariableName>("var_name","description");
1278  // getParam<VariableName>("var_name")
1279  info->_normal(0) = _nodal_normal_x->getValue(info->_side, info->_side_phi);
1280  info->_normal(1) = _nodal_normal_y->getValue(info->_side, info->_side_phi);
1281  info->_normal(2) = _nodal_normal_z->getValue(info->_side, info->_side_phi);
1282  const Real len(info->_normal.norm());
1283  if (len > 0)
1284  info->_normal /= len;
1285  }
1286  }
1287 }
1288 
1289 void
1291  std::vector<PenetrationInfo *> & edge_face_info,
1292  std::vector<Real> & edge_face_weights,
1293  std::vector<PenetrationInfo *> & p_info,
1294  const Node & secondary_node)
1295 {
1296  const Elem * side = info->_side;
1297  const Point & p = info->_closest_point_ref;
1298  std::set<dof_id_type> elems_to_exclude;
1299  elems_to_exclude.insert(info->_elem->id());
1300 
1301  std::vector<std::vector<const Node *>> edge_nodes;
1302 
1303  // Get the pairs of nodes along every edge that we are close enough to smooth with
1304  getSmoothingEdgeNodesAndWeights(p, side, edge_nodes, edge_face_weights);
1305  std::vector<Elem *> edge_neighbor_elems;
1306  edge_face_info.resize(edge_nodes.size(), NULL);
1307 
1308  std::vector<unsigned int> edges_without_neighbors;
1309 
1310  for (unsigned int i = 0; i < edge_nodes.size(); ++i)
1311  {
1312  // Sort all sets of edge nodes (needed for comparing edges)
1313  std::sort(edge_nodes[i].begin(), edge_nodes[i].end());
1314 
1315  std::vector<PenetrationInfo *> face_info_comm_edge;
1317  &secondary_node, elems_to_exclude, edge_nodes[i], face_info_comm_edge, p_info);
1318 
1319  if (face_info_comm_edge.size() == 0)
1320  edges_without_neighbors.push_back(i);
1321  else if (face_info_comm_edge.size() > 1)
1322  mooseError("Only one neighbor allowed per edge");
1323  else
1324  edge_face_info[i] = face_info_comm_edge[0];
1325  }
1326 
1327  // Remove edges without neighbors from the vector, starting from end
1328  std::vector<unsigned int>::reverse_iterator rit;
1329  for (rit = edges_without_neighbors.rbegin(); rit != edges_without_neighbors.rend(); ++rit)
1330  {
1331  unsigned int index = *rit;
1332  edge_nodes.erase(edge_nodes.begin() + index);
1333  edge_face_weights.erase(edge_face_weights.begin() + index);
1334  edge_face_info.erase(edge_face_info.begin() + index);
1335  }
1336 
1337  // Handle corner case
1338  if (edge_nodes.size() > 1)
1339  {
1340  if (edge_nodes.size() != 2)
1341  mooseError("Invalid number of smoothing edges");
1342 
1343  // find common node
1344  std::vector<const Node *> common_nodes;
1345  std::set_intersection(edge_nodes[0].begin(),
1346  edge_nodes[0].end(),
1347  edge_nodes[1].begin(),
1348  edge_nodes[1].end(),
1349  std::inserter(common_nodes, common_nodes.end()));
1350 
1351  if (common_nodes.size() != 1)
1352  mooseError("Invalid number of common nodes");
1353 
1354  for (const auto & pinfo : edge_face_info)
1355  elems_to_exclude.insert(pinfo->_elem->id());
1356 
1357  std::vector<PenetrationInfo *> face_info_comm_edge;
1359  &secondary_node, elems_to_exclude, common_nodes, face_info_comm_edge, p_info);
1360 
1361  unsigned int num_corner_neighbors = face_info_comm_edge.size();
1362 
1363  if (num_corner_neighbors > 0)
1364  {
1365  Real fw0 = edge_face_weights[0];
1366  Real fw1 = edge_face_weights[1];
1367 
1368  // Corner weight is product of edge weights. Spread out over multiple neighbors.
1369  Real fw_corner = (fw0 * fw1) / static_cast<Real>(num_corner_neighbors);
1370 
1371  // Adjust original edge weights
1372  edge_face_weights[0] *= (1.0 - fw1);
1373  edge_face_weights[1] *= (1.0 - fw0);
1374 
1375  for (unsigned int i = 0; i < num_corner_neighbors; ++i)
1376  {
1377  edge_face_weights.push_back(fw_corner);
1378  edge_face_info.push_back(face_info_comm_edge[i]);
1379  }
1380  }
1381  }
1382 }
1383 
1384 void
1386  const Point & p,
1387  const Elem * side,
1388  std::vector<std::vector<const Node *>> & edge_nodes,
1389  std::vector<Real> & edge_face_weights)
1390 {
1391  const ElemType t(side->type());
1392  const Real & xi = p(0);
1393  const Real & eta = p(1);
1394 
1395  Real smooth_limit = 1.0 - _normal_smoothing_distance;
1396 
1397  switch (t)
1398  {
1399  case EDGE2:
1400  case EDGE3:
1401  case EDGE4:
1402  {
1403  if (xi < -smooth_limit)
1404  {
1405  std::vector<const Node *> en;
1406  en.push_back(side->node_ptr(0));
1407  edge_nodes.push_back(en);
1408  Real fw = 0.5 - (1.0 + xi) / (2.0 * _normal_smoothing_distance);
1409  if (fw > 0.5)
1410  fw = 0.5;
1411  edge_face_weights.push_back(fw);
1412  }
1413  else if (xi > smooth_limit)
1414  {
1415  std::vector<const Node *> en;
1416  en.push_back(side->node_ptr(1));
1417  edge_nodes.push_back(en);
1418  Real fw = 0.5 - (1.0 - xi) / (2.0 * _normal_smoothing_distance);
1419  if (fw > 0.5)
1420  fw = 0.5;
1421  edge_face_weights.push_back(fw);
1422  }
1423  break;
1424  }
1425 
1426  case TRI3:
1427  case TRI6:
1428  case TRI7:
1429  {
1430  if (eta < -smooth_limit)
1431  {
1432  std::vector<const Node *> en;
1433  en.push_back(side->node_ptr(0));
1434  en.push_back(side->node_ptr(1));
1435  edge_nodes.push_back(en);
1436  Real fw = 0.5 - (1.0 + eta) / (2.0 * _normal_smoothing_distance);
1437  if (fw > 0.5)
1438  fw = 0.5;
1439  edge_face_weights.push_back(fw);
1440  }
1441  if ((xi + eta) > smooth_limit)
1442  {
1443  std::vector<const Node *> en;
1444  en.push_back(side->node_ptr(1));
1445  en.push_back(side->node_ptr(2));
1446  edge_nodes.push_back(en);
1447  Real fw = 0.5 - (1.0 - xi - eta) / (2.0 * _normal_smoothing_distance);
1448  if (fw > 0.5)
1449  fw = 0.5;
1450  edge_face_weights.push_back(fw);
1451  }
1452  if (xi < -smooth_limit)
1453  {
1454  std::vector<const Node *> en;
1455  en.push_back(side->node_ptr(2));
1456  en.push_back(side->node_ptr(0));
1457  edge_nodes.push_back(en);
1458  Real fw = 0.5 - (1.0 + xi) / (2.0 * _normal_smoothing_distance);
1459  if (fw > 0.5)
1460  fw = 0.5;
1461  edge_face_weights.push_back(fw);
1462  }
1463  break;
1464  }
1465 
1466  case QUAD4:
1467  case QUAD8:
1468  case QUAD9:
1469  {
1470  if (eta < -smooth_limit)
1471  {
1472  std::vector<const Node *> en;
1473  en.push_back(side->node_ptr(0));
1474  en.push_back(side->node_ptr(1));
1475  edge_nodes.push_back(en);
1476  Real fw = 0.5 - (1.0 + eta) / (2.0 * _normal_smoothing_distance);
1477  if (fw > 0.5)
1478  fw = 0.5;
1479  edge_face_weights.push_back(fw);
1480  }
1481  if (xi > smooth_limit)
1482  {
1483  std::vector<const Node *> en;
1484  en.push_back(side->node_ptr(1));
1485  en.push_back(side->node_ptr(2));
1486  edge_nodes.push_back(en);
1487  Real fw = 0.5 - (1.0 - xi) / (2.0 * _normal_smoothing_distance);
1488  if (fw > 0.5)
1489  fw = 0.5;
1490  edge_face_weights.push_back(fw);
1491  }
1492  if (eta > smooth_limit)
1493  {
1494  std::vector<const Node *> en;
1495  en.push_back(side->node_ptr(2));
1496  en.push_back(side->node_ptr(3));
1497  edge_nodes.push_back(en);
1498  Real fw = 0.5 - (1.0 - eta) / (2.0 * _normal_smoothing_distance);
1499  if (fw > 0.5)
1500  fw = 0.5;
1501  edge_face_weights.push_back(fw);
1502  }
1503  if (xi < -smooth_limit)
1504  {
1505  std::vector<const Node *> en;
1506  en.push_back(side->node_ptr(3));
1507  en.push_back(side->node_ptr(0));
1508  edge_nodes.push_back(en);
1509  Real fw = 0.5 - (1.0 + xi) / (2.0 * _normal_smoothing_distance);
1510  if (fw > 0.5)
1511  fw = 0.5;
1512  edge_face_weights.push_back(fw);
1513  }
1514  break;
1515  }
1516 
1517  default:
1518  {
1519  mooseError("Unsupported face type: ", t);
1520  break;
1521  }
1522  }
1523 }
1524 
1525 void
1527  const Node * secondary_node,
1528  const std::set<dof_id_type> & elems_to_exclude,
1529  const std::vector<const Node *> edge_nodes,
1530  std::vector<PenetrationInfo *> & face_info_comm_edge,
1531  std::vector<PenetrationInfo *> & p_info)
1532 {
1533  // elems connected to a node on this edge, find one that has the same corners as this, and is not
1534  // the current elem
1535  auto node_to_elem_pair = _node_to_elem_map.find(edge_nodes[0]->id()); // just need one of the
1536  // nodes
1537  mooseAssert(node_to_elem_pair != _node_to_elem_map.end(), "Missing entry in node to elem map");
1538  const std::vector<dof_id_type> & elems_connected_to_node = node_to_elem_pair->second;
1539 
1540  std::vector<const Elem *> elems_connected_to_edge;
1541 
1542  for (unsigned int ecni = 0; ecni < elems_connected_to_node.size(); ecni++)
1543  {
1544  if (elems_to_exclude.find(elems_connected_to_node[ecni]) != elems_to_exclude.end())
1545  continue;
1546  const Elem * elem = _mesh.elemPtr(elems_connected_to_node[ecni]);
1547 
1548  std::vector<const Node *> nodevec;
1549  for (unsigned int ni = 0; ni < elem->n_nodes(); ++ni)
1550  if (elem->is_vertex(ni))
1551  nodevec.push_back(elem->node_ptr(ni));
1552 
1553  std::vector<const Node *> common_nodes;
1554  std::sort(nodevec.begin(), nodevec.end());
1555  std::set_intersection(edge_nodes.begin(),
1556  edge_nodes.end(),
1557  nodevec.begin(),
1558  nodevec.end(),
1559  std::inserter(common_nodes, common_nodes.end()));
1560 
1561  if (common_nodes.size() == edge_nodes.size())
1562  elems_connected_to_edge.push_back(elem);
1563  }
1564 
1565  if (elems_connected_to_edge.size() > 0)
1566  {
1567 
1568  // There are potentially multiple elements that share a common edge
1569  // 2D:
1570  // There can only be one element on the same surface
1571  // 3D:
1572  // If there are two edge nodes, there can only be one element on the same surface
1573  // If there is only one edge node (a corner), there could be multiple elements on the same
1574  // surface
1575  bool allowMultipleNeighbors = false;
1576 
1577  if (elems_connected_to_edge[0]->dim() == 3)
1578  {
1579  if (edge_nodes.size() == 1)
1580  {
1581  allowMultipleNeighbors = true;
1582  }
1583  }
1584 
1585  for (unsigned int i = 0; i < elems_connected_to_edge.size(); ++i)
1586  {
1587  std::vector<PenetrationInfo *> thisElemInfo;
1588  getInfoForElem(thisElemInfo, p_info, elems_connected_to_edge[i]);
1589  if (thisElemInfo.size() > 0 && !allowMultipleNeighbors)
1590  {
1591  if (thisElemInfo.size() > 1)
1592  mooseError(
1593  "Found multiple neighbors to current edge/face on surface when only one is allowed");
1594  face_info_comm_edge.push_back(thisElemInfo[0]);
1595  break;
1596  }
1597 
1599  thisElemInfo, p_info, secondary_node, elems_connected_to_edge[i], edge_nodes);
1600  if (thisElemInfo.size() > 0 && !allowMultipleNeighbors)
1601  {
1602  if (thisElemInfo.size() > 1)
1603  mooseError(
1604  "Found multiple neighbors to current edge/face on surface when only one is allowed");
1605  face_info_comm_edge.push_back(thisElemInfo[0]);
1606  break;
1607  }
1608 
1609  for (unsigned int j = 0; j < thisElemInfo.size(); ++j)
1610  face_info_comm_edge.push_back(thisElemInfo[j]);
1611  }
1612  }
1613 }
1614 
1615 void
1616 PenetrationThread::getInfoForElem(std::vector<PenetrationInfo *> & thisElemInfo,
1617  std::vector<PenetrationInfo *> & p_info,
1618  const Elem * elem)
1619 {
1620  for (const auto & pi : p_info)
1621  {
1622  if (!pi)
1623  continue;
1624 
1625  if (pi->_elem == elem)
1626  thisElemInfo.push_back(pi);
1627  }
1628 }
1629 
1630 void
1631 PenetrationThread::createInfoForElem(std::vector<PenetrationInfo *> & thisElemInfo,
1632  std::vector<PenetrationInfo *> & p_info,
1633  const Node * secondary_node,
1634  const Elem * elem,
1635  const std::vector<const Node *> & nodes_that_must_be_on_side,
1636  const bool check_whether_reasonable)
1637 {
1638  std::vector<unsigned int> sides;
1639  // TODO: After libMesh update, add this line to MooseMesh.h, call sidesWithBoundaryID, delete
1640  // getSidesOnPrimaryBoundary, and delete vectors used by it
1641  // void sidesWithBoundaryID(std::vector<unsigned int>& sides, const Elem * const elem, const
1642  // BoundaryID boundary_id) const
1643  // {
1644  // _mesh.get_boundary_info().sides_with_boundary_id(sides, elem, boundary_id);
1645  // }
1646  getSidesOnPrimaryBoundary(sides, elem);
1647  // _mesh.sidesWithBoundaryID(sides, elem, _primary_boundary);
1648 
1649  for (unsigned int i = 0; i < sides.size(); ++i)
1650  {
1651  // Don't create info for this side if one already exists
1652  bool already_have_info_this_side = false;
1653  for (const auto & pi : thisElemInfo)
1654  if (pi->_side_num == sides[i])
1655  {
1656  already_have_info_this_side = true;
1657  break;
1658  }
1659 
1660  if (already_have_info_this_side)
1661  break;
1662 
1663  const Elem * side = (elem->build_side_ptr(sides[i], false)).release();
1664 
1665  // Only continue with creating info for this side if the side contains
1666  // all of the nodes in nodes_that_must_be_on_side
1667  std::vector<const Node *> nodevec;
1668  for (unsigned int ni = 0; ni < side->n_nodes(); ++ni)
1669  nodevec.push_back(side->node_ptr(ni));
1670 
1671  std::sort(nodevec.begin(), nodevec.end());
1672  std::vector<const Node *> common_nodes;
1673  std::set_intersection(nodes_that_must_be_on_side.begin(),
1674  nodes_that_must_be_on_side.end(),
1675  nodevec.begin(),
1676  nodevec.end(),
1677  std::inserter(common_nodes, common_nodes.end()));
1678  if (common_nodes.size() != nodes_that_must_be_on_side.size())
1679  {
1680  delete side;
1681  break;
1682  }
1683 
1684  FEBase * fe_elem = _fes[_tid][elem->dim()];
1685  FEBase * fe_side = _fes[_tid][side->dim()];
1686 
1687  // Optionally check to see whether face is reasonable candidate based on an
1688  // estimate of how closely it is likely to project to the face
1689  if (check_whether_reasonable)
1690  if (!isFaceReasonableCandidate(elem, side, fe_side, secondary_node, _tangential_tolerance))
1691  {
1692  delete side;
1693  break;
1694  }
1695 
1696  Point contact_phys;
1697  Point contact_ref;
1698  Point contact_on_face_ref;
1699  Real distance = 0.;
1700  Real tangential_distance = 0.;
1701  RealGradient normal;
1702  bool contact_point_on_side;
1703  std::vector<const Node *> off_edge_nodes;
1704  std::vector<std::vector<Real>> side_phi;
1705  std::vector<std::vector<RealGradient>> side_grad_phi;
1706  std::vector<RealGradient> dxyzdxi;
1707  std::vector<RealGradient> dxyzdeta;
1708  std::vector<RealGradient> d2xyzdxideta;
1709 
1710  PenetrationInfo * pen_info = new PenetrationInfo(elem,
1711  side,
1712  sides[i],
1713  normal,
1714  distance,
1715  tangential_distance,
1716  contact_phys,
1717  contact_ref,
1718  contact_on_face_ref,
1719  off_edge_nodes,
1720  side_phi,
1721  side_grad_phi,
1722  dxyzdxi,
1723  dxyzdeta,
1724  d2xyzdxideta);
1725 
1726  Moose::findContactPoint(*pen_info,
1727  fe_elem,
1728  fe_side,
1729  _fe_type,
1730  *secondary_node,
1731  true,
1733  contact_point_on_side);
1734 
1735  thisElemInfo.push_back(pen_info);
1736 
1737  p_info.push_back(pen_info);
1738  }
1739 }
1740 
1741 // TODO: After libMesh update, replace this with a call to sidesWithBoundaryID, delete vectors used
1742 // by this method
1743 void
1744 PenetrationThread::getSidesOnPrimaryBoundary(std::vector<unsigned int> & sides,
1745  const Elem * const elem)
1746 {
1747  // For each tuple, the fields are (0=elem_id, 1=side_id, 2=bc_id)
1748  sides.clear();
1749  struct Comp
1750  {
1751  bool operator()(const libMesh::BoundaryInfo::BCTuple & tup, dof_id_type id) const
1752  {
1753  return std::get<0>(tup) < id;
1754  }
1755  bool operator()(dof_id_type id, const libMesh::BoundaryInfo::BCTuple & tup) const
1756  {
1757  return id < std::get<0>(tup);
1758  }
1759  };
1760 
1761  auto range = std::equal_range(_bc_tuples.begin(), _bc_tuples.end(), elem->id(), Comp{});
1762 
1763  for (auto & t = range.first; t != range.second; ++t)
1764  if (std::get<2>(*t) == static_cast<boundary_id_type>(_primary_boundary))
1765  sides.push_back(std::get<1>(*t));
1766 }
void getSidesOnPrimaryBoundary(std::vector< unsigned int > &sides, const Elem *const elem)
MooseVariable * _nodal_normal_z
std::tuple< dof_id_type, unsigned short int, boundary_id_type > BCTuple
ElemType
void getSmoothingEdgeNodesAndWeights(const Point &p, const Elem *side, std::vector< std::vector< const Node *>> &edge_nodes, std::vector< Real > &edge_face_weights)
auto norm() const -> decltype(std::norm(Real()))
const unsigned int invalid_uint
QUAD8
RealVectorValue _normal
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:2864
MPI_Info info
OutputType getValue(const Elem *elem, const std::vector< std::vector< OutputShape >> &phi) const
Compute the variable value at a point on an element.
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:299
Data structure used to hold penetration information.
EDGE4
NearestNodeLocator & _nearest_node
StoredRange< std::vector< dof_id_type >::iterator, dof_id_type > NodeIdRange
Definition: MooseTypes.h:205
Finds the nearest node to each node in boundary1 to each node in boundary2 and the other way around...
const Elem * _starting_elem
const std::vector< std::tuple< dof_id_type, unsigned short int, boundary_id_type > > & _bc_tuples
MeshBase & mesh
bool findRidgeContactPoint(Point &contact_point, Real &tangential_distance, const Node *&closest_node, unsigned int &index, Point &contact_point_ref, std::vector< PenetrationInfo *> &p_info, const unsigned int index1, const unsigned int index2)
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:148
void createInfoForElem(std::vector< PenetrationInfo *> &thisElemInfo, std::vector< PenetrationInfo *> &p_info, const Node *secondary_node, const Elem *elem, const std::vector< const Node *> &nodes_that_must_be_on_side, const bool check_whether_reasonable=false)
bool restrictPointToFace(Point &p, const Node *&closest_node, const Elem *side)
unsigned int _stick_locked_this_step
void getSideCornerNodes(const Elem *side, std::vector< const Node *> &corner_nodes)
Threads::spin_mutex pinfo_mutex
RealVectorValue _contact_force_old
Real distance(const Point &p)
virtual const Node & nodeRef(const dof_id_type i) const
Definition: MooseMesh.C:637
void smoothNormal(PenetrationInfo *info, std::vector< PenetrationInfo *> &p_info, const Node &node)
void getInfoForFacesWithCommonNodes(const Node *secondary_node, const std::set< dof_id_type > &elems_to_exclude, const std::vector< const Node *> edge_nodes, std::vector< PenetrationInfo *> &face_info_comm_edge, std::vector< PenetrationInfo *> &p_info)
auto max(const L &left, const R &right)
TRI3
void getSmoothingFacesAndWeights(PenetrationInfo *info, std::vector< PenetrationInfo *> &edge_face_info, std::vector< Real > &edge_face_weights, std::vector< PenetrationInfo *> &p_info, const Node &secondary_node)
QUAD4
unsigned int _locked_this_step
BoundaryID _primary_boundary
SubProblem & _subproblem
ADRealEigenVector< T, D, asd > abs(const ADRealEigenVector< T, D, asd > &)
const MooseMesh & _mesh
std::vector< std::vector< FEBase * > > & _fes
void findContactPoint(PenetrationInfo &p_info, FEBase *fe_elem, FEBase *fe_side, FEType &fe_side_type, const Point &secondary_point, bool start_with_centroid, const Real tangential_tolerance, bool &contact_point_on_side)
Finds the closest point (called the contact point) on the primary_elem on side "side" to the secondar...
bool restrictPointToSpecifiedEdgeOfFace(Point &p, const Node *&closest_node, const Elem *side, const std::vector< const Node *> &edge_nodes)
int8_t boundary_id_type
bool isFaceReasonableCandidate(const Elem *primary_elem, const Elem *side, FEBase *fe, const Point *secondary_point, const Real tangential_tolerance)
const Elem * _elem
unsigned int _starting_side_num
TRI6
boundary_id_type BoundaryID
CompeteInteractionResult competeInteractions(PenetrationInfo *pi1, PenetrationInfo *pi2)
When interactions are identified between a node and two faces, compete between the faces to determine...
void operator()(const NodeIdRange &range)
MooseMesh wraps a libMesh::Mesh object and enhances its capabilities by caching additional data and s...
Definition: MooseMesh.h:88
std::vector< dof_id_type > _recheck_secondary_nodes
List of secondary nodes for which penetration was not detected in the current patch and for which pat...
unsigned int _side_num
virtual MooseVariable & getStandardVariable(const THREAD_ID tid, const std::string &var_name)=0
Returns the variable reference for requested MooseVariable which may be in any system.
const Elem * _side
TypeVector< typename CompareTypes< Real, T2 >::supertype > cross(const TypeVector< T2 > &v) const
FEGenericBase< Real > FEBase
RealVectorValue _contact_force
MECH_STATUS_ENUM _mech_status
void join(const PenetrationThread &other)
std::vector< const Node * > _off_edge_nodes
Point _starting_closest_point_ref
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Generic class for solving transient nonlinear problems.
Definition: SubProblem.h:75
EDGE2
std::map< dof_id_type, PenetrationInfo * > & _penetration_info
ElemSideBuilder _elem_side_builder
Helper for building element sides without extraneous allocation.
const Node * nearestNode(dof_id_type node_id)
Valid to call this after findNodes() has been called to get a pointer to the nearest node...
PenetrationThread(SubProblem &subproblem, const MooseMesh &mesh, BoundaryID primary_boundary, BoundaryID secondary_boundary, std::map< dof_id_type, PenetrationInfo *> &penetration_info, bool check_whether_reasonable, bool update_location, Real tangential_tolerance, bool do_normal_smoothing, Real normal_smoothing_distance, PenetrationLocator::NORMAL_SMOOTHING_METHOD normal_smoothing_method, std::vector< std::vector< FEBase *>> &fes, FEType &fe_type, NearestNodeLocator &nearest_node, const std::map< dof_id_type, std::vector< dof_id_type >> &node_to_elem_map, const std::vector< std::tuple< dof_id_type, unsigned short int, boundary_id_type >> &bc_tuples)
MooseVariable * _nodal_normal_y
void getInfoForElem(std::vector< PenetrationInfo *> &thisElemInfo, std::vector< PenetrationInfo *> &p_info, const Elem *elem)
TRI7
void computeSlip(FEBase &fe, PenetrationInfo &info)
MECH_STATUS_ENUM _mech_status_old
QUAD9
const std::map< dof_id_type, std::vector< dof_id_type > > & _node_to_elem_map
void switchInfo(PenetrationInfo *&info, PenetrationInfo *&infoNew)
RealVectorValue _lagrange_multiplier_slip
MooseVariable * _nodal_normal_x
bool relativeFuzzyLessThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether a variable is less than another variable within a relative tolerance...
Definition: MooseUtils.h:587
EDGE3
CompeteInteractionResult competeInteractionsBothOnFace(PenetrationInfo *pi1, PenetrationInfo *pi2)
Determine whether first (pi1) or second (pi2) interaction is stronger when it is known that the node ...
CommonEdgeResult interactionsOffCommonEdge(PenetrationInfo *pi1, PenetrationInfo *pi2)
PenetrationLocator::NORMAL_SMOOTHING_METHOD _normal_smoothing_method
uint8_t dof_id_type
const Real pi
std::vector< RidgeData > _ridge_data_vec