https://mooseframework.inl.gov
Classes | Public Member Functions | Public Attributes | Protected Types | Protected Member Functions | Protected Attributes | List of all members
PenetrationThread Class Reference

#include <PenetrationThread.h>

Classes

struct  RidgeData
 
struct  RidgeSetData
 

Public Member Functions

 PenetrationThread (SubProblem &subproblem, const MooseMesh &mesh, BoundaryID primary_boundary, BoundaryID secondary_boundary, std::map< dof_id_type, PenetrationInfo *> &penetration_info, bool check_whether_reasonable, bool update_location, Real tangential_tolerance, bool do_normal_smoothing, Real normal_smoothing_distance, PenetrationLocator::NORMAL_SMOOTHING_METHOD normal_smoothing_method, bool use_point_locator, std::vector< std::vector< libMesh::FEBase *>> &fes, libMesh::FEType &fe_type, NearestNodeLocator &nearest_node, const std::unordered_map< dof_id_type, std::vector< dof_id_type >> &node_to_elem_map)
 
 PenetrationThread (PenetrationThread &x, Threads::split split)
 
void operator() (const NodeIdRange &range)
 
void join (const PenetrationThread &other)
 

Public Attributes

std::vector< dof_id_type_recheck_secondary_nodes
 List of secondary nodes for which penetration was not detected in the current patch and for which patch has to be updated. More...
 

Protected Types

enum  CompeteInteractionResult { FIRST_WINS, SECOND_WINS, NEITHER_WINS }
 
enum  CommonEdgeResult { NO_COMMON, COMMON_EDGE, COMMON_NODE, EDGE_AND_COMMON_NODE }
 

Protected Member Functions

CompeteInteractionResult competeInteractions (PenetrationInfo *pi1, PenetrationInfo *pi2)
 When interactions are identified between a node and two faces, compete between the faces to determine whether first (pi1) or second (pi2) interaction is stronger. More...
 
CompeteInteractionResult competeInteractionsBothOnFace (PenetrationInfo *pi1, PenetrationInfo *pi2)
 Determine whether first (pi1) or second (pi2) interaction is stronger when it is known that the node projects to both of the two competing faces. More...
 
CommonEdgeResult interactionsOffCommonEdge (PenetrationInfo *pi1, PenetrationInfo *pi2)
 
bool findRidgeContactPoint (libMesh::Point &contact_point, Real &tangential_distance, const Node *&closest_node, unsigned int &index, libMesh::Point &contact_point_ref, std::vector< PenetrationInfo *> &p_info, const unsigned int index1, const unsigned int index2)
 
void getSideCornerNodes (const Elem *side, std::vector< const Node *> &corner_nodes)
 
bool restrictPointToSpecifiedEdgeOfFace (libMesh::Point &p, const Node *&closest_node, const Elem *side, const std::vector< const Node *> &edge_nodes)
 
bool restrictPointToFace (libMesh::Point &p, const Node *&closest_node, const Elem *side)
 
bool isFaceReasonableCandidate (const Elem *primary_elem, const Elem *side, libMesh::FEBase *fe, const libMesh::Point *secondary_point, const Real tangential_tolerance)
 
void smoothNormal (PenetrationInfo *info, std::vector< PenetrationInfo *> &p_info, const Node &node)
 
void getSmoothingFacesAndWeights (PenetrationInfo *info, std::vector< PenetrationInfo *> &edge_face_info, std::vector< Real > &edge_face_weights, std::vector< PenetrationInfo *> &p_info, const Node &secondary_node)
 
void getSmoothingEdgeNodesAndWeights (const libMesh::Point &p, const Elem *side, std::vector< std::vector< const Node *>> &edge_nodes, std::vector< Real > &edge_face_weights)
 
void getInfoForFacesWithCommonNodes (const Node *secondary_node, const std::set< dof_id_type > &elems_to_exclude, const std::vector< const Node *> edge_nodes, std::vector< PenetrationInfo *> &face_info_comm_edge, std::vector< PenetrationInfo *> &p_info)
 
void getInfoForElem (std::vector< PenetrationInfo *> &thisElemInfo, std::vector< PenetrationInfo *> &p_info, const Elem *elem)
 
void createInfoForElem (std::vector< PenetrationInfo *> &thisElemInfo, std::vector< PenetrationInfo *> &p_info, const Node *secondary_node, const Elem *elem, const std::vector< const Node *> &nodes_that_must_be_on_side, const bool check_whether_reasonable=false)
 
void getSidesOnPrimaryBoundary (std::vector< unsigned int > &sides, const Elem *const elem)
 
void computeSlip (libMesh::FEBase &fe, PenetrationInfo &info)
 
void switchInfo (PenetrationInfo *&info, PenetrationInfo *&infoNew)
 

Protected Attributes

SubProblem_subproblem
 
const MooseMesh_mesh
 
BoundaryID _primary_boundary
 
BoundaryID _secondary_boundary
 
std::map< dof_id_type, PenetrationInfo * > & _penetration_info
 
bool _check_whether_reasonable
 
bool _update_location
 
Real _tangential_tolerance
 
bool _do_normal_smoothing
 
Real _normal_smoothing_distance
 
PenetrationLocator::NORMAL_SMOOTHING_METHOD _normal_smoothing_method
 
bool _use_point_locator
 
MooseVariable_nodal_normal_x
 
MooseVariable_nodal_normal_y
 
MooseVariable_nodal_normal_z
 
std::vector< std::vector< libMesh::FEBase * > > & _fes
 
libMesh::FEType_fe_type
 
NearestNodeLocator_nearest_node
 
const std::unordered_map< dof_id_type, std::vector< dof_id_type > > & _node_to_elem_map
 
THREAD_ID _tid
 
libMesh::ElemSideBuilder _elem_side_builder
 Helper for building element sides without extraneous allocation. More...
 

Detailed Description

Definition at line 24 of file PenetrationThread.h.

Member Enumeration Documentation

◆ CommonEdgeResult

Enumerator
NO_COMMON 
COMMON_EDGE 
COMMON_NODE 
EDGE_AND_COMMON_NODE 

Definition at line 96 of file PenetrationThread.h.

◆ CompeteInteractionResult

Enumerator
FIRST_WINS 
SECOND_WINS 
NEITHER_WINS 

Definition at line 89 of file PenetrationThread.h.

Constructor & Destructor Documentation

◆ PenetrationThread() [1/2]

PenetrationThread::PenetrationThread ( SubProblem subproblem,
const MooseMesh mesh,
BoundaryID  primary_boundary,
BoundaryID  secondary_boundary,
std::map< dof_id_type, PenetrationInfo *> &  penetration_info,
bool  check_whether_reasonable,
bool  update_location,
Real  tangential_tolerance,
bool  do_normal_smoothing,
Real  normal_smoothing_distance,
PenetrationLocator::NORMAL_SMOOTHING_METHOD  normal_smoothing_method,
bool  use_point_locator,
std::vector< std::vector< libMesh::FEBase *>> &  fes,
libMesh::FEType fe_type,
NearestNodeLocator nearest_node,
const std::unordered_map< dof_id_type, std::vector< dof_id_type >> &  node_to_elem_map 
)

Definition at line 98 of file PenetrationThread.C.

115  : _subproblem(subproblem),
116  _mesh(mesh),
117  _primary_boundary(primary_boundary),
118  _secondary_boundary(secondary_boundary),
119  _penetration_info(penetration_info),
120  _check_whether_reasonable(check_whether_reasonable),
121  _update_location(update_location),
122  _tangential_tolerance(tangential_tolerance),
123  _do_normal_smoothing(do_normal_smoothing),
124  _normal_smoothing_distance(normal_smoothing_distance),
125  _normal_smoothing_method(normal_smoothing_method),
126  _use_point_locator(use_point_locator),
127  _nodal_normal_x(NULL),
128  _nodal_normal_y(NULL),
129  _nodal_normal_z(NULL),
130  _fes(fes),
131  _fe_type(fe_type),
132  _nearest_node(nearest_node),
133  _node_to_elem_map(node_to_elem_map)
134 {
135 }
MooseVariable * _nodal_normal_z
NearestNodeLocator & _nearest_node
libMesh::FEType & _fe_type
BoundaryID _primary_boundary
SubProblem & _subproblem
const MooseMesh & _mesh
BoundaryID _secondary_boundary
std::map< dof_id_type, PenetrationInfo * > & _penetration_info
MooseVariable * _nodal_normal_y
MooseVariable * _nodal_normal_x
const std::unordered_map< dof_id_type, std::vector< dof_id_type > > & _node_to_elem_map
std::vector< std::vector< libMesh::FEBase * > > & _fes
PenetrationLocator::NORMAL_SMOOTHING_METHOD _normal_smoothing_method

◆ PenetrationThread() [2/2]

PenetrationThread::PenetrationThread ( PenetrationThread x,
Threads::split  split 
)

Definition at line 138 of file PenetrationThread.C.

140  _mesh(x._mesh),
151  _fes(x._fes),
152  _fe_type(x._fe_type),
155 {
156 }
NearestNodeLocator & _nearest_node
libMesh::FEType & _fe_type
BoundaryID _primary_boundary
SubProblem & _subproblem
const MooseMesh & _mesh
BoundaryID _secondary_boundary
std::map< dof_id_type, PenetrationInfo * > & _penetration_info
const std::unordered_map< dof_id_type, std::vector< dof_id_type > > & _node_to_elem_map
std::vector< std::vector< libMesh::FEBase * > > & _fes
PenetrationLocator::NORMAL_SMOOTHING_METHOD _normal_smoothing_method

Member Function Documentation

◆ competeInteractions()

PenetrationThread::CompeteInteractionResult PenetrationThread::competeInteractions ( PenetrationInfo pi1,
PenetrationInfo pi2 
)
protected

When interactions are identified between a node and two faces, compete between the faces to determine whether first (pi1) or second (pi2) interaction is stronger.

Parameters
pi1Pointer to the PenetrationInfo object for the first face
pi2Pointer to the PenetrationInfo object for the second face
Returns
Appropriate ComputeInterationResult enum entry identifying which face is a better match

Definition at line 683 of file PenetrationThread.C.

Referenced by operator()().

684 {
685 
687 
689  pi2->_tangential_distance > _tangential_tolerance) // out of tol on both faces
690  result = NEITHER_WINS;
691 
692  else if (pi1->_tangential_distance == 0.0 &&
693  pi2->_tangential_distance > 0.0) // on face 1, off face 2
694  result = FIRST_WINS;
695 
696  else if (pi2->_tangential_distance == 0.0 &&
697  pi1->_tangential_distance > 0.0) // on face 2, off face 1
698  result = SECOND_WINS;
699 
700  else if (pi1->_tangential_distance <= _tangential_tolerance &&
701  pi2->_tangential_distance > _tangential_tolerance) // in face 1 tol, out of face 2 tol
702  result = FIRST_WINS;
703 
704  else if (pi2->_tangential_distance <= _tangential_tolerance &&
705  pi1->_tangential_distance > _tangential_tolerance) // in face 2 tol, out of face 1 tol
706  result = SECOND_WINS;
707 
708  else if (pi1->_tangential_distance == 0.0 && pi2->_tangential_distance == 0.0) // on both faces
709  result = competeInteractionsBothOnFace(pi1, pi2);
710 
711  else if (pi1->_tangential_distance <= _tangential_tolerance &&
712  pi2->_tangential_distance <= _tangential_tolerance) // off but within tol of both faces
713  {
715  if (cer == COMMON_EDGE || cer == COMMON_NODE) // ridge case.
716  {
717  // We already checked for ridges, and it got rejected, so neither face must be valid
718  result = NEITHER_WINS;
719  // mooseError("Erroneously encountered ridge case");
720  }
721  else if (cer == EDGE_AND_COMMON_NODE) // off side of face, off corner of another face. Favor
722  // the off-side face
723  {
724  if (pi1->_off_edge_nodes.size() == pi2->_off_edge_nodes.size())
725  mooseError("Invalid off_edge_nodes counts");
726 
727  else if (pi1->_off_edge_nodes.size() == 2)
728  result = FIRST_WINS;
729 
730  else if (pi2->_off_edge_nodes.size() == 2)
731  result = SECOND_WINS;
732 
733  else
734  mooseError("Invalid off_edge_nodes counts");
735  }
736  else // The node projects to both faces within tangential tolerance.
737  result = competeInteractionsBothOnFace(pi1, pi2);
738  }
739 
740  return result;
741 }
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
std::vector< const Node * > _off_edge_nodes
CompeteInteractionResult competeInteractionsBothOnFace(PenetrationInfo *pi1, PenetrationInfo *pi2)
Determine whether first (pi1) or second (pi2) interaction is stronger when it is known that the node ...
CommonEdgeResult interactionsOffCommonEdge(PenetrationInfo *pi1, PenetrationInfo *pi2)

◆ competeInteractionsBothOnFace()

PenetrationThread::CompeteInteractionResult PenetrationThread::competeInteractionsBothOnFace ( PenetrationInfo pi1,
PenetrationInfo pi2 
)
protected

Determine whether first (pi1) or second (pi2) interaction is stronger when it is known that the node projects to both of the two competing faces.

Parameters
pi1Pointer to the PenetrationInfo object for the first face
pi2Pointer to the PenetrationInfo object for the second face
Returns
Appropriate ComputeInterationResult enum entry identifying which face is a better match

Definition at line 744 of file PenetrationThread.C.

Referenced by competeInteractions().

745 {
747 
748  if (pi1->_distance >= 0.0 && pi2->_distance < 0.0)
749  result = FIRST_WINS; // favor face with positive distance (penetrated) -- first in this case
750 
751  else if (pi2->_distance >= 0.0 && pi1->_distance < 0.0)
752  result = SECOND_WINS; // favor face with positive distance (penetrated) -- second in this case
753 
754  // TODO: This logic below could cause an abrupt jump from one face to the other with small mesh
755  // movement. If there is some way to smooth the transition, we should do it.
756  else if (MooseUtils::relativeFuzzyLessThan(std::abs(pi1->_distance), std::abs(pi2->_distance)))
757  result = FIRST_WINS; // otherwise, favor the closer face -- first in this case
758 
759  else if (MooseUtils::relativeFuzzyLessThan(std::abs(pi2->_distance), std::abs(pi1->_distance)))
760  result = SECOND_WINS; // otherwise, favor the closer face -- second in this case
761 
762  else // Equal within tolerance. Favor the one with a smaller element id (for repeatibility)
763  {
764  if (pi1->_elem->id() < pi2->_elem->id())
765  result = FIRST_WINS;
766 
767  else
768  result = SECOND_WINS;
769  }
770 
771  return result;
772 }
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
Definition: EigenADReal.h:50
dof_id_type id() const
const Elem * _elem

◆ computeSlip()

void PenetrationThread::computeSlip ( libMesh::FEBase fe,
PenetrationInfo info 
)
protected

Definition at line 1398 of file PenetrationThread.C.

Referenced by operator()().

1399 {
1400  // Slip is current projected position of secondary node minus
1401  // original projected position of secondary node
1402  std::vector<Point> points(1);
1403  points[0] = info._starting_closest_point_ref;
1404  const auto & side = _elem_side_builder(*info._starting_elem, info._starting_side_num);
1405  fe.reinit(&side, &points);
1406  const std::vector<Point> & starting_point = fe.get_xyz();
1407  info._incremental_slip = info._closest_point - starting_point[0];
1408  if (info.isCaptured())
1409  {
1410  info._frictional_energy =
1411  info._frictional_energy_old + info._contact_force * info._incremental_slip;
1412  info._accumulated_slip = info._accumulated_slip_old + info._incremental_slip.norm();
1413  }
1414 }
MPI_Info info
virtual void reinit(const Elem *elem, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr)=0
virtual_for_inffe const std::vector< Point > & get_xyz() const
libMesh::ElemSideBuilder _elem_side_builder
Helper for building element sides without extraneous allocation.

◆ createInfoForElem()

void PenetrationThread::createInfoForElem ( std::vector< PenetrationInfo *> &  thisElemInfo,
std::vector< PenetrationInfo *> &  p_info,
const Node secondary_node,
const Elem elem,
const std::vector< const Node *> &  nodes_that_must_be_on_side,
const bool  check_whether_reasonable = false 
)
protected

Definition at line 1816 of file PenetrationThread.C.

Referenced by getInfoForFacesWithCommonNodes(), and operator()().

1822 {
1823  const BoundaryInfo & boundary_info = _mesh.getMesh().get_boundary_info();
1824 
1825  for (auto s : elem->side_index_range())
1826  {
1827  if (!boundary_info.has_boundary_id(elem, s, _primary_boundary))
1828  continue;
1829 
1830  // Don't create info for this side if one already exists
1831  bool already_have_info_this_side = false;
1832  for (const auto & pi : thisElemInfo)
1833  if (pi->_side_num == s)
1834  {
1835  already_have_info_this_side = true;
1836  break;
1837  }
1838 
1839  if (already_have_info_this_side)
1840  break;
1841 
1842  const Elem * side = elem->build_side_ptr(s).release();
1843 
1844  // Only continue with creating info for this side if the side contains
1845  // all of the nodes in nodes_that_must_be_on_side
1846  std::vector<const Node *> nodevec;
1847  for (unsigned int ni = 0; ni < side->n_nodes(); ++ni)
1848  nodevec.push_back(side->node_ptr(ni));
1849 
1850  std::sort(nodevec.begin(), nodevec.end());
1851  std::vector<const Node *> common_nodes;
1852  std::set_intersection(nodes_that_must_be_on_side.begin(),
1853  nodes_that_must_be_on_side.end(),
1854  nodevec.begin(),
1855  nodevec.end(),
1856  std::inserter(common_nodes, common_nodes.end()));
1857  if (common_nodes.size() != nodes_that_must_be_on_side.size())
1858  {
1859  delete side;
1860  break;
1861  }
1862 
1863  FEBase * fe_elem = _fes[_tid][elem->dim()];
1864  FEBase * fe_side = _fes[_tid][side->dim()];
1865 
1866  // Optionally check to see whether face is reasonable candidate based on an
1867  // estimate of how closely it is likely to project to the face
1868  if (check_whether_reasonable)
1869  if (!isFaceReasonableCandidate(elem, side, fe_side, secondary_node, _tangential_tolerance))
1870  {
1871  delete side;
1872  break;
1873  }
1874 
1875  Point contact_phys;
1876  Point contact_ref;
1877  Point contact_on_face_ref;
1878  Real distance = 0.;
1879  Real tangential_distance = 0.;
1880  RealGradient normal;
1881  bool contact_point_on_side;
1882  std::vector<const Node *> off_edge_nodes;
1883  std::vector<std::vector<Real>> side_phi;
1884  std::vector<std::vector<RealGradient>> side_grad_phi;
1885  std::vector<RealGradient> dxyzdxi;
1886  std::vector<RealGradient> dxyzdeta;
1887  std::vector<RealGradient> d2xyzdxideta;
1888 
1889  std::unique_ptr<PenetrationInfo> pen_info =
1890  std::make_unique<PenetrationInfo>(elem,
1891  side,
1892  s,
1893  normal,
1894  distance,
1895  tangential_distance,
1896  contact_phys,
1897  contact_ref,
1898  contact_on_face_ref,
1899  off_edge_nodes,
1900  side_phi,
1901  side_grad_phi,
1902  dxyzdxi,
1903  dxyzdeta,
1904  d2xyzdxideta);
1905 
1906  bool search_succeeded = false;
1907  Moose::findContactPoint(*pen_info,
1908  fe_elem,
1909  fe_side,
1910  _fe_type,
1911  *secondary_node,
1912  true,
1914  contact_point_on_side,
1915  search_succeeded);
1916 
1917  // Do not add contact info from failed searches
1918  if (search_succeeded)
1919  {
1920  thisElemInfo.push_back(pen_info.get());
1921  p_info.push_back(pen_info.release());
1922  }
1923  }
1924 }
bool has_boundary_id(const Node *const node, const boundary_id_type id) const
IntRange< unsigned short > side_index_range() const
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
const BoundaryInfo & get_boundary_info() const
Real distance(const Point &p)
libMesh::FEType & _fe_type
BoundaryID _primary_boundary
const MooseMesh & _mesh
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3548
virtual unsigned int n_nodes() const=0
void findContactPoint(PenetrationInfo &p_info, libMesh::FEBase *fe_elem, libMesh::FEBase *fe_side, libMesh::FEType &fe_side_type, const libMesh::Point &secondary_point, bool start_with_centroid, const Real tangential_tolerance, bool &contact_point_on_side, bool &search_succeeded)
Finds the closest point (called the contact point) on the primary_elem on side "side" to the secondar...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual unsigned short dim() const=0
const Node * node_ptr(const unsigned int i) const
std::vector< std::vector< libMesh::FEBase * > > & _fes
bool isFaceReasonableCandidate(const Elem *primary_elem, const Elem *side, libMesh::FEBase *fe, const libMesh::Point *secondary_point, const Real tangential_tolerance)
const Real pi

◆ findRidgeContactPoint()

bool PenetrationThread::findRidgeContactPoint ( libMesh::Point contact_point,
Real tangential_distance,
const Node *&  closest_node,
unsigned int index,
libMesh::Point contact_point_ref,
std::vector< PenetrationInfo *> &  p_info,
const unsigned int  index1,
const unsigned int  index2 
)
protected

Definition at line 829 of file PenetrationThread.C.

Referenced by operator()().

837 {
838  tangential_distance = 0.0;
839  closest_node = NULL;
840  PenetrationInfo * pi1 = p_info[index1];
841  PenetrationInfo * pi2 = p_info[index2];
842  const unsigned sidedim(pi1->_side->dim());
843  mooseAssert(sidedim == pi2->_side->dim(), "Incompatible dimensionalities");
844 
845  // Nodes on faces for the two interactions
846  std::vector<const Node *> side1_nodes;
847  getSideCornerNodes(pi1->_side, side1_nodes);
848  std::vector<const Node *> side2_nodes;
849  getSideCornerNodes(pi2->_side, side2_nodes);
850 
851  std::sort(side1_nodes.begin(), side1_nodes.end());
852  std::sort(side2_nodes.begin(), side2_nodes.end());
853 
854  // Find nodes shared by the two faces
855  std::vector<const Node *> common_nodes;
856  std::set_intersection(side1_nodes.begin(),
857  side1_nodes.end(),
858  side2_nodes.begin(),
859  side2_nodes.end(),
860  std::inserter(common_nodes, common_nodes.end()));
861 
862  if (common_nodes.size() != sidedim)
863  return false;
864 
865  bool found_point1, found_point2;
866  Point closest_coor_ref1(pi1->_closest_point_ref);
867  const Node * closest_node1;
868  found_point1 = restrictPointToSpecifiedEdgeOfFace(
869  closest_coor_ref1, closest_node1, pi1->_side, common_nodes);
870 
871  Point closest_coor_ref2(pi2->_closest_point_ref);
872  const Node * closest_node2;
873  found_point2 = restrictPointToSpecifiedEdgeOfFace(
874  closest_coor_ref2, closest_node2, pi2->_side, common_nodes);
875 
876  if (!found_point1 || !found_point2)
877  return false;
878 
879  // if (sidedim == 2)
880  // {
881  // TODO:
882  // We have the parametric coordinates of the closest intersection point for both faces.
883  // We need to find a point somewhere in the middle of them so there's not an abrupt jump.
884  // Find that point by taking dot products of vector from contact point to secondary node point
885  // with face normal vectors to see which face we're closer to.
886  // }
887 
888  FEBase * fe = NULL;
889  std::vector<Point> points(1);
890 
891  // We have to pick one of the two faces to own the contact point. Either one would
892  // generally work, but to avoid some corner-case numerical roundoff issues when
893  // determining signs of the normal and displacement, pick the one that the point is
894  // closer to projecting onto, which is the one with the larger projected distance.
895  // If that distance is the same for both faces (within numerical precision), pick the
896  // face with the lowest index for repeatability.
897  if (MooseUtils::absoluteFuzzyGreaterThan(std::abs(pi1->_distance), std::abs(pi2->_distance)) ||
898  (MooseUtils::absoluteFuzzyEqual(std::abs(pi1->_distance), std::abs(pi2->_distance)) &&
899  index1 < index2))
900  {
901  fe = _fes[_tid][pi1->_side->dim()];
902  contact_point_ref = closest_coor_ref1;
903  points[0] = closest_coor_ref1;
904  fe->reinit(pi1->_side, &points);
905  index = index1;
906  }
907  else
908  {
909  fe = _fes[_tid][pi2->_side->dim()];
910  contact_point_ref = closest_coor_ref2;
911  points[0] = closest_coor_ref2;
912  fe->reinit(pi2->_side, &points);
913  index = index2;
914  }
915 
916  contact_point = fe->get_xyz()[0];
917 
918  if (sidedim == 2)
919  {
920  if (closest_node1) // point is off the ridge between the two elements
921  {
922  mooseAssert((closest_node1 == closest_node2 || closest_node2 == NULL),
923  "If off edge of ridge, closest node must be the same on both elements");
924  closest_node = closest_node1;
925 
926  RealGradient off_face = *closest_node1 - contact_point;
927  tangential_distance = off_face.norm();
928  }
929  }
930 
931  return true;
932 }
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
Definition: EigenADReal.h:50
auto norm() const
Data structure used to hold penetration information.
void getSideCornerNodes(const Elem *side, std::vector< const Node *> &corner_nodes)
virtual void reinit(const Elem *elem, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr)=0
virtual_for_inffe const std::vector< Point > & get_xyz() const
bool restrictPointToSpecifiedEdgeOfFace(libMesh::Point &p, const Node *&closest_node, const Elem *side, const std::vector< const Node *> &edge_nodes)
const Elem * _side
virtual unsigned short dim() const=0
std::vector< std::vector< libMesh::FEBase * > > & _fes

◆ getInfoForElem()

void PenetrationThread::getInfoForElem ( std::vector< PenetrationInfo *> &  thisElemInfo,
std::vector< PenetrationInfo *> &  p_info,
const Elem elem 
)
protected

Definition at line 1801 of file PenetrationThread.C.

Referenced by getInfoForFacesWithCommonNodes().

1804 {
1805  for (const auto & pi : p_info)
1806  {
1807  if (!pi)
1808  continue;
1809 
1810  if (pi->_elem == elem)
1811  thisElemInfo.push_back(pi);
1812  }
1813 }
const Real pi

◆ getInfoForFacesWithCommonNodes()

void PenetrationThread::getInfoForFacesWithCommonNodes ( const Node secondary_node,
const std::set< dof_id_type > &  elems_to_exclude,
const std::vector< const Node *>  edge_nodes,
std::vector< PenetrationInfo *> &  face_info_comm_edge,
std::vector< PenetrationInfo *> &  p_info 
)
protected

Definition at line 1711 of file PenetrationThread.C.

Referenced by getSmoothingFacesAndWeights().

1717 {
1718  // elems connected to a node on this edge, find one that has the same corners as this, and is not
1719  // the current elem
1720  auto node_to_elem_pair = _node_to_elem_map.find(edge_nodes[0]->id()); // just need one of the
1721  // nodes
1722  mooseAssert(node_to_elem_pair != _node_to_elem_map.end(), "Missing entry in node to elem map");
1723  const std::vector<dof_id_type> & elems_connected_to_node = node_to_elem_pair->second;
1724 
1725  std::vector<const Elem *> elems_connected_to_edge;
1726 
1727  for (unsigned int ecni = 0; ecni < elems_connected_to_node.size(); ecni++)
1728  {
1729  if (elems_to_exclude.find(elems_connected_to_node[ecni]) != elems_to_exclude.end())
1730  continue;
1731  const Elem * elem = _mesh.elemPtr(elems_connected_to_node[ecni]);
1732 
1733  std::vector<const Node *> nodevec;
1734  for (unsigned int ni = 0; ni < elem->n_nodes(); ++ni)
1735  if (elem->is_vertex(ni))
1736  nodevec.push_back(elem->node_ptr(ni));
1737 
1738  std::vector<const Node *> common_nodes;
1739  std::sort(nodevec.begin(), nodevec.end());
1740  std::set_intersection(edge_nodes.begin(),
1741  edge_nodes.end(),
1742  nodevec.begin(),
1743  nodevec.end(),
1744  std::inserter(common_nodes, common_nodes.end()));
1745 
1746  if (common_nodes.size() == edge_nodes.size())
1747  elems_connected_to_edge.push_back(elem);
1748  }
1749 
1750  if (elems_connected_to_edge.size() > 0)
1751  {
1752 
1753  // There are potentially multiple elements that share a common edge
1754  // 2D:
1755  // There can only be one element on the same surface
1756  // 3D:
1757  // If there are two edge nodes, there can only be one element on the same surface
1758  // If there is only one edge node (a corner), there could be multiple elements on the same
1759  // surface
1760  bool allowMultipleNeighbors = false;
1761 
1762  if (elems_connected_to_edge[0]->dim() == 3)
1763  {
1764  if (edge_nodes.size() == 1)
1765  {
1766  allowMultipleNeighbors = true;
1767  }
1768  }
1769 
1770  for (unsigned int i = 0; i < elems_connected_to_edge.size(); ++i)
1771  {
1772  std::vector<PenetrationInfo *> thisElemInfo;
1773  getInfoForElem(thisElemInfo, p_info, elems_connected_to_edge[i]);
1774  if (thisElemInfo.size() > 0 && !allowMultipleNeighbors)
1775  {
1776  if (thisElemInfo.size() > 1)
1777  mooseError(
1778  "Found multiple neighbors to current edge/face on surface when only one is allowed");
1779  face_info_comm_edge.push_back(thisElemInfo[0]);
1780  break;
1781  }
1782 
1784  thisElemInfo, p_info, secondary_node, elems_connected_to_edge[i], edge_nodes);
1785  if (thisElemInfo.size() > 0 && !allowMultipleNeighbors)
1786  {
1787  if (thisElemInfo.size() > 1)
1788  mooseError(
1789  "Found multiple neighbors to current edge/face on surface when only one is allowed");
1790  face_info_comm_edge.push_back(thisElemInfo[0]);
1791  break;
1792  }
1793 
1794  for (unsigned int j = 0; j < thisElemInfo.size(); ++j)
1795  face_info_comm_edge.push_back(thisElemInfo[j]);
1796  }
1797  }
1798 }
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:3213
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:165
void createInfoForElem(std::vector< PenetrationInfo *> &thisElemInfo, std::vector< PenetrationInfo *> &p_info, const Node *secondary_node, const Elem *elem, const std::vector< const Node *> &nodes_that_must_be_on_side, const bool check_whether_reasonable=false)
const MooseMesh & _mesh
virtual unsigned int n_nodes() const=0
const Node * node_ptr(const unsigned int i) const
virtual bool is_vertex(const unsigned int i) const=0
void getInfoForElem(std::vector< PenetrationInfo *> &thisElemInfo, std::vector< PenetrationInfo *> &p_info, const Elem *elem)
const std::unordered_map< dof_id_type, std::vector< dof_id_type > > & _node_to_elem_map

◆ getSideCornerNodes()

void PenetrationThread::getSideCornerNodes ( const Elem side,
std::vector< const Node *> &  corner_nodes 
)
protected

Definition at line 935 of file PenetrationThread.C.

Referenced by findRidgeContactPoint().

936 {
937  const ElemType t(side->type());
938  corner_nodes.clear();
939 
940  corner_nodes.push_back(side->node_ptr(0));
941  corner_nodes.push_back(side->node_ptr(1));
942  switch (t)
943  {
944  case EDGE2:
945  case EDGE3:
946  case EDGE4:
947  {
948  break;
949  }
950 
951  case TRI3:
952  case TRI6:
953  case TRI7:
954  {
955  corner_nodes.push_back(side->node_ptr(2));
956  break;
957  }
958 
959  case QUAD4:
960  case QUAD8:
961  case QUAD9:
962  {
963  corner_nodes.push_back(side->node_ptr(2));
964  corner_nodes.push_back(side->node_ptr(3));
965  break;
966  }
967 
968  default:
969  {
970  mooseError("Unsupported face type: ", t);
971  break;
972  }
973  }
974 }
ElemType
QUAD8
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
EDGE4
TRI3
QUAD4
TRI6
EDGE2
const Node * node_ptr(const unsigned int i) const
TRI7
QUAD9
EDGE3
virtual ElemType type() const=0

◆ getSidesOnPrimaryBoundary()

void PenetrationThread::getSidesOnPrimaryBoundary ( std::vector< unsigned int > &  sides,
const Elem *const  elem 
)
protected

◆ getSmoothingEdgeNodesAndWeights()

void PenetrationThread::getSmoothingEdgeNodesAndWeights ( const libMesh::Point p,
const Elem side,
std::vector< std::vector< const Node *>> &  edge_nodes,
std::vector< Real > &  edge_face_weights 
)
protected

Definition at line 1570 of file PenetrationThread.C.

Referenced by getSmoothingFacesAndWeights().

1575 {
1576  const ElemType t(side->type());
1577  const Real & xi = p(0);
1578  const Real & eta = p(1);
1579 
1580  Real smooth_limit = 1.0 - _normal_smoothing_distance;
1581 
1582  switch (t)
1583  {
1584  case EDGE2:
1585  case EDGE3:
1586  case EDGE4:
1587  {
1588  if (xi < -smooth_limit)
1589  {
1590  std::vector<const Node *> en;
1591  en.push_back(side->node_ptr(0));
1592  edge_nodes.push_back(en);
1593  Real fw = 0.5 - (1.0 + xi) / (2.0 * _normal_smoothing_distance);
1594  if (fw > 0.5)
1595  fw = 0.5;
1596  edge_face_weights.push_back(fw);
1597  }
1598  else if (xi > smooth_limit)
1599  {
1600  std::vector<const Node *> en;
1601  en.push_back(side->node_ptr(1));
1602  edge_nodes.push_back(en);
1603  Real fw = 0.5 - (1.0 - xi) / (2.0 * _normal_smoothing_distance);
1604  if (fw > 0.5)
1605  fw = 0.5;
1606  edge_face_weights.push_back(fw);
1607  }
1608  break;
1609  }
1610 
1611  case TRI3:
1612  case TRI6:
1613  case TRI7:
1614  {
1615  if (eta < -smooth_limit)
1616  {
1617  std::vector<const Node *> en;
1618  en.push_back(side->node_ptr(0));
1619  en.push_back(side->node_ptr(1));
1620  edge_nodes.push_back(en);
1621  Real fw = 0.5 - (1.0 + eta) / (2.0 * _normal_smoothing_distance);
1622  if (fw > 0.5)
1623  fw = 0.5;
1624  edge_face_weights.push_back(fw);
1625  }
1626  if ((xi + eta) > smooth_limit)
1627  {
1628  std::vector<const Node *> en;
1629  en.push_back(side->node_ptr(1));
1630  en.push_back(side->node_ptr(2));
1631  edge_nodes.push_back(en);
1632  Real fw = 0.5 - (1.0 - xi - eta) / (2.0 * _normal_smoothing_distance);
1633  if (fw > 0.5)
1634  fw = 0.5;
1635  edge_face_weights.push_back(fw);
1636  }
1637  if (xi < -smooth_limit)
1638  {
1639  std::vector<const Node *> en;
1640  en.push_back(side->node_ptr(2));
1641  en.push_back(side->node_ptr(0));
1642  edge_nodes.push_back(en);
1643  Real fw = 0.5 - (1.0 + xi) / (2.0 * _normal_smoothing_distance);
1644  if (fw > 0.5)
1645  fw = 0.5;
1646  edge_face_weights.push_back(fw);
1647  }
1648  break;
1649  }
1650 
1651  case QUAD4:
1652  case QUAD8:
1653  case QUAD9:
1654  {
1655  if (eta < -smooth_limit)
1656  {
1657  std::vector<const Node *> en;
1658  en.push_back(side->node_ptr(0));
1659  en.push_back(side->node_ptr(1));
1660  edge_nodes.push_back(en);
1661  Real fw = 0.5 - (1.0 + eta) / (2.0 * _normal_smoothing_distance);
1662  if (fw > 0.5)
1663  fw = 0.5;
1664  edge_face_weights.push_back(fw);
1665  }
1666  if (xi > smooth_limit)
1667  {
1668  std::vector<const Node *> en;
1669  en.push_back(side->node_ptr(1));
1670  en.push_back(side->node_ptr(2));
1671  edge_nodes.push_back(en);
1672  Real fw = 0.5 - (1.0 - xi) / (2.0 * _normal_smoothing_distance);
1673  if (fw > 0.5)
1674  fw = 0.5;
1675  edge_face_weights.push_back(fw);
1676  }
1677  if (eta > smooth_limit)
1678  {
1679  std::vector<const Node *> en;
1680  en.push_back(side->node_ptr(2));
1681  en.push_back(side->node_ptr(3));
1682  edge_nodes.push_back(en);
1683  Real fw = 0.5 - (1.0 - eta) / (2.0 * _normal_smoothing_distance);
1684  if (fw > 0.5)
1685  fw = 0.5;
1686  edge_face_weights.push_back(fw);
1687  }
1688  if (xi < -smooth_limit)
1689  {
1690  std::vector<const Node *> en;
1691  en.push_back(side->node_ptr(3));
1692  en.push_back(side->node_ptr(0));
1693  edge_nodes.push_back(en);
1694  Real fw = 0.5 - (1.0 + xi) / (2.0 * _normal_smoothing_distance);
1695  if (fw > 0.5)
1696  fw = 0.5;
1697  edge_face_weights.push_back(fw);
1698  }
1699  break;
1700  }
1701 
1702  default:
1703  {
1704  mooseError("Unsupported face type: ", t);
1705  break;
1706  }
1707  }
1708 }
ElemType
QUAD8
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
EDGE4
TRI3
QUAD4
TRI6
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
EDGE2
const Node * node_ptr(const unsigned int i) const
TRI7
QUAD9
EDGE3
virtual ElemType type() const=0

◆ getSmoothingFacesAndWeights()

void PenetrationThread::getSmoothingFacesAndWeights ( PenetrationInfo info,
std::vector< PenetrationInfo *> &  edge_face_info,
std::vector< Real > &  edge_face_weights,
std::vector< PenetrationInfo *> &  p_info,
const Node secondary_node 
)
protected

Definition at line 1475 of file PenetrationThread.C.

Referenced by smoothNormal().

1480 {
1481  const Elem * side = info->_side;
1482  const Point & p = info->_closest_point_ref;
1483  std::set<dof_id_type> elems_to_exclude;
1484  elems_to_exclude.insert(info->_elem->id());
1485 
1486  std::vector<std::vector<const Node *>> edge_nodes;
1487 
1488  // Get the pairs of nodes along every edge that we are close enough to smooth with
1489  getSmoothingEdgeNodesAndWeights(p, side, edge_nodes, edge_face_weights);
1490  std::vector<Elem *> edge_neighbor_elems;
1491  edge_face_info.resize(edge_nodes.size(), NULL);
1492 
1493  std::vector<unsigned int> edges_without_neighbors;
1494 
1495  for (unsigned int i = 0; i < edge_nodes.size(); ++i)
1496  {
1497  // Sort all sets of edge nodes (needed for comparing edges)
1498  std::sort(edge_nodes[i].begin(), edge_nodes[i].end());
1499 
1500  std::vector<PenetrationInfo *> face_info_comm_edge;
1502  &secondary_node, elems_to_exclude, edge_nodes[i], face_info_comm_edge, p_info);
1503 
1504  if (face_info_comm_edge.size() == 0)
1505  edges_without_neighbors.push_back(i);
1506  else if (face_info_comm_edge.size() > 1)
1507  mooseError("Only one neighbor allowed per edge");
1508  else
1509  edge_face_info[i] = face_info_comm_edge[0];
1510  }
1511 
1512  // Remove edges without neighbors from the vector, starting from end
1513  std::vector<unsigned int>::reverse_iterator rit;
1514  for (rit = edges_without_neighbors.rbegin(); rit != edges_without_neighbors.rend(); ++rit)
1515  {
1516  unsigned int index = *rit;
1517  edge_nodes.erase(edge_nodes.begin() + index);
1518  edge_face_weights.erase(edge_face_weights.begin() + index);
1519  edge_face_info.erase(edge_face_info.begin() + index);
1520  }
1521 
1522  // Handle corner case
1523  if (edge_nodes.size() > 1)
1524  {
1525  if (edge_nodes.size() != 2)
1526  mooseError("Invalid number of smoothing edges");
1527 
1528  // find common node
1529  std::vector<const Node *> common_nodes;
1530  std::set_intersection(edge_nodes[0].begin(),
1531  edge_nodes[0].end(),
1532  edge_nodes[1].begin(),
1533  edge_nodes[1].end(),
1534  std::inserter(common_nodes, common_nodes.end()));
1535 
1536  if (common_nodes.size() != 1)
1537  mooseError("Invalid number of common nodes");
1538 
1539  for (const auto & pinfo : edge_face_info)
1540  elems_to_exclude.insert(pinfo->_elem->id());
1541 
1542  std::vector<PenetrationInfo *> face_info_comm_edge;
1544  &secondary_node, elems_to_exclude, common_nodes, face_info_comm_edge, p_info);
1545 
1546  unsigned int num_corner_neighbors = face_info_comm_edge.size();
1547 
1548  if (num_corner_neighbors > 0)
1549  {
1550  Real fw0 = edge_face_weights[0];
1551  Real fw1 = edge_face_weights[1];
1552 
1553  // Corner weight is product of edge weights. Spread out over multiple neighbors.
1554  Real fw_corner = (fw0 * fw1) / static_cast<Real>(num_corner_neighbors);
1555 
1556  // Adjust original edge weights
1557  edge_face_weights[0] *= (1.0 - fw1);
1558  edge_face_weights[1] *= (1.0 - fw0);
1559 
1560  for (unsigned int i = 0; i < num_corner_neighbors; ++i)
1561  {
1562  edge_face_weights.push_back(fw_corner);
1563  edge_face_info.push_back(face_info_comm_edge[i]);
1564  }
1565  }
1566  }
1567 }
void getSmoothingEdgeNodesAndWeights(const libMesh::Point &p, const Elem *side, std::vector< std::vector< const Node *>> &edge_nodes, std::vector< Real > &edge_face_weights)
MPI_Info info
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
void getInfoForFacesWithCommonNodes(const Node *secondary_node, const std::set< dof_id_type > &elems_to_exclude, const std::vector< const Node *> edge_nodes, std::vector< PenetrationInfo *> &face_info_comm_edge, std::vector< PenetrationInfo *> &p_info)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

◆ interactionsOffCommonEdge()

PenetrationThread::CommonEdgeResult PenetrationThread::interactionsOffCommonEdge ( PenetrationInfo pi1,
PenetrationInfo pi2 
)
protected

Definition at line 775 of file PenetrationThread.C.

Referenced by competeInteractions().

776 {
777  CommonEdgeResult common_edge(NO_COMMON);
778  const std::vector<const Node *> & off_edge_nodes1 = pi1->_off_edge_nodes;
779  const std::vector<const Node *> & off_edge_nodes2 = pi2->_off_edge_nodes;
780  const unsigned dim1 = pi1->_side->dim();
781 
782  if (dim1 == 1)
783  {
784  mooseAssert(pi2->_side->dim() == 1, "Incompatible dimensions.");
785  mooseAssert(off_edge_nodes1.size() < 2 && off_edge_nodes2.size() < 2,
786  "off_edge_nodes size should be <2 for 2D contact");
787  if (off_edge_nodes1.size() == 1 && off_edge_nodes2.size() == 1 &&
788  off_edge_nodes1[0] == off_edge_nodes2[0])
789  common_edge = COMMON_EDGE;
790  }
791  else
792  {
793  mooseAssert(dim1 == 2 && pi2->_side->dim() == 2, "Incompatible dimensions.");
794  mooseAssert(off_edge_nodes1.size() < 3 && off_edge_nodes2.size() < 3,
795  "off_edge_nodes size should be <3 for 3D contact");
796  if (off_edge_nodes1.size() == 1)
797  {
798  if (off_edge_nodes2.size() == 1)
799  {
800  if (off_edge_nodes1[0] == off_edge_nodes2[0])
801  common_edge = COMMON_NODE;
802  }
803  else if (off_edge_nodes2.size() == 2)
804  {
805  if (off_edge_nodes1[0] == off_edge_nodes2[0] || off_edge_nodes1[0] == off_edge_nodes2[1])
806  common_edge = EDGE_AND_COMMON_NODE;
807  }
808  }
809  else if (off_edge_nodes1.size() == 2)
810  {
811  if (off_edge_nodes2.size() == 1)
812  {
813  if (off_edge_nodes1[0] == off_edge_nodes2[0] || off_edge_nodes1[1] == off_edge_nodes2[0])
814  common_edge = EDGE_AND_COMMON_NODE;
815  }
816  else if (off_edge_nodes2.size() == 2)
817  {
818  if ((off_edge_nodes1[0] == off_edge_nodes2[0] &&
819  off_edge_nodes1[1] == off_edge_nodes2[1]) ||
820  (off_edge_nodes1[1] == off_edge_nodes2[0] && off_edge_nodes1[0] == off_edge_nodes2[1]))
821  common_edge = COMMON_EDGE;
822  }
823  }
824  }
825  return common_edge;
826 }
const Elem * _side
std::vector< const Node * > _off_edge_nodes
virtual unsigned short dim() const=0

◆ isFaceReasonableCandidate()

bool PenetrationThread::isFaceReasonableCandidate ( const Elem primary_elem,
const Elem side,
libMesh::FEBase fe,
const libMesh::Point secondary_point,
const Real  tangential_tolerance 
)
protected

Definition at line 1332 of file PenetrationThread.C.

Referenced by createInfoForElem().

1337 {
1338  unsigned int dim = primary_elem->dim();
1339 
1340  const std::vector<Point> & phys_point = fe->get_xyz();
1341 
1342  const std::vector<RealGradient> & dxyz_dxi = fe->get_dxyzdxi();
1343  const std::vector<RealGradient> & dxyz_deta = fe->get_dxyzdeta();
1344 
1345  Point ref_point;
1346 
1347  std::vector<Point> points(1); // Default constructor gives us a point at 0,0,0
1348 
1349  fe->reinit(side, &points);
1350 
1351  RealGradient d = *secondary_point - phys_point[0];
1352 
1353  const Real twosqrt2 = 2.8284; // way more precision than we actually need here
1354  Real max_face_length = side->hmax() + twosqrt2 * tangential_tolerance;
1355 
1356  RealVectorValue normal;
1357  if (dim - 1 == 2)
1358  {
1359  normal = dxyz_dxi[0].cross(dxyz_deta[0]);
1360  }
1361  else if (dim - 1 == 1)
1362  {
1363  const Node * const * elem_nodes = primary_elem->get_nodes();
1364  const Point in_plane_vector1 = *elem_nodes[1] - *elem_nodes[0];
1365  const Point in_plane_vector2 = *elem_nodes[2] - *elem_nodes[0];
1366 
1367  Point out_of_plane_normal = in_plane_vector1.cross(in_plane_vector2);
1368  out_of_plane_normal /= out_of_plane_normal.norm();
1369 
1370  normal = dxyz_dxi[0].cross(out_of_plane_normal);
1371  }
1372  else
1373  {
1374  return true;
1375  }
1376  normal /= normal.norm();
1377 
1378  const Real dot(d * normal);
1379 
1380  const RealGradient normcomp = dot * normal;
1381  const RealGradient tangcomp = d - normcomp;
1382 
1383  const Real tangdist = tangcomp.norm();
1384 
1385  // Increase the size of the zone that we consider if the vector from the face
1386  // to the node has a larger normal component
1387  const Real faceExpansionFactor = 2.0 * (1.0 + normcomp.norm() / d.norm());
1388 
1389  bool isReasonableCandidate = true;
1390  if (tangdist > faceExpansionFactor * max_face_length)
1391  {
1392  isReasonableCandidate = false;
1393  }
1394  return isReasonableCandidate;
1395 }
auto norm() const
virtual_for_inffe const std::vector< RealGradient > & get_dxyzdeta() const
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:165
virtual Real hmax() const
const Node *const * get_nodes() const
virtual void reinit(const Elem *elem, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr)=0
virtual_for_inffe const std::vector< Point > & get_xyz() const
TypeVector< typename CompareTypes< Real, T2 >::supertype > cross(const TypeVector< T2 > &v) const
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual unsigned short dim() const=0
virtual_for_inffe const std::vector< RealGradient > & get_dxyzdxi() const

◆ join()

void PenetrationThread::join ( const PenetrationThread other)

Definition at line 641 of file PenetrationThread.C.

642 {
644  other._recheck_secondary_nodes.begin(),
645  other._recheck_secondary_nodes.end());
646 }
std::vector< dof_id_type > _recheck_secondary_nodes
List of secondary nodes for which penetration was not detected in the current patch and for which pat...

◆ operator()()

void PenetrationThread::operator() ( const NodeIdRange range)

Definition at line 159 of file PenetrationThread.C.

160 {
161  ParallelUniqueId puid;
162  _tid = puid.id;
163 
164  // Must get the variables every time this is run because _tid can change
165  if (_do_normal_smoothing &&
167  {
168  _nodal_normal_x = &_subproblem.getStandardVariable(_tid, "nodal_normal_x");
169  _nodal_normal_y = &_subproblem.getStandardVariable(_tid, "nodal_normal_y");
170  _nodal_normal_z = &_subproblem.getStandardVariable(_tid, "nodal_normal_z");
171  }
172 
173  const BoundaryInfo & boundary_info = _mesh.getMesh().get_boundary_info();
174  std::unique_ptr<PointLocatorBase> point_locator;
175  if (_use_point_locator)
176  point_locator = _mesh.getPointLocator();
177 
178  for (const auto & node_id : range)
179  {
180  const Node & node = _mesh.nodeRef(node_id);
181 
182  // We're going to get a reference to the pointer for the pinfo for this node
183  // This will allow us to manipulate this pointer without having to go through
184  // the _penetration_info map... meaning this is the only mutex we'll have to do!
185  pinfo_mutex.lock();
188 
189  std::vector<PenetrationInfo *> p_info;
190  bool info_set(false);
191 
192  // See if we already have info about this node
193  if (info)
194  {
195  FEBase * fe_elem = _fes[_tid][info->_elem->dim()];
196  FEBase * fe_side = _fes[_tid][info->_side->dim()];
197 
198  if (!_update_location && (info->_distance >= 0 || info->isCaptured()))
199  {
200  const Point contact_ref = info->_closest_point_ref;
201  bool contact_point_on_side(false);
202 
203  // Secondary position must be the previous contact point
204  // Use the previous reference coordinates
205  std::vector<Point> points(1);
206  points[0] = contact_ref;
207  const std::vector<Point> & secondary_pos = fe_side->get_xyz();
208  bool search_succeeded = false;
209 
211  fe_elem,
212  fe_side,
213  _fe_type,
214  secondary_pos[0],
215  false,
217  contact_point_on_side,
218  search_succeeded);
219 
220  // Restore the original reference coordinates
221  info->_closest_point_ref = contact_ref;
222  // Just calculated as the distance of the contact point off the surface (0). Set to 0 to
223  // avoid round-off.
224  info->_distance = 0.0;
225  info_set = true;
226  }
227  else
228  {
229  Real old_tangential_distance(info->_tangential_distance);
230  bool contact_point_on_side(false);
231  bool search_succeeded = false;
232 
234  fe_elem,
235  fe_side,
236  _fe_type,
237  node,
238  false,
240  contact_point_on_side,
241  search_succeeded);
242 
243  if (contact_point_on_side)
244  {
245  if (info->_tangential_distance <= 0.0) // on the face
246  {
247  info_set = true;
248  }
249  else if (info->_tangential_distance > 0.0 && old_tangential_distance > 0.0)
250  { // off the face but within tolerance, was that way on the last step too
251  if (info->_side->dim() == 2 && info->_off_edge_nodes.size() < 2)
252  { // Closest point on face is on a node rather than an edge. Another
253  // face might be a better candidate.
254  }
255  else
256  {
257  info_set = true;
258  }
259  }
260  }
261  }
262  }
263 
264  if (!info_set)
265  {
266  const Node * closest_node = _nearest_node.nearestNode(node.id());
267 
268  std::vector<dof_id_type> located_elem_ids;
269  const std::vector<dof_id_type> * closest_elems;
270 
271  if (_use_point_locator)
272  {
273  std::set<const Elem *> candidate_elements;
274  (*point_locator)(*closest_node, candidate_elements);
275 
276  if (candidate_elements.empty())
277  mooseError("No proximate elements found at node ",
278  closest_node->id(),
279  " at ",
280  static_cast<const Point &>(*closest_node),
281  " on boundary ",
283  ". This should never happen.");
284 
285  for (const Elem * elem : candidate_elements)
286  {
287  for (auto s : elem->side_index_range())
288  if (boundary_info.has_boundary_id(elem, s, _primary_boundary))
289  {
290  located_elem_ids.push_back(elem->id());
291  break;
292  }
293  }
294 
295  if (located_elem_ids.empty())
296  mooseError("No proximate elements found at node ",
297  closest_node->id(),
298  " at ",
299  static_cast<const Point &>(*closest_node),
300  " on boundary ",
302  " share that boundary. This may happen if the mesh uses the same boundary id "
303  "for a nodeset and an unrelated sideset.");
304 
305  closest_elems = &located_elem_ids;
306  }
307  else
308  {
309  auto node_to_elem_pair = _node_to_elem_map.find(closest_node->id());
310  mooseAssert(node_to_elem_pair != _node_to_elem_map.end(),
311  "Missing entry in node to elem map");
312  closest_elems = &(node_to_elem_pair->second);
313  }
314 
315  for (const auto & elem_id : *closest_elems)
316  {
317  const Elem * elem = _mesh.elemPtr(elem_id);
318 
319  std::vector<PenetrationInfo *> thisElemInfo;
320 
321  std::vector<const Node *> nodesThatMustBeOnSide;
322  // If we have a disconnected mesh, we might not have *any*
323  // nodes that must be on a side we check; we'll rely on
324  // boundary info to find valid sides, then rely on comparing
325  // closest points from each to find the best.
326  //
327  // If we don't have a disconnected mesh, then for maximum
328  // backwards compatibility we're still using the older ridge
329  // and peak detection code, which depends on us ruling out
330  // sides that don't touch closest_node.
331  if (!_use_point_locator)
332  nodesThatMustBeOnSide.push_back(closest_node);
334  thisElemInfo, p_info, &node, elem, nodesThatMustBeOnSide, _check_whether_reasonable);
335  }
336 
337  if (_use_point_locator)
338  {
339  Real min_distance_sq = std::numeric_limits<Real>::max();
340  Point best_point;
341  unsigned int best_i = invalid_uint;
342 
343  // Find closest point in all p_info to the node of interest
344  for (unsigned int i = 0; i < p_info.size(); ++i)
345  {
346  const Point closest_point = closest_point_to_side(node, *p_info[i]->_side);
347  const Real distance_sq = (closest_point - node).norm_sq();
348  if (distance_sq < min_distance_sq)
349  {
350  min_distance_sq = distance_sq;
351  best_point = closest_point;
352  best_i = i;
353  }
354  }
355 
356  p_info[best_i]->_closest_point = best_point;
357  p_info[best_i]->_distance =
358  (p_info[best_i]->_distance >= 0.0 ? 1.0 : -1.0) * std::sqrt(min_distance_sq);
360  mooseError("Normal smoothing not implemented with point locator code");
361  Point normal = (best_point - node).unit();
362  const Real dot = normal * p_info[best_i]->_normal;
363  if (dot < 0)
364  normal *= -1;
365  p_info[best_i]->_normal = normal;
366 
367  switchInfo(info, p_info[best_i]);
368  info_set = true;
369  }
370  else
371  {
372  if (p_info.size() == 1)
373  {
374  if (p_info[0]->_tangential_distance <= _tangential_tolerance)
375  {
376  switchInfo(info, p_info[0]);
377  info_set = true;
378  }
379  }
380  else if (p_info.size() > 1)
381  {
382  // Loop through all pairs of faces, and check for contact on ridge between each face pair
383  std::vector<RidgeData> ridgeDataVec;
384  for (unsigned int i = 0; i + 1 < p_info.size(); ++i)
385  for (unsigned int j = i + 1; j < p_info.size(); ++j)
386  {
387  Point closest_coor;
388  Real tangential_distance(0.0);
389  const Node * closest_node_on_ridge = NULL;
390  unsigned int index = 0;
391  Point closest_coor_ref;
392  bool found_ridge_contact_point = findRidgeContactPoint(closest_coor,
393  tangential_distance,
394  closest_node_on_ridge,
395  index,
396  closest_coor_ref,
397  p_info,
398  i,
399  j);
400  if (found_ridge_contact_point)
401  {
402  RidgeData rpd;
403  rpd._closest_coor = closest_coor;
404  rpd._tangential_distance = tangential_distance;
405  rpd._closest_node = closest_node_on_ridge;
406  rpd._index = index;
407  rpd._closest_coor_ref = closest_coor_ref;
408  ridgeDataVec.push_back(rpd);
409  }
410  }
411 
412  if (ridgeDataVec.size() > 0) // Either find the ridge pair that is the best or find a peak
413  {
414  // Group together ridges for which we are off the edge of a common node.
415  // Those are peaks.
416  std::vector<RidgeSetData> ridgeSetDataVec;
417  for (unsigned int i = 0; i < ridgeDataVec.size(); ++i)
418  {
419  bool foundSetWithMatchingNode = false;
420  for (unsigned int j = 0; j < ridgeSetDataVec.size(); ++j)
421  {
422  if (ridgeDataVec[i]._closest_node != NULL &&
423  ridgeDataVec[i]._closest_node == ridgeSetDataVec[j]._closest_node)
424  {
425  foundSetWithMatchingNode = true;
426  ridgeSetDataVec[j]._ridge_data_vec.push_back(ridgeDataVec[i]);
427  break;
428  }
429  }
430  if (!foundSetWithMatchingNode)
431  {
432  RidgeSetData rsd;
433  rsd._distance = std::numeric_limits<Real>::max();
434  rsd._ridge_data_vec.push_back(ridgeDataVec[i]);
435  rsd._closest_node = ridgeDataVec[i]._closest_node;
436  ridgeSetDataVec.push_back(rsd);
437  }
438  }
439  // Compute distance to each set of ridges
440  for (unsigned int i = 0; i < ridgeSetDataVec.size(); ++i)
441  {
442  if (ridgeSetDataVec[i]._closest_node !=
443  NULL) // Either a peak or off the edge of single ridge
444  {
445  if (ridgeSetDataVec[i]._ridge_data_vec.size() == 1) // off edge of single ridge
446  {
447  if (ridgeSetDataVec[i]._ridge_data_vec[0]._tangential_distance <=
448  _tangential_tolerance) // off within tolerance
449  {
450  ridgeSetDataVec[i]._closest_coor =
451  ridgeSetDataVec[i]._ridge_data_vec[0]._closest_coor;
452  Point contact_point_vec = node - ridgeSetDataVec[i]._closest_coor;
453  ridgeSetDataVec[i]._distance = contact_point_vec.norm();
454  }
455  }
456  else // several ridges join at common node to make a peak. The common node is the
457  // contact point
458  {
459  ridgeSetDataVec[i]._closest_coor = *ridgeSetDataVec[i]._closest_node;
460  Point contact_point_vec = node - ridgeSetDataVec[i]._closest_coor;
461  ridgeSetDataVec[i]._distance = contact_point_vec.norm();
462  }
463  }
464  else // on a single ridge
465  {
466  ridgeSetDataVec[i]._closest_coor =
467  ridgeSetDataVec[i]._ridge_data_vec[0]._closest_coor;
468  Point contact_point_vec = node - ridgeSetDataVec[i]._closest_coor;
469  ridgeSetDataVec[i]._distance = contact_point_vec.norm();
470  }
471  }
472  // Find the set of ridges closest to us.
473  unsigned int closest_ridge_set_index(0);
474  Real closest_distance(ridgeSetDataVec[0]._distance);
475  Point closest_point(ridgeSetDataVec[0]._closest_coor);
476  for (unsigned int i = 1; i < ridgeSetDataVec.size(); ++i)
477  {
478  if (ridgeSetDataVec[i]._distance < closest_distance)
479  {
480  closest_ridge_set_index = i;
481  closest_distance = ridgeSetDataVec[i]._distance;
482  closest_point = ridgeSetDataVec[i]._closest_coor;
483  }
484  }
485 
486  if (closest_distance <
487  std::numeric_limits<Real>::max()) // contact point is on the closest ridge set
488  {
489  // find the face in the ridge set with the smallest index, assign that one to the
490  // interaction
491  // TODO: We may need to select the face with the largest projected distance
492  // rather than the smallest index, similar to what is done when picking the
493  // face in findRidgeContactPoint() to better condition corner-case checks for
494  // sign changes. That's less likely to be a problem here, though, and the
495  // code to do that would be messier.
496  unsigned int face_index(std::numeric_limits<unsigned int>::max());
497  for (unsigned int i = 0;
498  i < ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec.size();
499  ++i)
500  {
501  if (ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[i]._index < face_index)
502  face_index = ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[i]._index;
503  }
504 
505  mooseAssert(face_index < std::numeric_limits<unsigned int>::max(),
506  "face_index invalid");
507 
508  p_info[face_index]->_closest_point = closest_point;
509  p_info[face_index]->_distance =
510  (p_info[face_index]->_distance >= 0.0 ? 1.0 : -1.0) * closest_distance;
511  // Calculate the normal as the vector from the ridge to the point only if we're not
512  // doing normal
513  // smoothing. Normal smoothing will average out the normals on its own.
515  {
516  Point normal(closest_point - node);
517  const Real len(normal.norm());
518  if (len > 0)
519  {
520  normal /= len;
521  }
522  const Real dot(normal * p_info[face_index]->_normal);
523  if (dot < 0)
524  normal *= -1;
525  p_info[face_index]->_normal = normal;
526  }
527  p_info[face_index]->_tangential_distance = 0.0;
528 
529  Point closest_point_ref;
530  if (ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec.size() ==
531  1) // contact with a single ridge rather than a peak
532  {
533  p_info[face_index]->_tangential_distance = ridgeSetDataVec[closest_ridge_set_index]
534  ._ridge_data_vec[0]
535  ._tangential_distance;
536  p_info[face_index]->_closest_point_ref =
537  ridgeSetDataVec[closest_ridge_set_index]._ridge_data_vec[0]._closest_coor_ref;
538  }
539  else
540  { // peak
541  const Node * closest_node_on_face;
542  bool restricted = restrictPointToFace(p_info[face_index]->_closest_point_ref,
543  closest_node_on_face,
544  p_info[face_index]->_side);
545  if (restricted)
546  {
547  if (closest_node_on_face !=
548  ridgeSetDataVec[closest_ridge_set_index]._closest_node)
549  {
550  mooseError("Closest node when restricting point to face != closest node from "
551  "RidgeSetData");
552  }
553  }
554  }
555 
556  FEBase * fe = _fes[_tid][p_info[face_index]->_side->dim()];
557  std::vector<Point> points(1);
558  points[0] = p_info[face_index]->_closest_point_ref;
559  fe->reinit(p_info[face_index]->_side, &points);
560  p_info[face_index]->_side_phi = fe->get_phi();
561  p_info[face_index]->_side_grad_phi = fe->get_dphi();
562  p_info[face_index]->_dxyzdxi = fe->get_dxyzdxi();
563  p_info[face_index]->_dxyzdeta = fe->get_dxyzdeta();
564  p_info[face_index]->_d2xyzdxideta = fe->get_d2xyzdxideta();
565 
566  switchInfo(info, p_info[face_index]);
567  info_set = true;
568  }
569  else
570  { // todo:remove invalid ridge cases so they don't mess up individual face
571  // competition????
572  }
573  }
574 
575  if (!info_set) // contact wasn't on a ridge -- compete individual interactions
576  {
577  unsigned int best(0), i(1);
578  do
579  {
580  CompeteInteractionResult CIResult = competeInteractions(p_info[best], p_info[i]);
581  if (CIResult == FIRST_WINS)
582  {
583  i++;
584  }
585  else if (CIResult == SECOND_WINS)
586  {
587  best = i;
588  i++;
589  }
590  else if (CIResult == NEITHER_WINS)
591  {
592  best = i + 1;
593  i += 2;
594  }
595  } while (i < p_info.size() && best < p_info.size());
596  if (best < p_info.size())
597  {
598  // Ensure final info is within the tangential tolerance
599  if (p_info[best]->_tangential_distance <= _tangential_tolerance)
600  {
601  switchInfo(info, p_info[best]);
602  info_set = true;
603  }
604  }
605  }
606  }
607  }
608  }
609 
610  if (!info_set)
611  {
612  // If penetration is not detected within the saved patch, it is possible
613  // that the secondary node has moved outside the saved patch. So, the patch
614  // for the secondary nodes saved in _recheck_secondary_nodes has to be updated
615  // and the penetration detection has to be re-run on the updated patch.
616 
617  _recheck_secondary_nodes.push_back(node_id);
618 
619  delete info;
620  info = NULL;
621  }
622  else
623  {
624  smoothNormal(info, p_info, node);
625  FEBase * fe = _fes[_tid][info->_side->dim()];
626  computeSlip(*fe, *info);
627  }
628 
629  for (unsigned int j = 0; j < p_info.size(); ++j)
630  {
631  if (p_info[j])
632  {
633  delete p_info[j];
634  p_info[j] = NULL;
635  }
636  }
637  }
638 }
MooseVariable * _nodal_normal_z
auto norm() const
bool has_boundary_id(const Node *const node, const boundary_id_type id) const
const unsigned int invalid_uint
virtual_for_inffe const std::vector< RealGradient > & get_dxyzdeta() const
virtual Elem * elemPtr(const dof_id_type i)
Definition: MooseMesh.C:3213
MPI_Info info
bool findRidgeContactPoint(libMesh::Point &contact_point, Real &tangential_distance, const Node *&closest_node, unsigned int &index, libMesh::Point &contact_point_ref, std::vector< PenetrationInfo *> &p_info, const unsigned int index1, const unsigned int index2)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
Data structure used to hold penetration information.
auto norm_sq(const T &a)
NearestNodeLocator & _nearest_node
virtual_for_inffe const std::vector< RealGradient > & get_d2xyzdxideta() const
void createInfoForElem(std::vector< PenetrationInfo *> &thisElemInfo, std::vector< PenetrationInfo *> &p_info, const Node *secondary_node, const Elem *elem, const std::vector< const Node *> &nodes_that_must_be_on_side, const bool check_whether_reasonable=false)
Threads::spin_mutex pinfo_mutex
const BoundaryInfo & get_boundary_info() const
virtual const Node & nodeRef(const dof_id_type i) const
Definition: MooseMesh.C:839
void smoothNormal(PenetrationInfo *info, std::vector< PenetrationInfo *> &p_info, const Node &node)
libMesh::FEType & _fe_type
auto max(const L &left, const R &right)
BoundaryID _primary_boundary
SubProblem & _subproblem
const std::vector< std::vector< OutputGradient > > & get_dphi() const
const MooseMesh & _mesh
dof_id_type id() const
MeshBase & getMesh()
Accessor for the underlying libMesh Mesh object.
Definition: MooseMesh.C:3548
CompeteInteractionResult competeInteractions(PenetrationInfo *pi1, PenetrationInfo *pi2)
When interactions are identified between a node and two faces, compete between the faces to determine...
void computeSlip(libMesh::FEBase &fe, PenetrationInfo &info)
virtual void reinit(const Elem *elem, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr)=0
void findContactPoint(PenetrationInfo &p_info, libMesh::FEBase *fe_elem, libMesh::FEBase *fe_side, libMesh::FEType &fe_side_type, const libMesh::Point &secondary_point, bool start_with_centroid, const Real tangential_tolerance, bool &contact_point_on_side, bool &search_succeeded)
Finds the closest point (called the contact point) on the primary_elem on side "side" to the secondar...
std::vector< dof_id_type > _recheck_secondary_nodes
List of secondary nodes for which penetration was not detected in the current patch and for which pat...
virtual MooseVariable & getStandardVariable(const THREAD_ID tid, const std::string &var_name)=0
Returns the variable reference for requested MooseVariable which may be in any system.
virtual_for_inffe const std::vector< Point > & get_xyz() const
bool restrictPointToFace(libMesh::Point &p, const Node *&closest_node, const Elem *side)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::map< dof_id_type, PenetrationInfo * > & _penetration_info
const Node * nearestNode(dof_id_type node_id)
Valid to call this after findNodes() has been called to get a pointer to the nearest node...
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
MooseVariable * _nodal_normal_y
virtual std::unique_ptr< libMesh::PointLocatorBase > getPointLocator() const
Proxy function to get a (sub)PointLocator from either the underlying libMesh mesh (default)...
Definition: MooseMesh.C:3837
void switchInfo(PenetrationInfo *&info, PenetrationInfo *&infoNew)
virtual_for_inffe const std::vector< RealGradient > & get_dxyzdxi() const
MooseVariable * _nodal_normal_x
const std::unordered_map< dof_id_type, std::vector< dof_id_type > > & _node_to_elem_map
std::vector< std::vector< libMesh::FEBase * > > & _fes
PenetrationLocator::NORMAL_SMOOTHING_METHOD _normal_smoothing_method
const std::vector< std::vector< OutputShape > > & get_phi() const

◆ restrictPointToFace()

bool PenetrationThread::restrictPointToFace ( libMesh::Point p,
const Node *&  closest_node,
const Elem side 
)
protected

Definition at line 1151 of file PenetrationThread.C.

Referenced by operator()().

1152 {
1153  const ElemType t(side->type());
1154  Real & xi = p(0);
1155  Real & eta = p(1);
1156  closest_node = NULL;
1157 
1158  bool off_of_this_face(false);
1159 
1160  switch (t)
1161  {
1162  case EDGE2:
1163  case EDGE3:
1164  case EDGE4:
1165  {
1166  if (xi < -1.0)
1167  {
1168  xi = -1.0;
1169  off_of_this_face = true;
1170  closest_node = side->node_ptr(0);
1171  }
1172  else if (xi > 1.0)
1173  {
1174  xi = 1.0;
1175  off_of_this_face = true;
1176  closest_node = side->node_ptr(1);
1177  }
1178  break;
1179  }
1180 
1181  case TRI3:
1182  case TRI6:
1183  case TRI7:
1184  {
1185  if (eta < 0.0)
1186  {
1187  eta = 0.0;
1188  off_of_this_face = true;
1189  if (xi < 0.5)
1190  {
1191  closest_node = side->node_ptr(0);
1192  if (xi < 0.0)
1193  xi = 0.0;
1194  }
1195  else
1196  {
1197  closest_node = side->node_ptr(1);
1198  if (xi > 1.0)
1199  xi = 1.0;
1200  }
1201  }
1202  else if ((xi + eta) > 1.0)
1203  {
1204  Real delta = (xi + eta - 1.0) / 2.0;
1205  xi -= delta;
1206  eta -= delta;
1207  off_of_this_face = true;
1208  if (xi > 0.5)
1209  {
1210  closest_node = side->node_ptr(1);
1211  if (xi > 1.0)
1212  {
1213  xi = 1.0;
1214  eta = 0.0;
1215  }
1216  }
1217  else
1218  {
1219  closest_node = side->node_ptr(2);
1220  if (xi < 0.0)
1221  {
1222  xi = 0.0;
1223  eta = 1.0;
1224  }
1225  }
1226  }
1227  else if (xi < 0.0)
1228  {
1229  xi = 0.0;
1230  off_of_this_face = true;
1231  if (eta > 0.5)
1232  {
1233  closest_node = side->node_ptr(2);
1234  if (eta > 1.0)
1235  eta = 1.0;
1236  }
1237  else
1238  {
1239  closest_node = side->node_ptr(0);
1240  if (eta < 0.0)
1241  eta = 0.0;
1242  }
1243  }
1244  break;
1245  }
1246 
1247  case QUAD4:
1248  case QUAD8:
1249  case QUAD9:
1250  {
1251  if (eta < -1.0)
1252  {
1253  eta = -1.0;
1254  off_of_this_face = true;
1255  if (xi < 0.0)
1256  {
1257  closest_node = side->node_ptr(0);
1258  if (xi < -1.0)
1259  xi = -1.0;
1260  }
1261  else
1262  {
1263  closest_node = side->node_ptr(1);
1264  if (xi > 1.0)
1265  xi = 1.0;
1266  }
1267  }
1268  else if (xi > 1.0)
1269  {
1270  xi = 1.0;
1271  off_of_this_face = true;
1272  if (eta < 0.0)
1273  {
1274  closest_node = side->node_ptr(1);
1275  if (eta < -1.0)
1276  eta = -1.0;
1277  }
1278  else
1279  {
1280  closest_node = side->node_ptr(2);
1281  if (eta > 1.0)
1282  eta = 1.0;
1283  }
1284  }
1285  else if (eta > 1.0)
1286  {
1287  eta = 1.0;
1288  off_of_this_face = true;
1289  if (xi < 0.0)
1290  {
1291  closest_node = side->node_ptr(3);
1292  if (xi < -1.0)
1293  xi = -1.0;
1294  }
1295  else
1296  {
1297  closest_node = side->node_ptr(2);
1298  if (xi > 1.0)
1299  xi = 1.0;
1300  }
1301  }
1302  else if (xi < -1.0)
1303  {
1304  xi = -1.0;
1305  off_of_this_face = true;
1306  if (eta < 0.0)
1307  {
1308  closest_node = side->node_ptr(0);
1309  if (eta < -1.0)
1310  eta = -1.0;
1311  }
1312  else
1313  {
1314  closest_node = side->node_ptr(3);
1315  if (eta > 1.0)
1316  eta = 1.0;
1317  }
1318  }
1319  break;
1320  }
1321 
1322  default:
1323  {
1324  mooseError("Unsupported face type: ", t);
1325  break;
1326  }
1327  }
1328  return off_of_this_face;
1329 }
ElemType
QUAD8
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
EDGE4
TRI3
QUAD4
TRI6
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
EDGE2
const Node * node_ptr(const unsigned int i) const
TRI7
QUAD9
EDGE3
virtual ElemType type() const=0

◆ restrictPointToSpecifiedEdgeOfFace()

bool PenetrationThread::restrictPointToSpecifiedEdgeOfFace ( libMesh::Point p,
const Node *&  closest_node,
const Elem side,
const std::vector< const Node *> &  edge_nodes 
)
protected

Definition at line 977 of file PenetrationThread.C.

Referenced by findRidgeContactPoint().

981 {
982  const ElemType t = side->type();
983  Real & xi = p(0);
984  Real & eta = p(1);
985  closest_node = NULL;
986 
987  std::vector<unsigned int> local_node_indices;
988  for (const auto & edge_node : edge_nodes)
989  {
990  unsigned int local_index = side->get_node_index(edge_node);
991  if (local_index == libMesh::invalid_uint)
992  mooseError("Side does not contain node");
993  local_node_indices.push_back(local_index);
994  }
995  mooseAssert(local_node_indices.size() == side->dim(),
996  "Number of edge nodes must match side dimensionality");
997  std::sort(local_node_indices.begin(), local_node_indices.end());
998 
999  bool off_of_this_edge = false;
1000 
1001  switch (t)
1002  {
1003  case EDGE2:
1004  case EDGE3:
1005  case EDGE4:
1006  {
1007  if (local_node_indices[0] == 0)
1008  {
1009  if (xi <= -1.0)
1010  {
1011  xi = -1.0;
1012  off_of_this_edge = true;
1013  closest_node = side->node_ptr(0);
1014  }
1015  }
1016  else if (local_node_indices[0] == 1)
1017  {
1018  if (xi >= 1.0)
1019  {
1020  xi = 1.0;
1021  off_of_this_edge = true;
1022  closest_node = side->node_ptr(1);
1023  }
1024  }
1025  else
1026  {
1027  mooseError("Invalid local node indices");
1028  }
1029  break;
1030  }
1031 
1032  case TRI3:
1033  case TRI6:
1034  case TRI7:
1035  {
1036  if ((local_node_indices[0] == 0) && (local_node_indices[1] == 1))
1037  {
1038  if (eta <= 0.0)
1039  {
1040  eta = 0.0;
1041  off_of_this_edge = true;
1042  if (xi < 0.0)
1043  closest_node = side->node_ptr(0);
1044  else if (xi > 1.0)
1045  closest_node = side->node_ptr(1);
1046  }
1047  }
1048  else if ((local_node_indices[0] == 1) && (local_node_indices[1] == 2))
1049  {
1050  if ((xi + eta) > 1.0)
1051  {
1052  Real delta = (xi + eta - 1.0) / 2.0;
1053  xi -= delta;
1054  eta -= delta;
1055  off_of_this_edge = true;
1056  if (xi > 1.0)
1057  closest_node = side->node_ptr(1);
1058  else if (xi < 0.0)
1059  closest_node = side->node_ptr(2);
1060  }
1061  }
1062  else if ((local_node_indices[0] == 0) && (local_node_indices[1] == 2))
1063  {
1064  if (xi <= 0.0)
1065  {
1066  xi = 0.0;
1067  off_of_this_edge = true;
1068  if (eta > 1.0)
1069  closest_node = side->node_ptr(2);
1070  else if (eta < 0.0)
1071  closest_node = side->node_ptr(0);
1072  }
1073  }
1074  else
1075  {
1076  mooseError("Invalid local node indices");
1077  }
1078 
1079  break;
1080  }
1081 
1082  case QUAD4:
1083  case QUAD8:
1084  case QUAD9:
1085  {
1086  if ((local_node_indices[0] == 0) && (local_node_indices[1] == 1))
1087  {
1088  if (eta <= -1.0)
1089  {
1090  eta = -1.0;
1091  off_of_this_edge = true;
1092  if (xi < -1.0)
1093  closest_node = side->node_ptr(0);
1094  else if (xi > 1.0)
1095  closest_node = side->node_ptr(1);
1096  }
1097  }
1098  else if ((local_node_indices[0] == 1) && (local_node_indices[1] == 2))
1099  {
1100  if (xi >= 1.0)
1101  {
1102  xi = 1.0;
1103  off_of_this_edge = true;
1104  if (eta < -1.0)
1105  closest_node = side->node_ptr(1);
1106  else if (eta > 1.0)
1107  closest_node = side->node_ptr(2);
1108  }
1109  }
1110  else if ((local_node_indices[0] == 2) && (local_node_indices[1] == 3))
1111  {
1112  if (eta >= 1.0)
1113  {
1114  eta = 1.0;
1115  off_of_this_edge = true;
1116  if (xi < -1.0)
1117  closest_node = side->node_ptr(3);
1118  else if (xi > 1.0)
1119  closest_node = side->node_ptr(2);
1120  }
1121  }
1122  else if ((local_node_indices[0] == 0) && (local_node_indices[1] == 3))
1123  {
1124  if (xi <= -1.0)
1125  {
1126  xi = -1.0;
1127  off_of_this_edge = true;
1128  if (eta < -1.0)
1129  closest_node = side->node_ptr(0);
1130  else if (eta > 1.0)
1131  closest_node = side->node_ptr(3);
1132  }
1133  }
1134  else
1135  {
1136  mooseError("Invalid local node indices");
1137  }
1138  break;
1139  }
1140 
1141  default:
1142  {
1143  mooseError("Unsupported face type: ", t);
1144  break;
1145  }
1146  }
1147  return off_of_this_edge;
1148 }
ElemType
QUAD8
const unsigned int invalid_uint
unsigned int get_node_index(const Node *node_ptr) const
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
EDGE4
TRI3
QUAD4
TRI6
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
EDGE2
virtual unsigned short dim() const=0
const Node * node_ptr(const unsigned int i) const
TRI7
QUAD9
EDGE3
virtual ElemType type() const=0

◆ smoothNormal()

void PenetrationThread::smoothNormal ( PenetrationInfo info,
std::vector< PenetrationInfo *> &  p_info,
const Node node 
)
protected

Definition at line 1417 of file PenetrationThread.C.

Referenced by operator()().

1420 {
1422  {
1424  {
1425  // If we are within the smoothing distance of any edges or corners, find the
1426  // corner nodes for those edges/corners, and weights from distance to edge/corner
1427  std::vector<Real> edge_face_weights;
1428  std::vector<PenetrationInfo *> edge_face_info;
1429 
1430  getSmoothingFacesAndWeights(info, edge_face_info, edge_face_weights, p_info, node);
1431 
1432  mooseAssert(edge_face_info.size() == edge_face_weights.size(),
1433  "edge_face_info.size() != edge_face_weights.size()");
1434 
1435  if (edge_face_info.size() > 0)
1436  {
1437  // Smooth the normal using the weighting functions for all participating faces.
1438  RealVectorValue new_normal;
1439  Real this_face_weight = 1.0;
1440 
1441  for (unsigned int efwi = 0; efwi < edge_face_weights.size(); ++efwi)
1442  {
1443  PenetrationInfo * npi = edge_face_info[efwi];
1444  if (npi)
1445  new_normal += npi->_normal * edge_face_weights[efwi];
1446 
1447  this_face_weight -= edge_face_weights[efwi];
1448  }
1449  mooseAssert(this_face_weight >= (0.25 - 1e-8),
1450  "Sum of weights of other faces shouldn't exceed 0.75");
1451  new_normal += info->_normal * this_face_weight;
1452 
1453  const Real len = new_normal.norm();
1454  if (len > 0)
1455  new_normal /= len;
1456 
1457  info->_normal = new_normal;
1458  }
1459  }
1461  {
1462  // params.addParam<VariableName>("var_name","description");
1463  // getParam<VariableName>("var_name")
1464  info->_normal(0) = _nodal_normal_x->getValue(info->_side, info->_side_phi);
1465  info->_normal(1) = _nodal_normal_y->getValue(info->_side, info->_side_phi);
1466  info->_normal(2) = _nodal_normal_z->getValue(info->_side, info->_side_phi);
1467  const Real len(info->_normal.norm());
1468  if (len > 0)
1469  info->_normal /= len;
1470  }
1471  }
1472 }
MooseVariable * _nodal_normal_z
auto norm() const
RealVectorValue _normal
MPI_Info info
OutputType getValue(const Elem *elem, const std::vector< std::vector< OutputShape >> &phi) const
Compute the variable value at a point on an element.
Data structure used to hold penetration information.
void getSmoothingFacesAndWeights(PenetrationInfo *info, std::vector< PenetrationInfo *> &edge_face_info, std::vector< Real > &edge_face_weights, std::vector< PenetrationInfo *> &p_info, const Node &secondary_node)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
MooseVariable * _nodal_normal_y
MooseVariable * _nodal_normal_x
PenetrationLocator::NORMAL_SMOOTHING_METHOD _normal_smoothing_method

◆ switchInfo()

void PenetrationThread::switchInfo ( PenetrationInfo *&  info,
PenetrationInfo *&  infoNew 
)
protected

Definition at line 649 of file PenetrationThread.C.

Referenced by operator()().

650 {
651  mooseAssert(infoNew != NULL, "infoNew object is null");
652  if (info)
653  {
654  infoNew->_starting_elem = info->_starting_elem;
655  infoNew->_starting_side_num = info->_starting_side_num;
656  infoNew->_starting_closest_point_ref = info->_starting_closest_point_ref;
657  infoNew->_incremental_slip = info->_incremental_slip;
658  infoNew->_accumulated_slip = info->_accumulated_slip;
659  infoNew->_accumulated_slip_old = info->_accumulated_slip_old;
660  infoNew->_frictional_energy = info->_frictional_energy;
661  infoNew->_frictional_energy_old = info->_frictional_energy_old;
662  infoNew->_contact_force = info->_contact_force;
663  infoNew->_contact_force_old = info->_contact_force_old;
664  infoNew->_lagrange_multiplier = info->_lagrange_multiplier;
665  infoNew->_lagrange_multiplier_slip = info->_lagrange_multiplier_slip;
666  infoNew->_locked_this_step = info->_locked_this_step;
667  infoNew->_stick_locked_this_step = info->_stick_locked_this_step;
668  infoNew->_mech_status = info->_mech_status;
669  infoNew->_mech_status_old = info->_mech_status_old;
670  }
671  else
672  {
673  infoNew->_starting_elem = infoNew->_elem;
674  infoNew->_starting_side_num = infoNew->_side_num;
676  }
677  delete info;
678  info = infoNew;
679  infoNew = NULL; // Set this to NULL so that we don't delete it (now owned by _penetration_info).
680 }
MPI_Info info
const Elem * _starting_elem
unsigned int _stick_locked_this_step
RealVectorValue _contact_force_old
unsigned int _locked_this_step
const Elem * _elem
unsigned int _starting_side_num
unsigned int _side_num
RealVectorValue _contact_force
MECH_STATUS_ENUM _mech_status
Point _starting_closest_point_ref
MECH_STATUS_ENUM _mech_status_old
RealVectorValue _lagrange_multiplier_slip

Member Data Documentation

◆ _check_whether_reasonable

bool PenetrationThread::_check_whether_reasonable
protected

Definition at line 65 of file PenetrationThread.h.

Referenced by operator()().

◆ _do_normal_smoothing

bool PenetrationThread::_do_normal_smoothing
protected

Definition at line 68 of file PenetrationThread.h.

Referenced by operator()(), and smoothNormal().

◆ _elem_side_builder

libMesh::ElemSideBuilder PenetrationThread::_elem_side_builder
protected

Helper for building element sides without extraneous allocation.

Definition at line 87 of file PenetrationThread.h.

Referenced by computeSlip().

◆ _fe_type

libMesh::FEType& PenetrationThread::_fe_type
protected

Definition at line 78 of file PenetrationThread.h.

Referenced by createInfoForElem(), and operator()().

◆ _fes

std::vector<std::vector<libMesh::FEBase *> >& PenetrationThread::_fes
protected

Definition at line 76 of file PenetrationThread.h.

Referenced by createInfoForElem(), findRidgeContactPoint(), and operator()().

◆ _mesh

const MooseMesh& PenetrationThread::_mesh
protected

◆ _nearest_node

NearestNodeLocator& PenetrationThread::_nearest_node
protected

Definition at line 80 of file PenetrationThread.h.

Referenced by operator()().

◆ _nodal_normal_x

MooseVariable* PenetrationThread::_nodal_normal_x
protected

Definition at line 72 of file PenetrationThread.h.

Referenced by operator()(), and smoothNormal().

◆ _nodal_normal_y

MooseVariable* PenetrationThread::_nodal_normal_y
protected

Definition at line 73 of file PenetrationThread.h.

Referenced by operator()(), and smoothNormal().

◆ _nodal_normal_z

MooseVariable* PenetrationThread::_nodal_normal_z
protected

Definition at line 74 of file PenetrationThread.h.

Referenced by operator()(), and smoothNormal().

◆ _node_to_elem_map

const std::unordered_map<dof_id_type, std::vector<dof_id_type> >& PenetrationThread::_node_to_elem_map
protected

Definition at line 82 of file PenetrationThread.h.

Referenced by getInfoForFacesWithCommonNodes(), and operator()().

◆ _normal_smoothing_distance

Real PenetrationThread::_normal_smoothing_distance
protected

Definition at line 69 of file PenetrationThread.h.

Referenced by getSmoothingEdgeNodesAndWeights().

◆ _normal_smoothing_method

PenetrationLocator::NORMAL_SMOOTHING_METHOD PenetrationThread::_normal_smoothing_method
protected

Definition at line 70 of file PenetrationThread.h.

Referenced by operator()(), and smoothNormal().

◆ _penetration_info

std::map<dof_id_type, PenetrationInfo *>& PenetrationThread::_penetration_info
protected

Definition at line 63 of file PenetrationThread.h.

Referenced by operator()().

◆ _primary_boundary

BoundaryID PenetrationThread::_primary_boundary
protected

Definition at line 59 of file PenetrationThread.h.

Referenced by createInfoForElem(), and operator()().

◆ _recheck_secondary_nodes

std::vector<dof_id_type> PenetrationThread::_recheck_secondary_nodes

List of secondary nodes for which penetration was not detected in the current patch and for which patch has to be updated.

Definition at line 53 of file PenetrationThread.h.

Referenced by join(), and operator()().

◆ _secondary_boundary

BoundaryID PenetrationThread::_secondary_boundary
protected

Definition at line 60 of file PenetrationThread.h.

◆ _subproblem

SubProblem& PenetrationThread::_subproblem
protected

Definition at line 56 of file PenetrationThread.h.

Referenced by operator()().

◆ _tangential_tolerance

Real PenetrationThread::_tangential_tolerance
protected

Definition at line 67 of file PenetrationThread.h.

Referenced by competeInteractions(), createInfoForElem(), and operator()().

◆ _tid

THREAD_ID PenetrationThread::_tid
protected

Definition at line 84 of file PenetrationThread.h.

Referenced by createInfoForElem(), findRidgeContactPoint(), and operator()().

◆ _update_location

bool PenetrationThread::_update_location
protected

Definition at line 66 of file PenetrationThread.h.

Referenced by operator()().

◆ _use_point_locator

bool PenetrationThread::_use_point_locator
protected

Definition at line 71 of file PenetrationThread.h.

Referenced by operator()().


The documentation for this class was generated from the following files: