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

Generated by: LCOV version 1.14