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

Generated by: LCOV version 1.14