LCOV - code coverage report
Current view: top level - src/constraints - ComputeFrictionalForceCartesianLMMechanicalContact.C (source / functions) Hit Total Coverage
Test: idaholab/moose contact: #31653 (13b9bb) with base 7323e9 Lines: 155 159 97.5 %
Date: 2025-11-06 21:22:58 Functions: 9 9 100.0 %
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 "ComputeFrictionalForceCartesianLMMechanicalContact.h"
      11             : #include "MortarContactUtils.h"
      12             : #include "DisplacedProblem.h"
      13             : #include "Assembly.h"
      14             : #include "AutomaticMortarGeneration.h"
      15             : #include "metaphysicl/metaphysicl_version.h"
      16             : #include "metaphysicl/dualsemidynamicsparsenumberarray.h"
      17             : #include "metaphysicl/parallel_dualnumber.h"
      18             : #if METAPHYSICL_MAJOR_VERSION < 2
      19             : #include "metaphysicl/parallel_dynamic_std_array_wrapper.h"
      20             : #else
      21             : #include "metaphysicl/parallel_dynamic_array_wrapper.h"
      22             : #endif
      23             : #include "metaphysicl/parallel_semidynamicsparsenumberarray.h"
      24             : #include "timpi/parallel_sync.h"
      25             : 
      26             : registerMooseObject("ContactApp", ComputeFrictionalForceCartesianLMMechanicalContact);
      27             : 
      28             : namespace
      29             : {
      30             : const InputParameters &
      31          78 : assignVarsInParamsFriction(const InputParameters & params_in)
      32             : {
      33             :   InputParameters & ret = const_cast<InputParameters &>(params_in);
      34          78 :   const auto & disp_x_name = ret.get<std::vector<VariableName>>("disp_x");
      35          78 :   if (disp_x_name.size() != 1)
      36           0 :     mooseError("We require that the disp_x parameter have exactly one coupled name");
      37             : 
      38             :   // We do this so we don't get any variable errors during MortarConstraint(Base) construction
      39         156 :   ret.set<VariableName>("secondary_variable") = disp_x_name[0];
      40          78 :   ret.set<VariableName>("primary_variable") = disp_x_name[0];
      41             : 
      42          78 :   return ret;
      43             : }
      44             : }
      45             : 
      46             : InputParameters
      47         156 : ComputeFrictionalForceCartesianLMMechanicalContact::validParams()
      48             : {
      49         156 :   InputParameters params = ComputeWeightedGapCartesianLMMechanicalContact::validParams();
      50         156 :   params.addClassDescription("Computes mortar frictional forces.");
      51         312 :   params.addParam<Real>("c_t", 1e0, "Numerical parameter for tangential constraints");
      52         312 :   params.addParam<Real>(
      53             :       "epsilon",
      54         312 :       1.0e-7,
      55             :       "Minimum value of contact pressure that will trigger frictional enforcement");
      56         312 :   params.addRangeCheckedParam<Real>(
      57             :       "mu", "mu > 0", "The friction coefficient for the Coulomb friction law");
      58         156 :   return params;
      59           0 : }
      60             : 
      61          78 : ComputeFrictionalForceCartesianLMMechanicalContact::
      62          78 :     ComputeFrictionalForceCartesianLMMechanicalContact(const InputParameters & parameters)
      63             :   : ComputeWeightedGapCartesianLMMechanicalContact(assignVarsInParamsFriction(parameters)),
      64         156 :     _c_t(getParam<Real>("c_t")),
      65          78 :     _secondary_x_dot(adCoupledDot("disp_x")),
      66          78 :     _primary_x_dot(adCoupledNeighborValueDot("disp_x")),
      67          78 :     _secondary_y_dot(adCoupledDot("disp_y")),
      68          78 :     _primary_y_dot(adCoupledNeighborValueDot("disp_y")),
      69          78 :     _secondary_z_dot(_has_disp_z ? &adCoupledDot("disp_z") : nullptr),
      70          78 :     _primary_z_dot(_has_disp_z ? &adCoupledNeighborValueDot("disp_z") : nullptr),
      71         156 :     _mu(getParam<Real>("mu")),
      72         234 :     _epsilon(getParam<Real>("epsilon"))
      73             : {
      74          78 : }
      75             : 
      76             : void
      77      564604 : ComputeFrictionalForceCartesianLMMechanicalContact::computeQpProperties()
      78             : {
      79      564604 :   ComputeWeightedGapCartesianLMMechanicalContact::computeQpProperties();
      80             : 
      81             :   // Trim derivatives
      82             :   const auto & primary_ip_lowerd_map = amg().getPrimaryIpToLowerElementMap(
      83      564604 :       *_lower_primary_elem, *_lower_primary_elem->interior_parent(), *_lower_secondary_elem);
      84             :   const auto & secondary_ip_lowerd_map =
      85      564604 :       amg().getSecondaryIpToLowerElementMap(*_lower_secondary_elem);
      86             : 
      87      564604 :   std::array<const MooseVariable *, 3> var_array{{_disp_x_var, _disp_y_var, _disp_z_var}};
      88             :   std::array<ADReal, 3> primary_disp_dot{
      89      564604 :       {_primary_x_dot[_qp], _primary_y_dot[_qp], _has_disp_z ? (*_primary_z_dot)[_qp] : 0}};
      90             :   std::array<ADReal, 3> secondary_disp_dot{
      91      564604 :       {_secondary_x_dot[_qp], _secondary_y_dot[_qp], _has_disp_z ? (*_secondary_z_dot)[_qp] : 0}};
      92             : 
      93      564604 :   trimInteriorNodeDerivatives(primary_ip_lowerd_map, var_array, primary_disp_dot, false);
      94      564604 :   trimInteriorNodeDerivatives(secondary_ip_lowerd_map, var_array, secondary_disp_dot, true);
      95             : 
      96             :   const ADReal & prim_x_dot = primary_disp_dot[0];
      97             :   const ADReal & prim_y_dot = primary_disp_dot[1];
      98             :   const ADReal * prim_z_dot = nullptr;
      99      564604 :   if (_has_disp_z)
     100             :     prim_z_dot = &primary_disp_dot[2];
     101             : 
     102             :   const ADReal & sec_x_dot = secondary_disp_dot[0];
     103             :   const ADReal & sec_y_dot = secondary_disp_dot[1];
     104             :   const ADReal * sec_z_dot = nullptr;
     105      564604 :   if (_has_disp_z)
     106             :     sec_z_dot = &secondary_disp_dot[2];
     107             : 
     108             :   // Build relative velocity vector
     109             :   ADRealVectorValue relative_velocity;
     110             : 
     111      564604 :   if (_has_disp_z)
     112      169984 :     relative_velocity = {sec_x_dot - prim_x_dot, sec_y_dot - prim_y_dot, *sec_z_dot - *prim_z_dot};
     113             :   else
     114      789240 :     relative_velocity = {sec_x_dot - prim_x_dot, sec_y_dot - prim_y_dot, 0.0};
     115             : 
     116     1129208 :   _qp_tangential_velocity_nodal = relative_velocity * (_JxW_msm[_qp] * _coord[_qp]);
     117      564604 : }
     118             : 
     119             : void
     120     1469176 : ComputeFrictionalForceCartesianLMMechanicalContact::computeQpIProperties()
     121             : {
     122     1469176 :   ComputeWeightedGapCartesianLMMechanicalContact::computeQpIProperties();
     123             : 
     124     1469176 :   const auto & nodal_tangents = amg().getNodalTangents(*_lower_secondary_elem);
     125             :   // Get the _dof_to_weighted_tangential_velocity map
     126             :   const DofObject * const dof =
     127     1469176 :       _lm_vars[0]->isNodal() ? static_cast<const DofObject *>(_lower_secondary_elem->node_ptr(_i))
     128           0 :                              : static_cast<const DofObject *>(_lower_secondary_elem);
     129             : 
     130             :   _dof_to_weighted_tangential_velocity[dof][0] +=
     131     2938352 :       _test[_i][_qp] * _qp_tangential_velocity_nodal * nodal_tangents[0][_i];
     132             : 
     133             :   // Get the _dof_to_weighted_tangential_velocity map for a second direction
     134     1469176 :   if (_has_disp_z)
     135             :     _dof_to_weighted_tangential_velocity[dof][1] +=
     136     1359872 :         _test[_i][_qp] * _qp_tangential_velocity_nodal * nodal_tangents[1][_i];
     137     1469176 : }
     138             : 
     139             : void
     140        6069 : ComputeFrictionalForceCartesianLMMechanicalContact::residualSetup()
     141             : {
     142        6069 :   ComputeWeightedGapCartesianLMMechanicalContact::residualSetup();
     143             :   _dof_to_weighted_tangential_velocity.clear();
     144        6069 : }
     145             : 
     146             : void
     147        4483 : ComputeFrictionalForceCartesianLMMechanicalContact::post()
     148             : {
     149        4483 :   Moose::Mortar::Contact::communicateGaps(
     150        4483 :       _dof_to_weighted_gap, _mesh, _nodal, _normalize_c, _communicator, false);
     151        4483 :   Moose::Mortar::Contact::communicateVelocities(
     152        4483 :       _dof_to_weighted_tangential_velocity, _mesh, _nodal, _communicator, false);
     153             : 
     154             :   // Enforce frictional complementarity constraints
     155       75326 :   for (const auto & pr : _dof_to_weighted_tangential_velocity)
     156             :   {
     157       70843 :     const DofObject * const dof = pr.first;
     158             : 
     159       70843 :     if (dof->processor_id() != this->processor_id())
     160        1064 :       continue;
     161             : 
     162             :     auto & weighted_gap_pr = _dof_to_weighted_gap[dof];
     163       69779 :     _weighted_gap_ptr = &weighted_gap_pr.first;
     164       69779 :     _normalization_ptr = &weighted_gap_pr.second;
     165       69779 :     _tangential_vel_ptr[0] = &(pr.second[0]);
     166             : 
     167       69779 :     if (_has_disp_z)
     168        2656 :       _tangential_vel_ptr[1] = &(pr.second[1]);
     169             : 
     170       69779 :     enforceConstraintOnDof(dof);
     171             :   }
     172        4483 : }
     173             : 
     174             : void
     175        1586 : ComputeFrictionalForceCartesianLMMechanicalContact::incorrectEdgeDroppingPost(
     176             :     const std::unordered_set<const Node *> & inactive_lm_nodes)
     177             : {
     178        1586 :   Moose::Mortar::Contact::communicateGaps(
     179        1586 :       _dof_to_weighted_gap, _mesh, _nodal, _normalize_c, _communicator, false);
     180        1586 :   Moose::Mortar::Contact::communicateVelocities(
     181        1586 :       _dof_to_weighted_tangential_velocity, _mesh, _nodal, _communicator, false);
     182             : 
     183             :   // Enforce frictional complementarity constraints
     184      114074 :   for (const auto & pr : _dof_to_weighted_tangential_velocity)
     185             :   {
     186      112488 :     const DofObject * const dof = pr.first;
     187             : 
     188             :     // If node inactive, skip
     189      112488 :     if ((inactive_lm_nodes.find(static_cast<const Node *>(dof)) != inactive_lm_nodes.end()) ||
     190      110268 :         (dof->processor_id() != this->processor_id()))
     191        2696 :       continue;
     192             : 
     193      109792 :     _weighted_gap_ptr = &_dof_to_weighted_gap[dof].first;
     194      109792 :     _normalization_ptr = &_dof_to_weighted_gap[dof].second;
     195      109792 :     _tangential_vel_ptr[0] = &pr.second[0];
     196             : 
     197      109792 :     if (_has_disp_z)
     198           0 :       _tangential_vel_ptr[1] = &pr.second[1];
     199             : 
     200      109792 :     enforceConstraintOnDof(dof);
     201             :   }
     202        1586 : }
     203             : 
     204             : void
     205      179571 : ComputeFrictionalForceCartesianLMMechanicalContact::enforceConstraintOnDof(
     206             :     const DofObject * const dof)
     207             : {
     208             :   using std::abs, std::sqrt, std::max, std::min;
     209             : 
     210      179571 :   const auto & weighted_gap = *_weighted_gap_ptr;
     211      179571 :   const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
     212      179571 :   const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
     213             : 
     214      179571 :   const auto dof_index_x = dof->dof_number(_sys.number(), _lm_vars[0]->number(), 0);
     215      179571 :   const auto dof_index_y = dof->dof_number(_sys.number(), _lm_vars[1]->number(), 0);
     216      179571 :   const Real scaling_factor_x = _lm_vars[0]->scalingFactor();
     217      179571 :   const Real scaling_factor_y = _lm_vars[1]->scalingFactor();
     218             :   Real scaling_factor_z = 1;
     219             : 
     220      179571 :   ADReal lm_x = (*_sys.currentSolution())(dof_index_x);
     221      179571 :   ADReal lm_y = (*_sys.currentSolution())(dof_index_y);
     222             : 
     223             :   Moose::derivInsert(lm_x.derivatives(), dof_index_x, 1.);
     224             :   Moose::derivInsert(lm_y.derivatives(), dof_index_y, 1.);
     225             : 
     226             :   dof_id_type dof_index_z(-1);
     227             :   ADReal lm_z;
     228      179571 :   if (_has_disp_z)
     229             :   {
     230        2656 :     dof_index_z = dof->dof_number(_sys.number(), _lm_vars[2]->number(), 0);
     231        2656 :     lm_z = (*_sys.currentSolution())(dof_index_z);
     232             :     Moose::derivInsert(lm_z.derivatives(), dof_index_z, 1.);
     233        2656 :     scaling_factor_z = _lm_vars[2]->scalingFactor();
     234             :   }
     235             : 
     236             :   ADReal normal_pressure_value =
     237      179571 :       lm_x * _dof_to_normal_vector[dof](0) + lm_y * _dof_to_normal_vector[dof](1);
     238             :   ADReal tangential_pressure_value =
     239      179571 :       lm_x * _dof_to_tangent_vectors[dof][0](0) + lm_y * _dof_to_tangent_vectors[dof][0](1);
     240             : 
     241             :   ADReal tangential_pressure_value_dir;
     242             : 
     243      179571 :   if (_has_disp_z)
     244             :   {
     245        2656 :     normal_pressure_value += lm_z * _dof_to_normal_vector[dof](2);
     246        2656 :     tangential_pressure_value += lm_z * _dof_to_tangent_vectors[dof][0](2);
     247        2656 :     tangential_pressure_value_dir = lm_x * _dof_to_tangent_vectors[dof][1](0) +
     248        2656 :                                     lm_y * _dof_to_tangent_vectors[dof][1](1) +
     249        2656 :                                     lm_z * _dof_to_tangent_vectors[dof][1](2);
     250             :   }
     251             : 
     252      179571 :   ADReal normal_dof_residual = min(normal_pressure_value, weighted_gap * c);
     253             :   ADReal tangential_dof_residual;
     254             :   ADReal tangential_dof_residual_dir;
     255             : 
     256      179571 :   const ADReal & tangential_vel = *_tangential_vel_ptr[0];
     257             : 
     258             :   // Primal-dual active set strategy (PDASS)
     259      179571 :   if (!_has_disp_z)
     260             :   {
     261      176915 :     if (normal_pressure_value.value() < _epsilon)
     262      120699 :       tangential_dof_residual = tangential_pressure_value;
     263             :     else
     264             :     {
     265      112432 :       const auto term_1 = max(_mu * (normal_pressure_value + c * weighted_gap),
     266      112432 :                               abs(tangential_pressure_value + c_t * tangential_vel * _dt)) *
     267             :                           tangential_pressure_value;
     268       56216 :       const auto term_2 = _mu * max(0.0, normal_pressure_value + c * weighted_gap) *
     269      112432 :                           (tangential_pressure_value + c_t * tangential_vel * _dt);
     270             : 
     271       56216 :       tangential_dof_residual = term_1 - term_2;
     272             :     }
     273             :   }
     274             :   else
     275             :   {
     276        2656 :     if (normal_pressure_value.value() < _epsilon)
     277             :     {
     278         500 :       tangential_dof_residual = tangential_pressure_value;
     279         500 :       tangential_dof_residual_dir = tangential_pressure_value_dir;
     280             :     }
     281             :     else
     282             :     {
     283             :       const Real epsilon_sqrt = 1.0e-48;
     284             : 
     285        2156 :       const auto lamdba_plus_cg = normal_pressure_value + c * weighted_gap;
     286             : 
     287             :       std::array<ADReal, 2> lambda_t_plus_ctu;
     288        4312 :       lambda_t_plus_ctu[0] = tangential_pressure_value + c_t * (*_tangential_vel_ptr[0]) * _dt;
     289        4312 :       lambda_t_plus_ctu[1] = tangential_pressure_value_dir + c_t * (*_tangential_vel_ptr[1]) * _dt;
     290             : 
     291        4312 :       const auto term_1_x = max(_mu * lamdba_plus_cg,
     292        2156 :                                 sqrt(lambda_t_plus_ctu[0] * lambda_t_plus_ctu[0] +
     293        2156 :                                      lambda_t_plus_ctu[1] * lambda_t_plus_ctu[1] + epsilon_sqrt)) *
     294             :                             tangential_pressure_value;
     295             : 
     296        2156 :       const auto term_1_y = max(_mu * lamdba_plus_cg,
     297        2156 :                                 sqrt(lambda_t_plus_ctu[0] * lambda_t_plus_ctu[0] +
     298        2156 :                                      lambda_t_plus_ctu[1] * lambda_t_plus_ctu[1] + epsilon_sqrt)) *
     299             :                             tangential_pressure_value_dir;
     300             : 
     301        2156 :       const auto term_2_x = _mu * max(0.0, lamdba_plus_cg) * lambda_t_plus_ctu[0];
     302             : 
     303        4312 :       const auto term_2_y = _mu * max(0.0, lamdba_plus_cg) * lambda_t_plus_ctu[1];
     304             : 
     305        2156 :       tangential_dof_residual = term_1_x - term_2_x;
     306        2156 :       tangential_dof_residual_dir = term_1_y - term_2_y;
     307             :     }
     308             :   }
     309             : 
     310             :   // Compute the friction coefficient (constant or function)
     311             : 
     312             :   // Get index for normal constraint.
     313             :   // We do this to get a decent Jacobian structure, which is key for the use of iterative solvers.
     314             :   // Using old normal vector to avoid changes in the Jacobian structure within one time step
     315             : 
     316             :   Real ny, nz;
     317             :   // Intially, use the current normal vector
     318      179571 :   if (_dof_to_old_normal_vector[dof].norm() < TOLERANCE)
     319             :   {
     320       21790 :     ny = _dof_to_normal_vector[dof](1);
     321       21790 :     nz = _dof_to_normal_vector[dof](2);
     322             :   }
     323             :   else
     324             :   {
     325      157781 :     ny = _dof_to_old_normal_vector[dof](1);
     326      157781 :     nz = _dof_to_old_normal_vector[dof](2);
     327             :   }
     328             : 
     329             :   unsigned int component_normal = 0;
     330             : 
     331             :   // Consider constraint orientation to improve Jacobian structure
     332      179571 :   const Real threshold_for_Jacobian = _has_disp_z ? 1.0 / sqrt(3.0) : 1.0 / sqrt(2.0);
     333             : 
     334      179571 :   if (abs(ny) > threshold_for_Jacobian)
     335             :     component_normal = 1;
     336       71901 :   else if (abs(nz) > threshold_for_Jacobian)
     337             :     component_normal = 2;
     338             : 
     339      359142 :   addResidualsAndJacobian(
     340             :       _assembly,
     341      359142 :       std::array<ADReal, 1>{{normal_dof_residual}},
     342      179571 :       std::array<dof_id_type, 1>{{component_normal == 0
     343      110326 :                                       ? dof_index_x
     344             :                                       : (component_normal == 1 ? dof_index_y : dof_index_z)}},
     345             :       component_normal == 0 ? scaling_factor_x
     346             :                             : (component_normal == 1 ? scaling_factor_y : scaling_factor_z));
     347             : 
     348      179571 :   addResidualsAndJacobian(
     349             :       _assembly,
     350      359142 :       std::array<ADReal, 1>{{tangential_dof_residual}},
     351      179571 :       std::array<dof_id_type, 1>{
     352      179571 :           {(component_normal == 0 || component_normal == 2) ? dof_index_y : dof_index_x}},
     353      179571 :       (component_normal == 0 || component_normal == 2) ? scaling_factor_y : scaling_factor_x);
     354             : 
     355      179571 :   if (_has_disp_z)
     356        5312 :     addResidualsAndJacobian(
     357             :         _assembly,
     358        5312 :         std::array<ADReal, 1>{{tangential_dof_residual_dir}},
     359        5312 :         std::array<dof_id_type, 1>{
     360             :             {(component_normal == 0 || component_normal == 1) ? dof_index_z : dof_index_x}},
     361             :         (component_normal == 0 || component_normal == 1) ? scaling_factor_z : scaling_factor_x);
     362      179571 : }

Generated by: LCOV version 1.14