LCOV - code coverage report
Current view: top level - src/geomsearch - PenetrationLocator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 89 116 76.7 %
Date: 2025-07-17 01:28:37 Functions: 8 12 66.7 %
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             : #include "PenetrationLocator.h"
      11             : 
      12             : #include "ArbitraryQuadrature.h"
      13             : #include "Conversion.h"
      14             : #include "GeometricSearchData.h"
      15             : #include "LineSegment.h"
      16             : #include "MooseMesh.h"
      17             : #include "NearestNodeLocator.h"
      18             : #include "PenetrationThread.h"
      19             : #include "SubProblem.h"
      20             : #include "MooseApp.h"
      21             : 
      22             : using namespace libMesh;
      23             : 
      24        1684 : PenetrationLocator::PenetrationLocator(SubProblem & subproblem,
      25             :                                        GeometricSearchData & /*geom_search_data*/,
      26             :                                        MooseMesh & mesh,
      27             :                                        const unsigned int primary_id,
      28             :                                        const unsigned int secondary_id,
      29             :                                        Order order,
      30        1684 :                                        NearestNodeLocator & nearest_node)
      31             :   : Restartable(subproblem.getMooseApp(),
      32        3368 :                 Moose::stringify(primary_id) + "to" + Moose::stringify(secondary_id),
      33             :                 "PenetrationLocator",
      34             :                 0),
      35        1684 :     PerfGraphInterface(subproblem.getMooseApp().perfGraph(),
      36        3368 :                        "PenetrationLocator_" + Moose::stringify(primary_id) + "_" +
      37        1684 :                            Moose::stringify(secondary_id)),
      38        1684 :     _subproblem(subproblem),
      39        1684 :     _mesh(mesh),
      40        1684 :     _primary_boundary(primary_id),
      41        1684 :     _secondary_boundary(secondary_id),
      42        1684 :     _fe_type(order),
      43        1684 :     _nearest_node(nearest_node),
      44        1684 :     _penetration_info(declareRestartableDataWithContext<std::map<dof_id_type, PenetrationInfo *>>(
      45        1684 :         "penetration_info", &_mesh)),
      46        1684 :     _has_penetrated(declareRestartableData<std::set<dof_id_type>>("has_penetrated")),
      47        1684 :     _check_whether_reasonable(true),
      48        1684 :     _update_location(declareRestartableData<bool>("update_location", true)),
      49        1684 :     _tangential_tolerance(0.0),
      50        1684 :     _do_normal_smoothing(false),
      51        1684 :     _normal_smoothing_distance(0.0),
      52        1684 :     _normal_smoothing_method(NSM_EDGE_BASED),
      53       10104 :     _patch_update_strategy(_mesh.getPatchUpdateStrategy())
      54             : {
      55             :   // Preconstruct an FE object for each thread we're going to use and for each lower-dimensional
      56             :   // element
      57             :   // This is a time savings so that the thread objects don't do this themselves multiple times
      58        1684 :   _fe.resize(libMesh::n_threads());
      59        3512 :   for (unsigned int i = 0; i < libMesh::n_threads(); i++)
      60             :   {
      61        1828 :     unsigned int n_dims = _mesh.dimension();
      62        1828 :     _fe[i].resize(n_dims + 1);
      63        8028 :     for (unsigned int dim = 0; dim <= n_dims; ++dim)
      64             :     {
      65        6200 :       _fe[i][dim] = FEBase::build(dim, _fe_type).release();
      66        6200 :       _fe[i][dim]->get_xyz();
      67        6200 :       _fe[i][dim]->get_phi();
      68        6200 :       _fe[i][dim]->get_dphi();
      69        6200 :       _fe[i][dim]->get_dxyzdxi();
      70        6200 :       _fe[i][dim]->get_d2xyzdxi2();
      71        6200 :       _fe[i][dim]->get_d2xyzdxideta();
      72        6200 :       _fe[i][dim]->get_dxyzdeta();
      73        6200 :       _fe[i][dim]->get_d2xyzdeta2();
      74        6200 :       _fe[i][dim]->get_d2xyzdxideta();
      75             :     }
      76             :   }
      77             : 
      78        1684 :   if (_normal_smoothing_method == NSM_NODAL_NORMAL_BASED)
      79             :   {
      80           0 :     if (!((_subproblem.hasVariable("nodal_normal_x")) &&
      81           0 :           (_subproblem.hasVariable("nodal_normal_y")) &&
      82           0 :           (_subproblem.hasVariable("nodal_normal_z"))))
      83             :     {
      84           0 :       mooseError(
      85             :           "To use nodal-normal-based smoothing, the nodal_normal_x, nodal_normal_y, and "
      86             :           "nodal_normal_z variables must exist.  Are you missing the \\[NodalNormals\\] block?");
      87             :     }
      88             :   }
      89        1684 : }
      90             : 
      91        3352 : PenetrationLocator::~PenetrationLocator()
      92             : {
      93        3494 :   for (unsigned int i = 0; i < libMesh::n_threads(); i++)
      94        7988 :     for (unsigned int dim = 0; dim < _fe[i].size(); dim++)
      95        6170 :       delete _fe[i][dim];
      96             : 
      97       26423 :   for (auto & it : _penetration_info)
      98       24747 :     delete it.second;
      99        3352 : }
     100             : 
     101             : void
     102      170144 : PenetrationLocator::detectPenetration()
     103             : {
     104      170144 :   TIME_SECTION("detectPenetration", 3, "Detecting Penetration");
     105             : 
     106             :   // Get list of boundary (elem, side, id) tuples.
     107             :   std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> bc_tuples =
     108      170144 :       _mesh.buildActiveSideList();
     109             : 
     110             :   // Grab the secondary nodes we need to worry about from the NearestNodeLocator
     111      170144 :   NodeIdRange & secondary_node_range = _nearest_node.secondaryNodeRange();
     112             : 
     113             :   PenetrationThread pt(_subproblem,
     114      170144 :                        _mesh,
     115      170144 :                        _primary_boundary,
     116      170144 :                        _secondary_boundary,
     117             :                        _penetration_info,
     118      170144 :                        _check_whether_reasonable,
     119      170144 :                        _update_location,
     120             :                        _tangential_tolerance,
     121      170144 :                        _do_normal_smoothing,
     122             :                        _normal_smoothing_distance,
     123             :                        _normal_smoothing_method,
     124      170144 :                        _fe,
     125      170144 :                        _fe_type,
     126             :                        _nearest_node,
     127      170144 :                        _mesh.nodeToElemMap(),
     128      170144 :                        bc_tuples);
     129             : 
     130      170144 :   Threads::parallel_reduce(secondary_node_range, pt);
     131             : 
     132      170144 :   std::vector<dof_id_type> recheck_secondary_nodes = pt._recheck_secondary_nodes;
     133             : 
     134             :   // Update the patch for the secondary nodes in recheck_secondary_nodes and re-run penetration
     135             :   // thread on these nodes at every nonlinear iteration if patch update strategy is set to
     136             :   // "iteration".
     137      170144 :   if (recheck_secondary_nodes.size() > 0 && _patch_update_strategy == Moose::Iteration &&
     138           0 :       _subproblem.currentlyComputingJacobian())
     139             :   {
     140             :     // Update the patch for this subset of secondary nodes and calculate the nearest neighbor_nodes
     141           0 :     _nearest_node.updatePatch(recheck_secondary_nodes);
     142             : 
     143             :     // Re-run the penetration thread to see if these nodes are in contact with the updated patch
     144             :     NodeIdRange recheck_secondary_node_range(
     145           0 :         recheck_secondary_nodes.begin(), recheck_secondary_nodes.end(), 1);
     146             : 
     147           0 :     Threads::parallel_reduce(recheck_secondary_node_range, pt);
     148           0 :   }
     149             : 
     150      238371 :   if (recheck_secondary_nodes.size() > 0 && _patch_update_strategy != Moose::Iteration &&
     151       68227 :       _subproblem.currentlyComputingJacobian())
     152       10044 :     mooseDoOnce(mooseWarning("Warning in PenetrationLocator. Penetration is not "
     153             :                              "detected for one or more secondary nodes. This could be because "
     154             :                              "those secondary nodes simply do not project to faces on the primary "
     155             :                              "surface. However, this could also be because contact should be "
     156             :                              "enforced on those nodes, but the faces that they project to "
     157             :                              "are outside the contact patch, which will give an erroneous "
     158             :                              "result. Use appropriate options for 'patch_size' and "
     159             :                              "'patch_update_strategy' in the Mesh block to avoid this issue. "
     160             :                              "Setting 'patch_update_strategy=iteration' is recommended because "
     161             :                              "it completely avoids this potential issue. Also note that this "
     162             :                              "warning is printed only once, so a similar situation could occur "
     163             :                              "multiple times during the simulation but this warning is printed "
     164             :                              "only at the first occurrence."));
     165      170140 : }
     166             : 
     167             : void
     168         110 : PenetrationLocator::reinit()
     169             : {
     170         110 :   TIME_SECTION("reinit", 3, "Reinitializing PenetrationLocator");
     171             : 
     172             :   // Delete the PenetrationInfo objects we own before clearing the
     173             :   // map, or we have a memory leak.
     174         354 :   for (auto & it : _penetration_info)
     175         244 :     delete it.second;
     176             : 
     177         110 :   _penetration_info.clear();
     178             : 
     179         110 :   _has_penetrated.clear();
     180             : 
     181         110 :   detectPenetration();
     182         110 : }
     183             : 
     184             : Real
     185           0 : PenetrationLocator::penetrationDistance(dof_id_type node_id)
     186             : {
     187           0 :   PenetrationInfo * info = _penetration_info[node_id];
     188             : 
     189           0 :   if (info)
     190           0 :     return info->_distance;
     191             :   else
     192           0 :     return 0;
     193             : }
     194             : 
     195             : RealVectorValue
     196           0 : PenetrationLocator::penetrationNormal(dof_id_type node_id)
     197             : {
     198             :   std::map<dof_id_type, PenetrationInfo *>::const_iterator found_it =
     199           0 :       _penetration_info.find(node_id);
     200             : 
     201           0 :   if (found_it != _penetration_info.end())
     202           0 :     return found_it->second->_normal;
     203             :   else
     204           0 :     return RealVectorValue(0, 0, 0);
     205             : }
     206             : 
     207             : void
     208           0 : PenetrationLocator::setCheckWhetherReasonable(bool state)
     209             : {
     210           0 :   _check_whether_reasonable = state;
     211           0 : }
     212             : 
     213             : void
     214           0 : PenetrationLocator::setUpdate(bool update)
     215             : {
     216           0 :   _update_location = update;
     217           0 : }
     218             : 
     219             : void
     220         512 : PenetrationLocator::setTangentialTolerance(Real tangential_tolerance)
     221             : {
     222         512 :   _tangential_tolerance = tangential_tolerance;
     223         512 : }
     224             : 
     225             : void
     226         304 : PenetrationLocator::setNormalSmoothingDistance(Real normal_smoothing_distance)
     227             : {
     228         304 :   _normal_smoothing_distance = normal_smoothing_distance;
     229         304 :   if (_normal_smoothing_distance > 0.0)
     230         304 :     _do_normal_smoothing = true;
     231         304 : }
     232             : 
     233             : void
     234         104 : PenetrationLocator::setNormalSmoothingMethod(std::string nsmString)
     235             : {
     236         104 :   if (nsmString == "edge_based")
     237           0 :     _normal_smoothing_method = NSM_EDGE_BASED;
     238         104 :   else if (nsmString == "nodal_normal_based")
     239         104 :     _normal_smoothing_method = NSM_NODAL_NORMAL_BASED;
     240             :   else
     241           0 :     mooseError("Invalid normal_smoothing_method: ", nsmString);
     242         104 :   _do_normal_smoothing = true;
     243         104 : }

Generated by: LCOV version 1.14