LCOV - code coverage report
Current view: top level - src/constraints - ComputeWeightedGapLMMechanicalContact.C (source / functions) Hit Total Coverage
Test: idaholab/moose contact: 8601ad Lines: 80 91 87.9 %
Date: 2025-07-18 13:27:36 Functions: 10 13 76.9 %
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 "ComputeWeightedGapLMMechanicalContact.h"
      11             : #include "DisplacedProblem.h"
      12             : #include "Assembly.h"
      13             : #include "MortarContactUtils.h"
      14             : #include "WeightedGapUserObject.h"
      15             : #include "metaphysicl/dualsemidynamicsparsenumberarray.h"
      16             : #include "metaphysicl/parallel_dualnumber.h"
      17             : #include "metaphysicl/parallel_dynamic_std_array_wrapper.h"
      18             : #include "metaphysicl/parallel_semidynamicsparsenumberarray.h"
      19             : #include "timpi/parallel_sync.h"
      20             : 
      21             : registerMooseObject("ContactApp", ComputeWeightedGapLMMechanicalContact);
      22             : 
      23             : namespace
      24             : {
      25             : const InputParameters &
      26         625 : assignVarsInParams(const InputParameters & params_in)
      27             : {
      28             :   InputParameters & ret = const_cast<InputParameters &>(params_in);
      29         625 :   const auto & disp_x_name = ret.get<std::vector<VariableName>>("disp_x");
      30         625 :   if (disp_x_name.size() != 1)
      31           0 :     mooseError("We require that the disp_x parameter have exactly one coupled name");
      32             : 
      33             :   // We do this so we don't get any variable errors during MortarConstraint(Base) construction
      34        1250 :   ret.set<VariableName>("secondary_variable") = disp_x_name[0];
      35         625 :   ret.set<VariableName>("primary_variable") = disp_x_name[0];
      36             : 
      37         625 :   return ret;
      38             : }
      39             : }
      40             : 
      41             : InputParameters
      42        1250 : ComputeWeightedGapLMMechanicalContact::validParams()
      43             : {
      44        1250 :   InputParameters params = ADMortarConstraint::validParams();
      45        1250 :   params.addClassDescription("Computes the weighted gap that will later be used to enforce the "
      46             :                              "zero-penetration mechanical contact conditions");
      47        1250 :   params.suppressParameter<VariableName>("secondary_variable");
      48        1250 :   params.suppressParameter<VariableName>("primary_variable");
      49        2500 :   params.addRequiredCoupledVar("disp_x", "The x displacement variable");
      50        2500 :   params.addRequiredCoupledVar("disp_y", "The y displacement variable");
      51        2500 :   params.addCoupledVar("disp_z", "The z displacement variable");
      52        2500 :   params.addParam<Real>(
      53        2500 :       "c", 1e6, "Parameter for balancing the size of the gap and contact pressure");
      54        2500 :   params.addParam<bool>(
      55             :       "normalize_c",
      56        2500 :       false,
      57             :       "Whether to normalize c by weighting function norm. When unnormalized "
      58             :       "the value of c effectively depends on element size since in the constraint we compare nodal "
      59             :       "Lagrange Multiplier values to integrated gap values (LM nodal value is independent of "
      60             :       "element size, where integrated values are dependent on element size).");
      61        1250 :   params.set<bool>("use_displaced_mesh") = true;
      62        1250 :   params.set<bool>("interpolate_normals") = false;
      63        2500 :   params.addRequiredParam<UserObjectName>("weighted_gap_uo", "The weighted gap user object");
      64        1250 :   return params;
      65           0 : }
      66             : 
      67         625 : ComputeWeightedGapLMMechanicalContact::ComputeWeightedGapLMMechanicalContact(
      68         625 :     const InputParameters & parameters)
      69             :   : ADMortarConstraint(assignVarsInParams(parameters)),
      70         625 :     _secondary_disp_x(adCoupledValue("disp_x")),
      71         625 :     _primary_disp_x(adCoupledNeighborValue("disp_x")),
      72         625 :     _secondary_disp_y(adCoupledValue("disp_y")),
      73         625 :     _primary_disp_y(adCoupledNeighborValue("disp_y")),
      74         625 :     _has_disp_z(isCoupled("disp_z")),
      75         625 :     _secondary_disp_z(_has_disp_z ? &adCoupledValue("disp_z") : nullptr),
      76         625 :     _primary_disp_z(_has_disp_z ? &adCoupledNeighborValue("disp_z") : nullptr),
      77        1250 :     _c(getParam<Real>("c")),
      78        1250 :     _normalize_c(getParam<bool>("normalize_c")),
      79         625 :     _nodal(getVar("disp_x", 0)->feType().family == LAGRANGE),
      80         625 :     _disp_x_var(getVar("disp_x", 0)),
      81         625 :     _disp_y_var(getVar("disp_y", 0)),
      82         767 :     _disp_z_var(_has_disp_z ? getVar("disp_z", 0) : nullptr),
      83        1250 :     _weighted_gap_uo(getUserObject<WeightedGapUserObject>("weighted_gap_uo"))
      84             : {
      85        1250 :   if (!getParam<bool>("use_displaced_mesh"))
      86           0 :     paramError(
      87             :         "use_displaced_mesh",
      88             :         "'use_displaced_mesh' must be true for the ComputeWeightedGapLMMechanicalContact object");
      89             : 
      90         625 :   if (!_var->isNodal())
      91           0 :     if (_var->feType().order != static_cast<Order>(0))
      92           0 :       mooseError("Normal contact constraints only support elemental variables of CONSTANT order");
      93         625 : }
      94             : 
      95           0 : ADReal ComputeWeightedGapLMMechanicalContact::computeQpResidual(Moose::MortarType)
      96             : {
      97           0 :   mooseError("We should never call computeQpResidual for ComputeWeightedGapLMMechanicalContact");
      98             : }
      99             : 
     100             : void
     101           0 : ComputeWeightedGapLMMechanicalContact::computeQpProperties()
     102             : {
     103           0 : }
     104             : 
     105             : void
     106           0 : ComputeWeightedGapLMMechanicalContact::computeQpIProperties()
     107             : {
     108           0 : }
     109             : 
     110             : void
     111       38015 : ComputeWeightedGapLMMechanicalContact::residualSetup()
     112             : {
     113       38015 : }
     114             : 
     115             : void
     116       22517 : ComputeWeightedGapLMMechanicalContact::jacobianSetup()
     117             : {
     118       22517 : }
     119             : 
     120             : void
     121     4358560 : ComputeWeightedGapLMMechanicalContact::computeResidual(const Moose::MortarType /*mortar_type*/)
     122             : {
     123     4358560 : }
     124             : 
     125             : void
     126      764598 : ComputeWeightedGapLMMechanicalContact::computeJacobian(const Moose::MortarType mortar_type)
     127             : {
     128             :   // During "computeResidual" and "computeJacobian" we are actually just computing properties on the
     129             :   // mortar segment element mesh. We are *not* actually assembling into the residual/Jacobian. For
     130             :   // the zero-penetration constraint, the property of interest is the map from node to weighted gap.
     131             :   // Computation of the properties proceeds identically for residual and Jacobian evaluation hence
     132             :   // why we simply call computeResidual here. We will assemble into the residual/Jacobian later from
     133             :   // the post() method
     134      764598 :   computeResidual(mortar_type);
     135      764598 : }
     136             : 
     137             : void
     138       19714 : ComputeWeightedGapLMMechanicalContact::post()
     139             : {
     140             :   parallel_object_only();
     141             : 
     142       19714 :   const auto & dof_to_weighted_gap = _weighted_gap_uo.dofToWeightedGap();
     143             : 
     144       88114 :   for (const auto & [dof_object, weighted_gap_pr] : dof_to_weighted_gap)
     145             :   {
     146       68400 :     if (dof_object->processor_id() != this->processor_id())
     147         741 :       continue;
     148             : 
     149             :     const auto & [weighted_gap, normalization] = weighted_gap_pr;
     150       67659 :     _weighted_gap_ptr = &weighted_gap;
     151       67659 :     _normalization_ptr = &normalization;
     152             : 
     153       67659 :     enforceConstraintOnDof(dof_object);
     154             :   }
     155       19714 : }
     156             : 
     157             : void
     158       26113 : ComputeWeightedGapLMMechanicalContact::incorrectEdgeDroppingPost(
     159             :     const std::unordered_set<const Node *> & inactive_lm_nodes)
     160             : {
     161       26113 :   const auto & dof_to_weighted_gap = _weighted_gap_uo.dofToWeightedGap();
     162             : 
     163      137328 :   for (const auto & [dof_object, weighted_gap_pr] : dof_to_weighted_gap)
     164             :   {
     165      218074 :     if ((inactive_lm_nodes.find(static_cast<const Node *>(dof_object)) !=
     166      111215 :          inactive_lm_nodes.end()) ||
     167             :         (dof_object->processor_id() != this->processor_id()))
     168        4356 :       continue;
     169             : 
     170             :     const auto & [weighted_gap, normalization] = weighted_gap_pr;
     171      106859 :     _weighted_gap_ptr = &weighted_gap;
     172      106859 :     _normalization_ptr = &normalization;
     173             : 
     174      106859 :     enforceConstraintOnDof(dof_object);
     175             :   }
     176       26113 : }
     177             : 
     178             : void
     179      404897 : ComputeWeightedGapLMMechanicalContact::enforceConstraintOnDof(const DofObject * const dof)
     180             : {
     181      404897 :   const auto & weighted_gap = *_weighted_gap_ptr;
     182      404897 :   const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
     183             : 
     184      404897 :   const auto dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
     185      404897 :   ADReal lm_value = (*_sys.currentSolution())(dof_index);
     186             :   Moose::derivInsert(lm_value.derivatives(), dof_index, 1.);
     187             : 
     188      404897 :   const ADReal dof_residual = std::min(lm_value, weighted_gap * c);
     189             : 
     190      404897 :   addResidualsAndJacobian(_assembly,
     191      404897 :                           std::array<ADReal, 1>{{dof_residual}},
     192      809794 :                           std::array<dof_id_type, 1>{{dof_index}},
     193      404897 :                           _var->scalingFactor());
     194      404897 : }

Generated by: LCOV version 1.14