LCOV - code coverage report
Current view: top level - src/geomsearch - PenetrationThread.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 771 923 83.5 %
Date: 2025-07-17 01:28:37 Functions: 23 23 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14