LCOV - code coverage report
Current view: top level - src/userobjects - PenaltySimpleCohesiveZoneModel.C (source / functions) Hit Total Coverage
Test: idaholab/moose contact: 8601ad Lines: 78 91 85.7 %
Date: 2025-07-18 13:27:36 Functions: 7 10 70.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 "PenaltySimpleCohesiveZoneModel.h"
      11             : #include "MooseVariableFE.h"
      12             : #include "SystemBase.h"
      13             : #include "MortarUtils.h"
      14             : #include "MooseUtils.h"
      15             : #include "MathUtils.h"
      16             : 
      17             : #include "MortarContactUtils.h"
      18             : #include "FactorizedRankTwoTensor.h"
      19             : 
      20             : #include "ADReal.h"
      21             : 
      22             : #include "CohesiveZoneModelTools.h"
      23             : 
      24             : #include <Eigen/Core>
      25             : 
      26             : registerMooseObject("ContactApp", PenaltySimpleCohesiveZoneModel);
      27             : 
      28             : InputParameters
      29         144 : PenaltySimpleCohesiveZoneModel::validParams()
      30             : {
      31         144 :   InputParameters params = WeightedVelocitiesUserObject::validParams();
      32         144 :   params += PenaltyWeightedGapUserObject::validParams();
      33             : 
      34         144 :   params.addClassDescription(
      35             :       "Computes the mortar frictional contact force via a penalty approach.");
      36         288 :   params.addParam<Real>("penalty_friction",
      37             :                         "The penalty factor for frictional interaction. If not provide, the normal "
      38             :                         "penalty factor is also used for the frictional problem.");
      39         288 :   params.addRequiredParam<Real>("friction_coefficient",
      40             :                                 "The friction coefficient ruling Coulomb friction equations.");
      41             : 
      42         144 :   return params;
      43           0 : }
      44             : 
      45          72 : PenaltySimpleCohesiveZoneModel::PenaltySimpleCohesiveZoneModel(const InputParameters & parameters)
      46             :   /*
      47             :    * We are using virtual inheritance to avoid the "Diamond inheritance" problem. This means that
      48             :    * that we have to construct WeightedGapUserObject explicitly as it will _not_ be constructed in
      49             :    * the intermediate base classes PenaltyWeightedGapUserObject and WeightedVelocitiesUserObject.
      50             :    * Virtual inheritance ensures that only one instance of WeightedGapUserObject is included in this
      51             :    * class. The inheritance diagram is as follows:
      52             :    *
      53             :    * WeightedGapUserObject         <-----   PenaltyWeightedGapUserObject
      54             :    *          ^                                         ^
      55             :    *          |                                         |
      56             :    * WeightedVelocitiesUserObject  <-----   PenaltySimpleCohesiveZoneModel
      57             :    *
      58             :    */
      59             :   : WeightedGapUserObject(parameters),
      60             :     PenaltyWeightedGapUserObject(parameters),
      61             :     WeightedVelocitiesUserObject(parameters),
      62          72 :     _penalty(getParam<Real>("penalty")),
      63         288 :     _penalty_friction(isParamValid("penalty_friction") ? getParam<Real>("penalty_friction")
      64          72 :                                                        : getParam<Real>("penalty")),
      65         144 :     _friction_coefficient(getParam<Real>("friction_coefficient")),
      66          72 :     _epsilon_tolerance(1.0e-40)
      67             : 
      68             : {
      69          72 :   if (_augmented_lagrange_problem)
      70           0 :     mooseError("PenaltySimpleCohesiveZoneModel constraints cannot be enforced with an augmented "
      71             :                "Lagrange approach.");
      72          72 : }
      73             : 
      74             : const VariableTestValue &
      75          72 : PenaltySimpleCohesiveZoneModel::test() const
      76             : {
      77          72 :   return _aux_lm_var ? _aux_lm_var->phiLower() : _disp_x_var->phiLower();
      78             : }
      79             : 
      80             : void
      81         585 : PenaltySimpleCohesiveZoneModel::timestepSetup()
      82             : {
      83             :   // these functions do not call WeightedGapUserObject::timestepSetup to avoid double initialization
      84         585 :   WeightedVelocitiesUserObject::selfTimestepSetup();
      85         585 :   PenaltyWeightedGapUserObject::selfTimestepSetup();
      86             : 
      87             :   // instead we call it explicitly here
      88         585 :   WeightedGapUserObject::timestepSetup();
      89             : 
      90             :   // save off accumulated slip from the last timestep
      91        1996 :   for (auto & map_pr : _dof_to_accumulated_slip)
      92             :   {
      93             :     auto & [accumulated_slip, old_accumulated_slip] = map_pr.second;
      94             :     old_accumulated_slip = accumulated_slip;
      95             :   }
      96             : 
      97         585 :   for (auto & dof_lp : _dof_to_local_penalty_friction)
      98           0 :     dof_lp.second = _penalty_friction;
      99             : 
     100             :   // save off tangential traction from the last timestep
     101        1996 :   for (auto & map_pr : _dof_to_tangential_traction)
     102             :   {
     103             :     auto & [tangential_traction, old_tangential_traction] = map_pr.second;
     104             :     old_tangential_traction = {MetaPhysicL::raw_value(tangential_traction(0)),
     105             :                                MetaPhysicL::raw_value(tangential_traction(1))};
     106        2822 :     tangential_traction = {0.0, 0.0};
     107             :   }
     108             : 
     109         585 :   for (auto & [dof_object, delta_tangential_lm] : _dof_to_frictional_lagrange_multipliers)
     110             :     delta_tangential_lm.setZero();
     111         585 : }
     112             : 
     113             : void
     114        7091 : PenaltySimpleCohesiveZoneModel::initialize()
     115             : {
     116             :   // these functions do not call WeightedGapUserObject::initialize to avoid double initialization
     117        7091 :   WeightedVelocitiesUserObject::selfInitialize();
     118        7091 :   PenaltyWeightedGapUserObject::selfInitialize();
     119             : 
     120             :   // instead we call it explicitly here
     121        7091 :   WeightedGapUserObject::initialize();
     122        7091 : }
     123             : 
     124             : void
     125       15034 : PenaltySimpleCohesiveZoneModel::reinit()
     126             : {
     127             :   // Normal contact pressure with penalty
     128       15034 :   PenaltyWeightedGapUserObject::reinit();
     129             : 
     130             :   // Compute all rotations that were created as material properties in CZMComputeDisplacementJump
     131       15034 :   prepareJumpKinematicQuantities();
     132             : 
     133             :   // Reset frictional pressure
     134       15034 :   _frictional_contact_traction_one.resize(_qrule_msm->n_points());
     135       15034 :   _frictional_contact_traction_two.resize(_qrule_msm->n_points()); // 3D
     136             : 
     137       45102 :   for (const auto qp : make_range(_qrule_msm->n_points()))
     138             :   {
     139       30068 :     _frictional_contact_traction_one[qp] = 0.0;
     140       30068 :     _frictional_contact_traction_two[qp] = 0.0;
     141             :   }
     142             : 
     143             :   // zero vector
     144       15034 :   const static TwoVector zero{0.0, 0.0};
     145             : 
     146             :   // iterate over nodes
     147       45102 :   for (const auto i : make_range(_test->size()))
     148             :   {
     149             :     // current node
     150       30068 :     const Node * const node = _lower_secondary_elem->node_ptr(i);
     151             : 
     152             :     // Compute the weighted nodal deformation gradient and rotation tensors.
     153       30068 :     computeFandR(node);
     154             : 
     155             :     // The call below is a 'macro' call. Create a utility function or user object for it.
     156       30068 :     computeBilinearMixedModeTraction(node);
     157             : 
     158             :     // Build final traction vector
     159       30068 :     computeGlobalTraction(node);
     160             : 
     161             :     // Compute mechanical contact until end of method.
     162       30068 :     const auto penalty_friction = findValue(
     163       30068 :         _dof_to_local_penalty_friction, static_cast<const DofObject *>(node), _penalty_friction);
     164             : 
     165             :     // utilized quantities
     166       30068 :     const auto & normal_pressure = _dof_to_normal_pressure[node];
     167             : 
     168             :     // map the tangential traction and accumulated slip
     169       30068 :     auto & [tangential_traction, old_tangential_traction] = _dof_to_tangential_traction[node];
     170       30068 :     auto & [accumulated_slip, old_accumulated_slip] = _dof_to_accumulated_slip[node];
     171             : 
     172             :     Real normal_lm = -1;
     173       30068 :     if (_dof_to_lagrange_multiplier.find(node) != _dof_to_lagrange_multiplier.end())
     174           0 :       normal_lm = libmesh_map_find(_dof_to_lagrange_multiplier, node);
     175             : 
     176             :     // Keep active set fixed from second Uzawa loop
     177       30068 :     if (normal_lm < -TOLERANCE && normal_pressure > TOLERANCE)
     178             :     {
     179             :       using namespace std;
     180             : 
     181             :       const auto & real_tangential_velocity =
     182        1960 :           libmesh_map_find(_dof_to_real_tangential_velocity, node);
     183        1960 :       const ADTwoVector slip_distance = {real_tangential_velocity[0] * _dt,
     184        1960 :                                          real_tangential_velocity[1] * _dt};
     185             : 
     186             :       // frictional lagrange multiplier (delta lambda^(k)_T)
     187             :       const auto & tangential_lm =
     188        1960 :           _augmented_lagrange_problem ? _dof_to_frictional_lagrange_multipliers[node] : zero;
     189             : 
     190             :       // tangential trial traction (Simo 3.12)
     191             :       // Modified for implementation in MOOSE: Avoid pingponging on frictional sign (max. 0.4
     192             :       // capacity)
     193        1960 :       ADTwoVector inner_iteration_penalty_friction = penalty_friction * slip_distance;
     194             : 
     195             :       const auto slip_metric = std::abs(MetaPhysicL::raw_value(slip_distance).cwiseAbs()(0)) +
     196        1960 :                                std::abs(MetaPhysicL::raw_value(slip_distance).cwiseAbs()(1));
     197             : 
     198        3776 :       if (slip_metric > _epsilon_tolerance &&
     199        5592 :           penalty_friction * slip_distance.norm() >
     200        7408 :               0.4 * _friction_coefficient * std::abs(normal_pressure))
     201             :       {
     202             :         inner_iteration_penalty_friction =
     203           0 :             MetaPhysicL::raw_value(0.4 * _friction_coefficient * std::abs(normal_pressure) /
     204           0 :                                    (penalty_friction * slip_distance.norm())) *
     205           0 :             penalty_friction * slip_distance;
     206             :       }
     207             : 
     208             :       ADTwoVector tangential_trial_traction =
     209        1960 :           old_tangential_traction + tangential_lm + inner_iteration_penalty_friction;
     210             : 
     211             :       // Nonlinearity below
     212        1960 :       ADReal tangential_trial_traction_norm = 0;
     213        3920 :       if (std::abs(tangential_trial_traction(0)) + std::abs(tangential_trial_traction(1)) >
     214             :           _epsilon_tolerance)
     215           0 :         tangential_trial_traction_norm = tangential_trial_traction.norm();
     216             : 
     217        3920 :       ADReal phi_trial = tangential_trial_traction_norm - _friction_coefficient * normal_pressure;
     218             :       tangential_traction = tangential_trial_traction;
     219             : 
     220             :       // Simo considers this a 'return mapping'; we are just capping friction to the Coulomb limit.
     221        1960 :       if (phi_trial > 0.0)
     222             :         // Simo 3.13/3.14 (the penalty formulation has an error in the paper)
     223             :         tangential_traction -=
     224           0 :             phi_trial * tangential_trial_traction / tangential_trial_traction_norm;
     225             : 
     226             :       // track accumulated slip for output purposes
     227        1960 :       accumulated_slip = old_accumulated_slip + MetaPhysicL::raw_value(slip_distance).cwiseAbs();
     228             :     }
     229             :     else
     230             :     {
     231             :       // reset slip and clear traction
     232             :       accumulated_slip.setZero();
     233       28108 :       tangential_traction.setZero();
     234             :     }
     235             : 
     236             :     // Now that we have consistent nodal frictional values, create an interpolated frictional
     237             :     // pressure variable.
     238       30068 :     const auto & test_i = (*_test)[i];
     239       90204 :     for (const auto qp : make_range(_qrule_msm->n_points()))
     240             :     {
     241      120272 :       _frictional_contact_traction_one[qp] += test_i[qp] * tangential_traction(0);
     242       60136 :       _frictional_contact_traction_two[qp] += test_i[qp] * tangential_traction(1);
     243             :     }
     244             :   }
     245       15034 : }
     246             : 
     247             : void
     248        7091 : PenaltySimpleCohesiveZoneModel::finalize()
     249             : {
     250        7091 :   WeightedVelocitiesUserObject::finalize();
     251        7091 :   PenaltyWeightedGapUserObject::selfFinalize();
     252        7091 : }
     253             : 
     254             : const ADVariableValue &
     255           0 : PenaltySimpleCohesiveZoneModel::contactTangentialPressureDirOne() const
     256             : {
     257           0 :   return _frictional_contact_traction_one;
     258             : }
     259             : 
     260             : const ADVariableValue &
     261           0 : PenaltySimpleCohesiveZoneModel::contactTangentialPressureDirTwo() const
     262             : {
     263           0 :   return _frictional_contact_traction_two;
     264             : }

Generated by: LCOV version 1.14