LCOV - code coverage report
Current view: top level - src/constraints - ComputeFrictionalForceLMMechanicalContact.C (source / functions) Hit Total Coverage
Test: idaholab/moose contact: 8601ad Lines: 130 143 90.9 %
Date: 2025-07-18 13:27:36 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 "WeightedVelocitiesUserObject.h"
      15             : 
      16             : #include "metaphysicl/dualsemidynamicsparsenumberarray.h"
      17             : #include "metaphysicl/parallel_dualnumber.h"
      18             : #include "metaphysicl/parallel_dynamic_std_array_wrapper.h"
      19             : #include "metaphysicl/parallel_semidynamicsparsenumberarray.h"
      20             : #include "timpi/parallel_sync.h"
      21             : 
      22             : #include <cmath>
      23             : 
      24             : registerMooseObject("ContactApp", ComputeFrictionalForceLMMechanicalContact);
      25             : 
      26             : InputParameters
      27         550 : ComputeFrictionalForceLMMechanicalContact::validParams()
      28             : {
      29         550 :   InputParameters params = ComputeWeightedGapLMMechanicalContact::validParams();
      30         550 :   params.addClassDescription("Computes the tangential frictional forces");
      31        1100 :   params.addRequiredCoupledVar("friction_lm", "The frictional Lagrange's multiplier");
      32        1100 :   params.addCoupledVar("friction_lm_dir",
      33             :                        "The frictional Lagrange's multiplier for an addtional direction.");
      34        1100 :   params.addParam<FunctionName>(
      35             :       "function_friction",
      36             :       "Coupled function to evaluate friction with values from contact pressure and relative "
      37             :       "tangential velocities");
      38        1100 :   params.addParam<Real>("c_t", 1e0, "Numerical parameter for tangential constraints");
      39        1100 :   params.addParam<Real>(
      40             :       "epsilon",
      41        1100 :       1.0e-7,
      42             :       "Minimum value of contact pressure that will trigger frictional enforcement");
      43        1100 :   params.addRangeCheckedParam<Real>(
      44             :       "mu", "mu > 0", "The friction coefficient for the Coulomb friction law");
      45        1100 :   params.addRequiredParam<UserObjectName>("weighted_velocities_uo",
      46             :                                           "The weighted tangential velocities user object.");
      47             : 
      48         550 :   return params;
      49           0 : }
      50             : 
      51         275 : ComputeFrictionalForceLMMechanicalContact::ComputeFrictionalForceLMMechanicalContact(
      52         275 :     const InputParameters & parameters)
      53             :   : ComputeWeightedGapLMMechanicalContact(parameters),
      54         275 :     _weighted_velocities_uo(getUserObject<WeightedVelocitiesUserObject>("weighted_velocities_uo")),
      55         550 :     _c_t(getParam<Real>("c_t")),
      56         275 :     _secondary_x_dot(_secondary_var.adUDot()),
      57         275 :     _primary_x_dot(_primary_var.adUDotNeighbor()),
      58         275 :     _secondary_y_dot(adCoupledDot("disp_y")),
      59         275 :     _primary_y_dot(adCoupledNeighborValueDot("disp_y")),
      60         275 :     _secondary_z_dot(_has_disp_z ? &adCoupledDot("disp_z") : nullptr),
      61         275 :     _primary_z_dot(_has_disp_z ? &adCoupledNeighborValueDot("disp_z") : nullptr),
      62         550 :     _epsilon(getParam<Real>("epsilon")),
      63         817 :     _mu(isParamValid("function_friction") ? std::numeric_limits<double>::quiet_NaN()
      64         809 :                                           : getParam<Real>("mu")),
      65         558 :     _function_friction(isParamValid("function_friction") ? &getFunction("function_friction")
      66             :                                                          : nullptr),
      67         550 :     _has_friction_function(isParamValid("function_friction")),
      68         550 :     _3d(_has_disp_z)
      69             : 
      70             : {
      71         550 :   if (parameters.isParamSetByUser("mu") && _has_friction_function)
      72           0 :     paramError(
      73             :         "mu",
      74             :         "Please only provide friction either as a function or as a constant value, but not both.");
      75         550 :   else if (!parameters.isParamSetByUser("mu") && !_has_friction_function)
      76           0 :     paramError("mu", "Please provide a value or a function for the coefficient of friction.");
      77             : 
      78         550 :   if (!getParam<bool>("use_displaced_mesh"))
      79           0 :     paramError("use_displaced_mesh",
      80             :                "'use_displaced_mesh' must be true for the "
      81             :                "ComputeFrictionalForceLMMechanicalContact object");
      82             : 
      83         440 :   if (_3d && !isParamValid("friction_lm_dir"))
      84           0 :     paramError("friction_lm_dir",
      85             :                "Three-dimensional mortar frictional contact simulations require an additional "
      86             :                "frictional Lagrange's multiplier to enforce a second tangential pressure");
      87             : 
      88         275 :   _friction_vars.push_back(getVar("friction_lm", 0));
      89             : 
      90         275 :   if (_3d)
      91         110 :     _friction_vars.push_back(getVar("friction_lm_dir", 0));
      92             : 
      93         275 :   if (!_friction_vars[0]->isNodal())
      94           0 :     if (_friction_vars[0]->feType().order != static_cast<Order>(0))
      95           0 :       paramError(
      96             :           "friction_lm",
      97             :           "Frictional contact constraints only support elemental variables of CONSTANT order");
      98         275 : }
      99             : 
     100             : void
     101           0 : ComputeFrictionalForceLMMechanicalContact::computeQpProperties()
     102             : {
     103           0 : }
     104             : 
     105             : void
     106           0 : ComputeFrictionalForceLMMechanicalContact::computeQpIProperties()
     107             : {
     108           0 : }
     109             : 
     110             : void
     111       63841 : ComputeFrictionalForceLMMechanicalContact::residualSetup()
     112             : {
     113       63841 : }
     114             : 
     115             : void
     116       32052 : ComputeFrictionalForceLMMechanicalContact::post()
     117             : {
     118             :   const auto & dof_to_weighted_tangential_velocity =
     119       32052 :       _weighted_velocities_uo.dofToWeightedVelocities();
     120             : 
     121             :   const std::unordered_map<const DofObject *, std::pair<ADReal, Real>> & dof_to_weighted_gap =
     122       32052 :       _weighted_gap_uo.dofToWeightedGap();
     123             : 
     124             :   // Enforce frictional constraints
     125             : 
     126      131077 :   for (const auto & [dof_object, weighted_velocities_pr] : dof_to_weighted_tangential_velocity)
     127             :   {
     128       99025 :     if (dof_object->processor_id() != this->processor_id())
     129        3963 :       continue;
     130             : 
     131             :     const auto & [weighted_gap_pr, normalization] =
     132       95062 :         libmesh_map_find(dof_to_weighted_gap, dof_object);
     133       95062 :     _weighted_gap_ptr = &weighted_gap_pr;
     134       95062 :     _normalization_ptr = &normalization;
     135       95062 :     _tangential_vel_ptr[0] = &(weighted_velocities_pr[0]);
     136             : 
     137       95062 :     if (_3d)
     138             :     {
     139           0 :       _tangential_vel_ptr[1] = &(weighted_velocities_pr[1]);
     140           0 :       enforceConstraintOnDof3d(dof_object);
     141             :     }
     142             :     else
     143       95062 :       enforceConstraintOnDof(dof_object);
     144             :   }
     145       32052 : }
     146             : 
     147             : void
     148       46494 : ComputeFrictionalForceLMMechanicalContact::incorrectEdgeDroppingPost(
     149             :     const std::unordered_set<const Node *> & inactive_lm_nodes)
     150             : {
     151             :   const auto & dof_to_weighted_tangential_velocity =
     152       46494 :       _weighted_velocities_uo.dofToWeightedVelocities();
     153       46494 :   const auto & dof_to_weighted_gap = _weighted_gap_uo.dofToWeightedGap();
     154             :   // Enforce frictional complementarity constraints
     155      185927 :   for (const auto & [dof_object, weighted_velocities_pr] : dof_to_weighted_tangential_velocity)
     156             :   {
     157             :     // If node inactive, skip
     158      274750 :     if ((inactive_lm_nodes.find(static_cast<const Node *>(dof_object)) !=
     159      139433 :          inactive_lm_nodes.end()) ||
     160             :         (dof_object->processor_id() != this->processor_id()))
     161        4116 :       continue;
     162             : 
     163      135317 :     _weighted_gap_ptr = &dof_to_weighted_gap.at(dof_object).first;
     164      135317 :     _normalization_ptr = &dof_to_weighted_gap.at(dof_object).second;
     165      135317 :     _tangential_vel_ptr[0] = &weighted_velocities_pr[0];
     166             : 
     167      135317 :     if (_3d)
     168             :     {
     169       34671 :       _tangential_vel_ptr[1] = &weighted_velocities_pr[1];
     170       34671 :       enforceConstraintOnDof3d(dof_object);
     171             :     }
     172             :     else
     173      100646 :       enforceConstraintOnDof(dof_object);
     174             :   }
     175       46494 : }
     176             : 
     177             : void
     178       34671 : ComputeFrictionalForceLMMechanicalContact::enforceConstraintOnDof3d(const DofObject * const dof)
     179             : {
     180       34671 :   ComputeWeightedGapLMMechanicalContact::enforceConstraintOnDof(dof);
     181             : 
     182             :   // Get normal LM
     183       34671 :   const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
     184       34671 :   const ADReal & weighted_gap = *_weighted_gap_ptr;
     185       34671 :   ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
     186             :   Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
     187             : 
     188             :   // Get friction LMs
     189             :   std::array<const ADReal *, 2> & tangential_vel = _tangential_vel_ptr;
     190             :   std::array<dof_id_type, 2> friction_dof_indices;
     191             :   std::array<ADReal, 2> friction_lm_values;
     192             : 
     193             :   const unsigned int num_tangents = 2;
     194      104013 :   for (const auto i : make_range(num_tangents))
     195             :   {
     196       69342 :     friction_dof_indices[i] = dof->dof_number(_sys.number(), _friction_vars[i]->number(), 0);
     197       69342 :     friction_lm_values[i] = (*_sys.currentSolution())(friction_dof_indices[i]);
     198             :     Moose::derivInsert(friction_lm_values[i].derivatives(), friction_dof_indices[i], 1.);
     199             :   }
     200             : 
     201             :   // Get normalized c and c_t values (if normalization specified
     202       34671 :   const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
     203       34671 :   const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
     204             : 
     205             :   // Compute the friction coefficient (constant or function)
     206             :   ADReal mu_ad = computeFrictionValue(contact_pressure,
     207             :                                       _dof_to_real_tangential_velocity[dof][0],
     208       34671 :                                       _dof_to_real_tangential_velocity[dof][1]);
     209             : 
     210             :   ADReal dof_residual;
     211             :   ADReal dof_residual_dir;
     212             : 
     213             :   // Primal-dual active set strategy (PDASS)
     214       34671 :   if (contact_pressure < _epsilon)
     215             :   {
     216        9560 :     dof_residual = friction_lm_values[0];
     217        9560 :     dof_residual_dir = friction_lm_values[1];
     218             :   }
     219             :   else
     220             :   {
     221             :     // Espilon to avoid automatic differentiation singularity
     222             :     const Real epsilon_sqrt = 1.0e-48;
     223             : 
     224       25111 :     const auto lamdba_plus_cg = contact_pressure + c * weighted_gap;
     225             :     std::array<ADReal, 2> lambda_t_plus_ctu;
     226       50222 :     lambda_t_plus_ctu[0] = friction_lm_values[0] + c_t * *tangential_vel[0] * _dt;
     227       50222 :     lambda_t_plus_ctu[1] = friction_lm_values[1] + c_t * *tangential_vel[1] * _dt;
     228             : 
     229             :     const auto term_1_x =
     230       25111 :         std::max(mu_ad * lamdba_plus_cg,
     231       25111 :                  std::sqrt(lambda_t_plus_ctu[0] * lambda_t_plus_ctu[0] +
     232       25111 :                            lambda_t_plus_ctu[1] * lambda_t_plus_ctu[1] + epsilon_sqrt)) *
     233             :         friction_lm_values[0];
     234             : 
     235             :     const auto term_1_y =
     236       25111 :         std::max(mu_ad * lamdba_plus_cg,
     237       25111 :                  std::sqrt(lambda_t_plus_ctu[0] * lambda_t_plus_ctu[0] +
     238       25111 :                            lambda_t_plus_ctu[1] * lambda_t_plus_ctu[1] + epsilon_sqrt)) *
     239             :         friction_lm_values[1];
     240             : 
     241       25111 :     const auto term_2_x = mu_ad * std::max(0.0, lamdba_plus_cg) * lambda_t_plus_ctu[0];
     242             : 
     243       50222 :     const auto term_2_y = mu_ad * std::max(0.0, lamdba_plus_cg) * lambda_t_plus_ctu[1];
     244             : 
     245       25111 :     dof_residual = term_1_x - term_2_x;
     246       25111 :     dof_residual_dir = term_1_y - term_2_y;
     247             :   }
     248             : 
     249       34671 :   addResidualsAndJacobian(_assembly,
     250       34671 :                           std::array<ADReal, 1>{{dof_residual}},
     251       34671 :                           std::array<dof_id_type, 1>{{friction_dof_indices[0]}},
     252             :                           _friction_vars[0]->scalingFactor());
     253       34671 :   addResidualsAndJacobian(_assembly,
     254       34671 :                           std::array<ADReal, 1>{{dof_residual_dir}},
     255       34671 :                           std::array<dof_id_type, 1>{{friction_dof_indices[1]}},
     256             :                           _friction_vars[1]->scalingFactor());
     257      104013 : }
     258             : 
     259             : void
     260      195708 : ComputeFrictionalForceLMMechanicalContact::enforceConstraintOnDof(const DofObject * const dof)
     261             : {
     262      195708 :   ComputeWeightedGapLMMechanicalContact::enforceConstraintOnDof(dof);
     263             : 
     264             :   // Get friction LM
     265      195708 :   const auto friction_dof_index = dof->dof_number(_sys.number(), _friction_vars[0]->number(), 0);
     266      195708 :   const ADReal & tangential_vel = *_tangential_vel_ptr[0];
     267      195708 :   ADReal friction_lm_value = (*_sys.currentSolution())(friction_dof_index);
     268             :   Moose::derivInsert(friction_lm_value.derivatives(), friction_dof_index, 1.);
     269             : 
     270             :   // Get normal LM
     271      195708 :   const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
     272      195708 :   const ADReal & weighted_gap = *_weighted_gap_ptr;
     273      195708 :   ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
     274             :   Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
     275             : 
     276             :   // Get normalized c and c_t values (if normalization specified
     277      195708 :   const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
     278      195708 :   const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
     279             : 
     280             :   // Compute the friction coefficient (constant or function)
     281             :   ADReal mu_ad =
     282      391416 :       computeFrictionValue(contact_pressure, _dof_to_real_tangential_velocity[dof][0], 0.0);
     283             : 
     284             :   ADReal dof_residual;
     285             :   // Primal-dual active set strategy (PDASS)
     286      195708 :   if (contact_pressure < _epsilon)
     287       56908 :     dof_residual = friction_lm_value;
     288             :   else
     289             :   {
     290      138800 :     const auto term_1 = std::max(mu_ad * (contact_pressure + c * weighted_gap),
     291      277600 :                                  std::abs(friction_lm_value + c_t * tangential_vel * _dt)) *
     292             :                         friction_lm_value;
     293      138800 :     const auto term_2 = mu_ad * std::max(0.0, contact_pressure + c * weighted_gap) *
     294      277600 :                         (friction_lm_value + c_t * tangential_vel * _dt);
     295             : 
     296      138800 :     dof_residual = term_1 - term_2;
     297             :   }
     298             : 
     299      195708 :   addResidualsAndJacobian(_assembly,
     300      195708 :                           std::array<ADReal, 1>{{dof_residual}},
     301      195708 :                           std::array<dof_id_type, 1>{{friction_dof_index}},
     302             :                           _friction_vars[0]->scalingFactor());
     303      391416 : }
     304             : 
     305             : ADReal
     306      230379 : ComputeFrictionalForceLMMechanicalContact::computeFrictionValue(const ADReal & contact_pressure,
     307             :                                                                 const ADReal & tangential_vel,
     308             :                                                                 const ADReal & tangential_vel_dir)
     309             : {
     310             :   // TODO: Introduce temperature dependence in the function. Do this when we have an example.
     311             :   ADReal mu_ad;
     312             : 
     313      230379 :   if (!_has_friction_function)
     314      225339 :     mu_ad = _mu;
     315             :   else
     316             :   {
     317        5040 :     ADReal tangential_vel_magnitude = std::sqrt(tangential_vel * tangential_vel +
     318        5040 :                                                 tangential_vel_dir * tangential_vel_dir + 1.0e-24);
     319        5040 :     mu_ad = _function_friction->value<ADReal>(0.0, contact_pressure, tangential_vel_magnitude, 0.0);
     320             :   }
     321             : 
     322      230379 :   return mu_ad;
     323             : }

Generated by: LCOV version 1.14