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