LCOV - code coverage report
Current view: top level - src/constraints - ComputeFrictionalForceLMMechanicalContact.C (source / functions) Hit Total Coverage
Test: idaholab/moose contact: #33358 (34476d) with base 4fcf7b Lines: 149 162 92.0 %
Date: 2026-07-17 19:51:21 Functions: 9 11 81.8 %
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 "ComputeFrictionalForceLMMechanicalContact.h"
      11             : #include "DisplacedProblem.h"
      12             : #include "Assembly.h"
      13             : #include "MortarContactUtils.h"
      14             : #include "ContactFrictionUtils.h"
      15             : #include "WeightedVelocitiesUserObject.h"
      16             : 
      17             : #include "metaphysicl/metaphysicl_version.h"
      18             : #include "metaphysicl/dualsemidynamicsparsenumberarray.h"
      19             : #include "metaphysicl/parallel_dualnumber.h"
      20             : #if METAPHYSICL_MAJOR_VERSION < 2
      21             : #include "metaphysicl/parallel_dynamic_std_array_wrapper.h"
      22             : #else
      23             : #include "metaphysicl/parallel_dynamic_array_wrapper.h"
      24             : #endif
      25             : #include "metaphysicl/parallel_semidynamicsparsenumberarray.h"
      26             : #include "timpi/parallel_sync.h"
      27             : 
      28             : #include <cmath>
      29             : 
      30             : registerMooseObject("ContactApp", ComputeFrictionalForceLMMechanicalContact);
      31             : 
      32             : InputParameters
      33         354 : ComputeFrictionalForceLMMechanicalContact::validParams()
      34             : {
      35         354 :   InputParameters params = ComputeWeightedGapLMMechanicalContact::validParams();
      36         354 :   params.addClassDescription("Computes the tangential frictional forces");
      37         708 :   params.addRequiredCoupledVar("friction_lm", "The frictional Lagrange's multiplier");
      38         708 :   params.addCoupledVar("friction_lm_dir",
      39             :                        "The frictional Lagrange's multiplier for an addtional direction.");
      40         708 :   params.addParam<FunctionName>(
      41             :       "function_friction",
      42             :       "Coupled function to evaluate friction with values from contact pressure and relative "
      43             :       "tangential velocities");
      44         708 :   params.addParam<Real>("c_t", 1e0, "Numerical parameter for tangential constraints");
      45        1062 :   params.addRangeCheckedParam<Real>(
      46             :       "epsilon",
      47         708 :       1.0e-7,
      48             :       "epsilon > 0",
      49             :       "Minimum value of contact pressure that will trigger frictional enforcement");
      50         708 :   params.addRangeCheckedParam<Real>(
      51             :       "mu", "mu >= 0", "The friction coefficient for the Coulomb friction law");
      52         708 :   params.addParam<MooseEnum>("friction_coefficient_regularization",
      53         708 :                              Moose::Contact::frictionCoefficientRegularizationOptions(),
      54             :                              "The regularization applied to the Coulomb friction coefficient.");
      55        1062 :   params.addRangeCheckedParam<Real>(
      56             :       "friction_reference_slip",
      57         708 :       0.0,
      58             :       "friction_reference_slip >= 0",
      59             :       "Reference slip increment used by friction coefficient regularization.");
      60        1062 :   params.addRangeCheckedParam<Real>(
      61             :       "friction_elastic_slip",
      62         708 :       0.0,
      63             :       "friction_elastic_slip >= 0",
      64             :       "Tangential elastic slip distance over which the Coulomb friction bound is reached.");
      65         708 :   params.addRequiredParam<UserObjectName>("weighted_velocities_uo",
      66             :                                           "The weighted tangential velocities user object.");
      67             : 
      68         354 :   return params;
      69           0 : }
      70             : 
      71         177 : ComputeFrictionalForceLMMechanicalContact::ComputeFrictionalForceLMMechanicalContact(
      72         177 :     const InputParameters & parameters)
      73             :   : ComputeWeightedGapLMMechanicalContact(parameters),
      74         177 :     _weighted_velocities_uo(getUserObject<WeightedVelocitiesUserObject>("weighted_velocities_uo")),
      75         354 :     _c_t(getParam<Real>("c_t")),
      76         177 :     _secondary_x_dot(_secondary_var.adUDot()),
      77         177 :     _primary_x_dot(_primary_var.adUDotNeighbor()),
      78         177 :     _secondary_y_dot(adCoupledDot("disp_y")),
      79         177 :     _primary_y_dot(adCoupledNeighborValueDot("disp_y")),
      80         177 :     _secondary_z_dot(_has_disp_z ? &adCoupledDot("disp_z") : nullptr),
      81         177 :     _primary_z_dot(_has_disp_z ? &adCoupledNeighborValueDot("disp_z") : nullptr),
      82         354 :     _epsilon(getParam<Real>("epsilon")),
      83         524 :     _mu(isParamValid("function_friction") ? std::numeric_limits<double>::quiet_NaN()
      84         517 :                                           : getParam<Real>("mu")),
      85         177 :     _friction_coefficient_regularization(
      86         177 :         getParam<MooseEnum>("friction_coefficient_regularization")
      87             :             .getEnum<Moose::Contact::FrictionCoefficientRegularization>()),
      88         354 :     _friction_reference_slip(getParam<Real>("friction_reference_slip")),
      89         354 :     _friction_elastic_slip(getParam<Real>("friction_elastic_slip")),
      90         361 :     _function_friction(isParamValid("function_friction") ? &getFunction("function_friction")
      91             :                                                          : nullptr),
      92         354 :     _has_friction_function(isParamValid("function_friction")),
      93         177 :     _3d(_has_disp_z)
      94             : 
      95             : {
      96         354 :   if (parameters.isParamSetByUser("mu") && _has_friction_function)
      97           0 :     paramError(
      98             :         "mu",
      99             :         "Please only provide friction either as a function or as a constant value, but not both.");
     100         354 :   else if (!parameters.isParamSetByUser("mu") && !_has_friction_function)
     101           0 :     paramError("mu", "Please provide a value or a function for the coefficient of friction.");
     102             : 
     103         354 :   if (!getParam<bool>("use_displaced_mesh"))
     104           0 :     paramError("use_displaced_mesh",
     105             :                "'use_displaced_mesh' must be true for the "
     106             :                "ComputeFrictionalForceLMMechanicalContact object");
     107             : 
     108         318 :   if (_3d && !isParamValid("friction_lm_dir"))
     109           0 :     paramError("friction_lm_dir",
     110             :                "Three-dimensional mortar frictional contact simulations require an additional "
     111             :                "frictional Lagrange's multiplier to enforce a second tangential pressure");
     112             : 
     113         177 :   if (_friction_coefficient_regularization !=
     114          16 :           Moose::Contact::FrictionCoefficientRegularization::NONE &&
     115          16 :       _friction_reference_slip <= 0.0)
     116           2 :     paramError("friction_reference_slip",
     117             :                "A positive friction_reference_slip is required when "
     118             :                "friction_coefficient_regularization is not NONE.");
     119             : 
     120         175 :   _friction_vars.push_back(getVar("friction_lm", 0));
     121             : 
     122         175 :   if (_3d)
     123          94 :     _friction_vars.push_back(getVar("friction_lm_dir", 0));
     124             : 
     125         175 :   if (!_friction_vars[0]->isNodal())
     126           0 :     if (_friction_vars[0]->feType().order != static_cast<Order>(0))
     127           0 :       paramError(
     128             :           "friction_lm",
     129             :           "Frictional contact constraints only support elemental variables of CONSTANT order");
     130         175 : }
     131             : 
     132             : void
     133           0 : ComputeFrictionalForceLMMechanicalContact::computeQpProperties()
     134             : {
     135           0 : }
     136             : 
     137             : void
     138           0 : ComputeFrictionalForceLMMechanicalContact::computeQpIProperties()
     139             : {
     140           0 : }
     141             : 
     142             : void
     143       42077 : ComputeFrictionalForceLMMechanicalContact::residualSetup()
     144             : {
     145       42077 : }
     146             : 
     147             : void
     148       20000 : ComputeFrictionalForceLMMechanicalContact::post()
     149             : {
     150             :   const auto & dof_to_weighted_tangential_velocity =
     151       20000 :       _weighted_velocities_uo.dofToWeightedVelocities();
     152             : 
     153             :   const std::unordered_map<const DofObject *, std::pair<ADReal, Real>> & dof_to_weighted_gap =
     154       20000 :       _weighted_gap_uo.dofToWeightedGap();
     155             : 
     156             :   // Enforce frictional constraints
     157             : 
     158       93878 :   for (const auto & [dof_object, weighted_velocities_pr] : dof_to_weighted_tangential_velocity)
     159             :   {
     160       73878 :     if (dof_object->processor_id() != this->processor_id())
     161        1557 :       continue;
     162             : 
     163             :     const auto & [weighted_gap_pr, normalization] =
     164       72321 :         libmesh_map_find(dof_to_weighted_gap, dof_object);
     165       72321 :     _weighted_gap_ptr = &weighted_gap_pr;
     166       72321 :     _normalization_ptr = &normalization;
     167       72321 :     _tangential_vel_ptr[0] = &(weighted_velocities_pr[0]);
     168             : 
     169       72321 :     if (_3d)
     170             :     {
     171           0 :       _tangential_vel_ptr[1] = &(weighted_velocities_pr[1]);
     172           0 :       enforceConstraintOnDof3d(dof_object);
     173             :     }
     174             :     else
     175       72321 :       enforceConstraintOnDof(dof_object);
     176             :   }
     177       20000 : }
     178             : 
     179             : void
     180       31824 : ComputeFrictionalForceLMMechanicalContact::incorrectEdgeDroppingPost(
     181             :     const std::unordered_set<const Node *> & inactive_lm_nodes)
     182             : {
     183             :   const auto & dof_to_weighted_tangential_velocity =
     184       31824 :       _weighted_velocities_uo.dofToWeightedVelocities();
     185       31824 :   const auto & dof_to_weighted_gap = _weighted_gap_uo.dofToWeightedGap();
     186             :   // Enforce frictional complementarity constraints
     187      151909 :   for (const auto & [dof_object, weighted_velocities_pr] : dof_to_weighted_tangential_velocity)
     188             :   {
     189             :     // If node inactive, skip
     190      237950 :     if ((inactive_lm_nodes.find(static_cast<const Node *>(dof_object)) !=
     191      120085 :          inactive_lm_nodes.end()) ||
     192      120069 :         (dof_object->processor_id() != this->processor_id()))
     193        2220 :       continue;
     194             : 
     195      117865 :     _weighted_gap_ptr = &dof_to_weighted_gap.at(dof_object).first;
     196      117865 :     _normalization_ptr = &dof_to_weighted_gap.at(dof_object).second;
     197      117865 :     _tangential_vel_ptr[0] = &weighted_velocities_pr[0];
     198             : 
     199      117865 :     if (_3d)
     200             :     {
     201       39890 :       _tangential_vel_ptr[1] = &weighted_velocities_pr[1];
     202       39890 :       enforceConstraintOnDof3d(dof_object);
     203             :     }
     204             :     else
     205       77975 :       enforceConstraintOnDof(dof_object);
     206             :   }
     207       31824 : }
     208             : 
     209             : void
     210       39890 : ComputeFrictionalForceLMMechanicalContact::enforceConstraintOnDof3d(const DofObject * const dof)
     211             : {
     212             :   using std::max, std::sqrt;
     213             : 
     214       39890 :   ComputeWeightedGapLMMechanicalContact::enforceConstraintOnDof(dof);
     215             : 
     216             :   // Get normal LM
     217       39890 :   const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
     218       39890 :   const ADReal & weighted_gap = *_weighted_gap_ptr;
     219       39890 :   ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
     220             :   Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
     221             : 
     222             :   // Get friction LMs
     223             :   std::array<const ADReal *, 2> & tangential_vel = _tangential_vel_ptr;
     224             :   std::array<dof_id_type, 2> friction_dof_indices;
     225             :   std::array<ADReal, 2> friction_lm_values;
     226             : 
     227             :   const unsigned int num_tangents = 2;
     228      119670 :   for (const auto i : make_range(num_tangents))
     229             :   {
     230       79780 :     friction_dof_indices[i] = dof->dof_number(_sys.number(), _friction_vars[i]->number(), 0);
     231       79780 :     friction_lm_values[i] = (*_sys.currentSolution())(friction_dof_indices[i]);
     232             :     Moose::derivInsert(friction_lm_values[i].derivatives(), friction_dof_indices[i], 1.);
     233             :   }
     234             : 
     235             :   // Get normalized c and c_t values (if normalization specified
     236       39890 :   const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
     237       39890 :   const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
     238             : 
     239             :   // Compute the friction coefficient (constant or function)
     240             :   const auto & real_tangential_velocity =
     241       39890 :       libmesh_map_find(_weighted_velocities_uo.dofToRealVelocities(), dof);
     242             :   ADReal mu_ad = computeFrictionValue(
     243       39890 :       contact_pressure, real_tangential_velocity[0], real_tangential_velocity[1]);
     244             : 
     245             :   ADReal dof_residual;
     246             :   ADReal dof_residual_dir;
     247             : 
     248             :   // Primal-dual active set strategy (PDASS)
     249       39890 :   if (contact_pressure < _epsilon)
     250             :   {
     251       11818 :     dof_residual = friction_lm_values[0];
     252       11818 :     dof_residual_dir = friction_lm_values[1];
     253             :   }
     254             :   else
     255             :   {
     256             :     // Epsilon to avoid automatic differentiation singularity
     257             :     const Real epsilon_sqrt = 1.0e-48;
     258             : 
     259       28072 :     const auto lamdba_plus_cg = contact_pressure + c * weighted_gap;
     260       28072 :     const auto normal_bound = max(0.0, lamdba_plus_cg);
     261             :     const auto friction_bound = mu_ad * normal_bound;
     262             :     const auto tangential_compliance =
     263       28072 :         _friction_elastic_slip > 0.0 ? _friction_elastic_slip / (friction_bound + _epsilon) : 0.0;
     264             : 
     265             :     std::array<ADReal, 2> lambda_t_plus_ctu;
     266             :     lambda_t_plus_ctu[0] =
     267       28072 :         friction_lm_values[0] +
     268       56144 :         c_t * (*tangential_vel[0] * _dt - tangential_compliance * friction_lm_values[0]);
     269             :     lambda_t_plus_ctu[1] =
     270       28072 :         friction_lm_values[1] +
     271       56144 :         c_t * (*tangential_vel[1] * _dt - tangential_compliance * friction_lm_values[1]);
     272             : 
     273             :     const auto tangential_trial_norm =
     274       28072 :         sqrt(lambda_t_plus_ctu[0] * lambda_t_plus_ctu[0] +
     275       28072 :              lambda_t_plus_ctu[1] * lambda_t_plus_ctu[1] + epsilon_sqrt);
     276             : 
     277       28072 :     const auto term_1_x = max(friction_bound, tangential_trial_norm) * friction_lm_values[0];
     278             : 
     279       56144 :     const auto term_1_y = max(friction_bound, tangential_trial_norm) * friction_lm_values[1];
     280             : 
     281             :     const auto term_2_x = friction_bound * lambda_t_plus_ctu[0];
     282             : 
     283             :     const auto term_2_y = friction_bound * lambda_t_plus_ctu[1];
     284             : 
     285       28072 :     dof_residual = term_1_x - term_2_x;
     286       28072 :     dof_residual_dir = term_1_y - term_2_y;
     287             :   }
     288             : 
     289       39890 :   addResidualsAndJacobian(_assembly,
     290       79780 :                           std::array<ADReal, 1>{{dof_residual}},
     291       79780 :                           std::array<dof_id_type, 1>{{friction_dof_indices[0]}},
     292             :                           _friction_vars[0]->scalingFactor());
     293       39890 :   addResidualsAndJacobian(_assembly,
     294       79780 :                           std::array<ADReal, 1>{{dof_residual_dir}},
     295       79780 :                           std::array<dof_id_type, 1>{{friction_dof_indices[1]}},
     296             :                           _friction_vars[1]->scalingFactor());
     297       39890 : }
     298             : 
     299             : void
     300      150296 : ComputeFrictionalForceLMMechanicalContact::enforceConstraintOnDof(const DofObject * const dof)
     301             : {
     302             :   using std::abs, std::max;
     303             : 
     304      150296 :   ComputeWeightedGapLMMechanicalContact::enforceConstraintOnDof(dof);
     305             : 
     306             :   // Get friction LM
     307      150296 :   const auto friction_dof_index = dof->dof_number(_sys.number(), _friction_vars[0]->number(), 0);
     308      150296 :   const ADReal & tangential_vel = *_tangential_vel_ptr[0];
     309      150296 :   ADReal friction_lm_value = (*_sys.currentSolution())(friction_dof_index);
     310             :   Moose::derivInsert(friction_lm_value.derivatives(), friction_dof_index, 1.);
     311             : 
     312             :   // Get normal LM
     313      150296 :   const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
     314      150296 :   const ADReal & weighted_gap = *_weighted_gap_ptr;
     315      150296 :   ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
     316             :   Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
     317             : 
     318             :   // Get normalized c and c_t values (if normalization specified
     319      150296 :   const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
     320      150296 :   const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
     321             : 
     322             :   // Compute the friction coefficient (constant or function)
     323             :   const auto & real_tangential_velocity =
     324      150296 :       libmesh_map_find(_weighted_velocities_uo.dofToRealVelocities(), dof);
     325      150296 :   ADReal mu_ad = computeFrictionValue(contact_pressure, real_tangential_velocity[0], 0.0);
     326             : 
     327             :   ADReal dof_residual;
     328             :   // Primal-dual active set strategy (PDASS)
     329      150296 :   if (contact_pressure < _epsilon)
     330       42743 :     dof_residual = friction_lm_value;
     331             :   else
     332             :   {
     333      107553 :     const auto lambda_plus_cg = contact_pressure + c * weighted_gap;
     334      107553 :     const auto normal_bound = max(0.0, lambda_plus_cg);
     335             :     const auto friction_bound = mu_ad * normal_bound;
     336             :     const auto tangential_compliance =
     337      115001 :         _friction_elastic_slip > 0.0 ? _friction_elastic_slip / (friction_bound + _epsilon) : 0.0;
     338             :     const auto lambda_t_plus_ctu =
     339             :         friction_lm_value +
     340      107553 :         c_t * (tangential_vel * _dt - tangential_compliance * friction_lm_value);
     341             : 
     342      215106 :     const auto term_1 = max(friction_bound, abs(lambda_t_plus_ctu)) * friction_lm_value;
     343             :     const auto term_2 = friction_bound * lambda_t_plus_ctu;
     344             : 
     345      107553 :     dof_residual = term_1 - term_2;
     346             :   }
     347             : 
     348      150296 :   addResidualsAndJacobian(_assembly,
     349      300592 :                           std::array<ADReal, 1>{{dof_residual}},
     350      300592 :                           std::array<dof_id_type, 1>{{friction_dof_index}},
     351             :                           _friction_vars[0]->scalingFactor());
     352      150296 : }
     353             : 
     354             : ADReal
     355      190186 : ComputeFrictionalForceLMMechanicalContact::computeFrictionValue(const ADReal & contact_pressure,
     356             :                                                                 const ADReal & tangential_vel,
     357             :                                                                 const ADReal & tangential_vel_dir)
     358             : {
     359             :   using std::sqrt;
     360             : 
     361             :   // TODO: Introduce temperature dependence in the function. Do this when we have an example.
     362             :   ADReal mu_ad;
     363             : 
     364      190186 :   if (!_has_friction_function)
     365      183578 :     mu_ad = _mu;
     366             :   else
     367             :   {
     368             :     ADReal tangential_vel_magnitude =
     369        6608 :         sqrt(tangential_vel * tangential_vel + tangential_vel_dir * tangential_vel_dir + 1.0e-24);
     370        6608 :     mu_ad = _function_friction->value<ADReal>(0.0, contact_pressure, tangential_vel_magnitude, 0.0);
     371             :   }
     372             : 
     373             :   const auto slip_increment =
     374      190186 :       sqrt(tangential_vel * tangential_vel + tangential_vel_dir * tangential_vel_dir + 1.0e-24) *
     375      190186 :       _dt;
     376             :   return Moose::Contact::regularizedFrictionCoefficient(
     377      190186 :       mu_ad, slip_increment, _friction_coefficient_regularization, _friction_reference_slip);
     378             : }

Generated by: LCOV version 1.14