LCOV - code coverage report
Current view: top level - src/userobjects - CohesiveZoneModelBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose contact: #33390 (250e9c) with base 846a5c Lines: 183 194 94.3 %
Date: 2026-07-31 18:16:12 Functions: 14 16 87.5 %
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 "CohesiveZoneModelBase.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             : InputParameters
      27          90 : CohesiveZoneModelBase::validParams()
      28             : {
      29          90 :   InputParameters params = WeightedVelocitiesUserObject::validParams();
      30          90 :   params += PenaltyWeightedGapUserObject::validParams();
      31         180 :   params.addRequiredCoupledVar("displacements",
      32             :                                "The string of displacements suitable for the problem statement");
      33          90 :   params.addClassDescription(
      34             :       "Base class for mortar-based cohesive zone model. To handle frictional cohesive interfaces, "
      35             :       "it computes the mortar frictional contact forces using a penalty approach.");
      36         180 :   params.addParam<Real>(
      37             :       "penalty_friction",
      38             :       "The penalty factor for frictional interaction. If not provided, the normal "
      39             :       "penalty factor is also used for the frictional problem.");
      40         180 :   params.addParam<Real>(
      41         180 :       "friction_coefficient", 0.0, "The friction coefficient ruling Coulomb friction equations.");
      42             :   // Suppress augmented Lagrange parameters. AL implementation for CZM remains to be done.
      43          90 :   params.suppressParameter<Real>("max_penalty_multiplier");
      44          90 :   params.suppressParameter<Real>("penalty_multiplier");
      45          90 :   params.suppressParameter<Real>("penetration_tolerance");
      46          90 :   params.setParameters("penalty", 0.0);
      47          90 :   params.set<bool>("allow_nodal_normal_derivatives") = false;
      48          90 :   params.set<bool>("use_nodal_normal_derivatives") = false;
      49          90 :   params.set<bool>("ghost_point_neighbors") = false;
      50          90 :   return params;
      51           0 : }
      52             : 
      53          45 : CohesiveZoneModelBase::CohesiveZoneModelBase(const InputParameters & parameters)
      54             :   /*
      55             :    * We are using virtual inheritance to avoid the "Diamond inheritance" problem. This means that
      56             :    * we have to construct WeightedGapUserObject explicitly as it will _not_ be constructed in
      57             :    * the intermediate base classes PenaltyWeightedGapUserObject and WeightedVelocitiesUserObject.
      58             :    * Virtual inheritance ensures that only one instance of WeightedGapUserObject is included in this
      59             :    * class. The inheritance diagram is as follows:
      60             :    *
      61             :    * WeightedGapUserObject         <-----   PenaltyWeightedGapUserObject
      62             :    *          ^                                         ^
      63             :    *          |                                         |
      64             :    * WeightedVelocitiesUserObject  <-----   CohesiveZoneModelBase
      65             :    *
      66             :    */
      67             :   : WeightedGapUserObject(parameters),
      68             :     PenaltyWeightedGapUserObject(parameters),
      69             :     WeightedVelocitiesUserObject(parameters),
      70          45 :     _ndisp(coupledComponents("displacements")),
      71         180 :     _penalty_friction(isParamValid("penalty_friction") ? getParam<Real>("penalty_friction")
      72          45 :                                                        : getParam<Real>("penalty")),
      73          90 :     _friction_coefficient(getParam<Real>("friction_coefficient")),
      74          45 :     _dof_to_accumulated_slip(
      75          45 :         declareRestartableData<std::unordered_map<dof_id_type, std::pair<ADTwoVector, TwoVector>>>(
      76             :             "dof_to_accumulated_slip",
      77          45 :             std::unordered_map<dof_id_type, std::pair<ADTwoVector, TwoVector>>{})),
      78          45 :     _dof_to_tangential_traction(
      79          45 :         declareRestartableData<std::unordered_map<dof_id_type, std::pair<ADTwoVector, TwoVector>>>(
      80             :             "dof_to_tangential_traction",
      81          90 :             std::unordered_map<dof_id_type, std::pair<ADTwoVector, TwoVector>>{})),
      82          45 :     _epsilon_tolerance(1.0e-40),
      83          45 :     _dof_to_damage(declareRestartableData<std::unordered_map<dof_id_type, std::pair<ADReal, Real>>>(
      84         135 :         "dof_do_damage", std::unordered_map<dof_id_type, std::pair<ADReal, Real>>{}))
      85             : {
      86          45 :   _czm_interpolated_traction.resize(_ndisp);
      87             : 
      88         135 :   for (unsigned int i = 0; i < _ndisp; ++i)
      89             :   {
      90          90 :     _grad_disp.push_back(&adCoupledGradient("displacements", i));
      91         180 :     _grad_disp_neighbor.push_back(&adCoupledGradient("displacements", i));
      92             :   }
      93             : 
      94             :   // Set non-intervening components to zero
      95         135 :   for ([[maybe_unused]] const auto i : make_range(_ndisp))
      96             :   {
      97          90 :     _grad_disp.push_back(&_ad_grad_zero);
      98          90 :     _grad_disp_neighbor.push_back(&_ad_grad_zero);
      99             :   }
     100             : 
     101          45 :   if (_augmented_lagrange_problem)
     102           0 :     mooseError("CohesiveZoneModelBase constraints cannot be enforced with an augmented "
     103             :                "Lagrange approach.");
     104          45 : }
     105             : 
     106             : const VariableTestValue &
     107          45 : CohesiveZoneModelBase::test() const
     108             : {
     109          45 :   return _aux_lm_var ? _aux_lm_var->phiLower() : _disp_x_var->phiLower();
     110             : }
     111             : 
     112             : void
     113       26886 : CohesiveZoneModelBase::computeQpProperties()
     114             : {
     115       26886 :   WeightedVelocitiesUserObject::computeQpProperties();
     116             : 
     117             :   // Compute F and R.
     118       26886 :   const auto F = (ADRankTwoTensor::Identity() +
     119       53772 :                   ADRankTwoTensor::initializeFromRows(
     120       53772 :                       (*_grad_disp[0])[_qp], (*_grad_disp[1])[_qp], (*_grad_disp[2])[_qp]));
     121       26886 :   const auto F_neighbor = (ADRankTwoTensor::Identity() +
     122       53772 :                            ADRankTwoTensor::initializeFromRows((*_grad_disp_neighbor[0])[_qp],
     123       26886 :                                                                (*_grad_disp_neighbor[1])[_qp],
     124       53772 :                                                                (*_grad_disp_neighbor[2])[_qp]));
     125             : 
     126             :   // TODO in follow-on PRs: Trim interior node variable derivatives
     127       26886 :   _F_interpolation = F * (_JxW_msm[_qp] * _coord[_qp]);
     128       26886 :   _F_neighbor_interpolation = F_neighbor * (_JxW_msm[_qp] * _coord[_qp]);
     129       26886 : }
     130             : 
     131             : void
     132       53772 : CohesiveZoneModelBase::computeQpIProperties()
     133             : {
     134       53772 :   WeightedVelocitiesUserObject::computeQpIProperties();
     135             :   // Get the _dof_to_weighted_gap map
     136       53772 :   const auto * const dof = static_cast<const DofObject *>(_lower_secondary_elem->node_ptr(_i));
     137             : 
     138             :   // TODO: Probably better to interpolate the deformation gradients.
     139      107544 :   _dof_to_F[dof] += (*_test)[_i][_qp] * _F_interpolation;
     140      107544 :   _dof_to_F_neighbor[dof] += (*_test)[_i][_qp] * _F_neighbor_interpolation;
     141       53772 : }
     142             : 
     143             : void
     144       26886 : CohesiveZoneModelBase::computeFandR(const Node * const node)
     145             : {
     146             :   // First call does not have maps available
     147       26886 :   const bool return_boolean = _dof_to_F.find(node) == _dof_to_F.end();
     148       26886 :   if (return_boolean)
     149           0 :     return;
     150             : 
     151       26886 :   const auto normalized_F = normalizeQuantity(_dof_to_F, node);
     152       26886 :   const auto normalized_F_neighbor = normalizeQuantity(_dof_to_F_neighbor, node);
     153             : 
     154             :   // This 'averaging' assumption below can probably be improved upon.
     155       80658 :   _dof_to_interface_F[node] = 0.5 * (normalized_F + normalized_F_neighbor);
     156             : 
     157      107544 :   for (const auto i : make_range(Moose::dim))
     158      322632 :     for (const auto j : make_range(Moose::dim))
     159      241974 :       if (!std::isfinite(MetaPhysicL::raw_value(normalized_F(i, j))))
     160             :         throw MooseException("The deformation gradient on the secondary surface is not finite in "
     161           0 :                              "CohesiveZoneModelBase. MOOSE needs to cut the time step size.");
     162             : 
     163       26886 :   const auto dof_to_interface_F_node = libmesh_map_find(_dof_to_interface_F, node);
     164             : 
     165       26886 :   const ADFactorizedRankTwoTensor C = dof_to_interface_F_node.transpose() * dof_to_interface_F_node;
     166       26886 :   const auto Uinv = MathUtils::sqrt(C).inverse().get();
     167       53772 :   _dof_to_interface_R[node] = dof_to_interface_F_node * Uinv;
     168             : 
     169             :   // Transform interface jump according to two rotation matrices
     170       26886 :   const auto global_interface_displacement = _dof_to_interface_displacement_jump[node];
     171       53772 :   _dof_to_interface_displacement_jump[node] =
     172       26886 :       (_dof_to_interface_R[node] * _dof_to_rotation_matrix[node]).transpose() *
     173       26886 :       global_interface_displacement;
     174             : }
     175             : 
     176             : void
     177         493 : CohesiveZoneModelBase::timestepSetup()
     178             : {
     179             :   // these functions do not call WeightedGapUserObject::timestepSetup to avoid double initialization
     180         493 :   WeightedVelocitiesUserObject::selfTimestepSetup();
     181         493 :   PenaltyWeightedGapUserObject::selfTimestepSetup();
     182             : 
     183             :   // instead we call it explicitly here
     184         493 :   WeightedGapUserObject::timestepSetup();
     185             : 
     186             :   // Clear step slip (values used in between AL iterations for penalty adaptivity)
     187         493 :   for (auto & map_pr : _dof_to_step_slip)
     188             :   {
     189             :     auto & [step_slip, old_step_slip] = map_pr.second;
     190             :     old_step_slip = {0.0, 0.0};
     191           0 :     step_slip = {0.0, 0.0};
     192             :   }
     193             : 
     194             :   // save off accumulated slip from the last timestep
     195        1758 :   for (auto & map_pr : _dof_to_accumulated_slip)
     196             :   {
     197             :     auto & [accumulated_slip, old_accumulated_slip] = map_pr.second;
     198             :     old_accumulated_slip = MetaPhysicL::raw_value(accumulated_slip);
     199             :   }
     200             : 
     201         493 :   for (auto & dof_lp : _dof_to_local_penalty_friction)
     202           0 :     dof_lp.second = _penalty_friction;
     203             : 
     204             :   // save off tangential traction from the last timestep
     205        1758 :   for (auto & map_pr : _dof_to_tangential_traction)
     206             :   {
     207             :     auto & [tangential_traction, old_tangential_traction] = map_pr.second;
     208             :     old_tangential_traction = {MetaPhysicL::raw_value(tangential_traction(0)),
     209             :                                MetaPhysicL::raw_value(tangential_traction(1))};
     210        2530 :     tangential_traction = {0.0, 0.0};
     211             :   }
     212             : 
     213         493 :   for (auto & [dof_object, delta_tangential_lm] : _dof_to_frictional_lagrange_multipliers)
     214             :     delta_tangential_lm.setZero();
     215             : 
     216             :   // save off damage from the last timestep
     217        1758 :   for (auto & map_pr : _dof_to_damage)
     218             :   {
     219             :     auto & [damage, old_damage] = map_pr.second;
     220        1265 :     old_damage = {MetaPhysicL::raw_value(damage)};
     221        1265 :     damage = {0.0};
     222             :   }
     223         493 : }
     224             : 
     225             : void
     226        7087 : CohesiveZoneModelBase::initialize()
     227             : {
     228             :   // these functions do not call WeightedGapUserObject::initialize to avoid double initialization
     229        7087 :   WeightedVelocitiesUserObject::selfInitialize();
     230        7087 :   PenaltyWeightedGapUserObject::selfInitialize();
     231             : 
     232             :   // instead we call it explicitly here
     233        7087 :   WeightedGapUserObject::initialize();
     234             :   _dof_to_F.clear();
     235             :   _dof_to_F_neighbor.clear();
     236             :   _dof_to_interface_displacement_jump.clear();
     237             :   _dof_to_interface_F.clear();
     238             :   _dof_to_interface_R.clear();
     239             :   _dof_to_czm_traction.clear();
     240       21918 :   for (auto & map_pr : _dof_to_rotation_matrix)
     241       14831 :     map_pr.second.setToIdentity();
     242        7087 : }
     243             : 
     244             : void
     245       13443 : CohesiveZoneModelBase::reinit()
     246             : {
     247             :   // Normal contact pressure with penalty
     248       13443 :   PenaltyWeightedGapUserObject::reinit();
     249             : 
     250             :   // Compute all rotations that were created as material properties in CZMComputeDisplacementJump
     251       13443 :   prepareJumpKinematicQuantities();
     252             : 
     253             :   // Reset frictional pressure
     254       13443 :   _frictional_contact_traction_one.resize(_qrule_msm->n_points());
     255       13443 :   _frictional_contact_traction_two.resize(_qrule_msm->n_points()); // 3D
     256             : 
     257       40329 :   for (const auto qp : make_range(_qrule_msm->n_points()))
     258             :   {
     259       26886 :     _frictional_contact_traction_one[qp] = 0.0;
     260       26886 :     _frictional_contact_traction_two[qp] = 0.0;
     261             :   }
     262             : 
     263             :   // zero vector
     264       13443 :   const static TwoVector zero{0.0, 0.0};
     265             : 
     266             :   // iterate over nodes
     267       40329 :   for (const auto i : make_range(_test->size()))
     268             :   {
     269             :     // current node
     270       26886 :     const Node * const node = _lower_secondary_elem->node_ptr(i);
     271             : 
     272             :     // Compute the weighted nodal deformation gradient and rotation tensors.
     273       26886 :     computeFandR(node);
     274             : 
     275             :     // The call below is a 'macro' call. Create a utility function or user object for it.
     276       26886 :     computeCZMTraction(node);
     277             : 
     278             :     // Build final traction vector
     279       26886 :     computeGlobalTraction(node);
     280             : 
     281             :     // Compute mechanical contact until end of method.
     282       53772 :     const auto penalty_friction = findValue(
     283       26886 :         _dof_to_local_penalty_friction, static_cast<const DofObject *>(node), _penalty_friction);
     284             : 
     285             :     // utilized quantities
     286       26886 :     const auto & normal_pressure = _dof_to_normal_pressure[node];
     287             : 
     288             :     // map the tangential traction and accumulated slip
     289       26886 :     auto & [tangential_traction, old_tangential_traction] = _dof_to_tangential_traction[node->id()];
     290       26886 :     auto & [accumulated_slip, old_accumulated_slip] = _dof_to_accumulated_slip[node->id()];
     291             : 
     292             :     Real normal_lm = -1;
     293       26886 :     if (auto it = _dof_to_lagrange_multiplier.find(node); it != _dof_to_lagrange_multiplier.end())
     294           0 :       normal_lm = it->second;
     295             : 
     296             :     // Keep active set fixed from second Uzawa loop
     297       26886 :     if (normal_lm < -TOLERANCE && normal_pressure > TOLERANCE)
     298             :     {
     299             :       using std::abs;
     300             : 
     301        5358 :       const auto & damage = _dof_to_damage[node->id()].first;
     302             : 
     303             :       const auto & real_tangential_velocity =
     304        5358 :           libmesh_map_find(_dof_to_real_tangential_velocity, node);
     305        5358 :       const ADTwoVector slip_distance = {real_tangential_velocity[0] * _dt,
     306        5358 :                                          real_tangential_velocity[1] * _dt};
     307             : 
     308             :       // frictional lagrange multiplier (delta lambda^(k)_T)
     309             :       const auto & tangential_lm =
     310        5358 :           _augmented_lagrange_problem ? _dof_to_frictional_lagrange_multipliers[node] : zero;
     311             : 
     312             :       // tangential trial traction (Simo 3.12)
     313             :       // Modified for implementation in MOOSE: Avoid pingponging on frictional sign (max. 0.4
     314             :       // capacity)
     315        5358 :       ADTwoVector inner_iteration_penalty_friction = penalty_friction * slip_distance;
     316             : 
     317             :       const auto slip_metric = std::abs(MetaPhysicL::raw_value(slip_distance).cwiseAbs()(0)) +
     318        5358 :                                std::abs(MetaPhysicL::raw_value(slip_distance).cwiseAbs()(1));
     319             : 
     320           0 :       const auto slip_norm = (MetaPhysicL::raw_value(slip_distance)).norm();
     321        5358 :       const auto friction_limit = 0.4 * _friction_coefficient * damage * abs(normal_pressure);
     322             : 
     323        5358 :       if (slip_metric > _epsilon_tolerance && penalty_friction * slip_norm > friction_limit)
     324             :       {
     325             :         inner_iteration_penalty_friction =
     326        5588 :             MetaPhysicL::raw_value(friction_limit / (penalty_friction * slip_norm)) *
     327        2794 :             penalty_friction * slip_distance;
     328             :       }
     329             : 
     330             :       ADTwoVector tangential_trial_traction =
     331        5358 :           old_tangential_traction + tangential_lm + inner_iteration_penalty_friction;
     332             : 
     333             :       // Nonlinearity below
     334             :       ADReal tangential_trial_traction_norm =
     335        5358 :           (MetaPhysicL::raw_value(tangential_trial_traction)).norm();
     336       10716 :       ADReal phi_trial = tangential_trial_traction_norm - _friction_coefficient * normal_pressure;
     337             :       tangential_traction = tangential_trial_traction;
     338             : 
     339             :       // Simo considers this a 'return mapping'; we are just capping friction to the Coulomb limit.
     340        5358 :       if (phi_trial > 0.0)
     341             :         // Simo 3.13/3.14 (the penalty formulation has an error in the paper)
     342             :         tangential_traction -=
     343        2439 :             phi_trial * tangential_trial_traction / tangential_trial_traction_norm;
     344             : 
     345             :       // track accumulated slip for output purposes
     346        5358 :       accumulated_slip = old_accumulated_slip + slip_distance.cwiseAbs();
     347             :     }
     348             :     else
     349             :     {
     350             :       // reset slip and clear traction
     351       21528 :       accumulated_slip.setZero();
     352       21528 :       tangential_traction.setZero();
     353             :     }
     354             : 
     355             :     // Now that we have consistent nodal frictional values, create an interpolated frictional
     356             :     // pressure variable.
     357       26886 :     const auto test_i = (*_test)[i];
     358       80658 :     for (const auto qp : make_range(_qrule_msm->n_points()))
     359             :     {
     360      107544 :       _frictional_contact_traction_one[qp] += test_i[qp] * tangential_traction(0);
     361       53772 :       _frictional_contact_traction_two[qp] += test_i[qp] * tangential_traction(1);
     362             :     }
     363       26886 :   }
     364             : 
     365       40329 :   for (const auto i : index_range(_czm_interpolated_traction))
     366             :   {
     367       26886 :     _czm_interpolated_traction[i].resize(_qrule_msm->n_points());
     368             : 
     369       80658 :     for (const auto qp : make_range(_qrule_msm->n_points()))
     370       53772 :       _czm_interpolated_traction[i][qp] = 0.0;
     371             :   }
     372             : 
     373             :   // iterate over nodes
     374       40329 :   for (const auto i : make_range(_test->size()))
     375             :   {
     376             :     // current node
     377       26886 :     const Node * const node = _lower_secondary_elem->node_ptr(i);
     378             : 
     379             :     // End of CZM bilinear computations
     380       26886 :     auto it = _dof_to_czm_traction.find(node);
     381       26886 :     if (it == _dof_to_czm_traction.end())
     382             :       return;
     383             : 
     384       26886 :     const auto & test_i = (*_test)[i];
     385       80658 :     for (const auto qp : make_range(_qrule_msm->n_points()))
     386      161316 :       for (const auto idx : index_range(_czm_interpolated_traction))
     387      215088 :         _czm_interpolated_traction[idx][qp] += test_i[qp] * (it->second)(idx);
     388             :   }
     389             : }
     390             : 
     391             : void
     392       13443 : CohesiveZoneModelBase::prepareJumpKinematicQuantities()
     393             : {
     394             :   // Compute rotation matrix from secondary surface
     395             :   // Rotation matrices and local interface displacement jump.
     396       40329 :   for (const auto i : make_range(_test->size()))
     397             :   {
     398       26886 :     const Node * const node = _lower_secondary_elem->node_ptr(i);
     399             : 
     400             :     // First call does not have maps available
     401       26886 :     const bool return_boolean = _dof_to_weighted_gap.find(node) == _dof_to_weighted_gap.end();
     402       26886 :     if (return_boolean)
     403             :       return;
     404             : 
     405       53772 :     _dof_to_rotation_matrix[node] = CohesiveZoneModelTools::computeReferenceRotation<true>(
     406       26886 :         _normals[i], _subproblem.mesh().dimension());
     407             : 
     408             :     // Every time we used quantities from a map we "denormalize" it from the mortar integral.
     409             :     // See normalizing member functions.
     410       26886 :     if (auto it = _dof_to_weighted_displacements.find(node);
     411             :         it != _dof_to_weighted_displacements.end())
     412       53772 :       _dof_to_interface_displacement_jump[node] = it->second;
     413             :   }
     414             : }
     415             : 
     416             : void
     417        7087 : CohesiveZoneModelBase::finalize()
     418             : {
     419        7087 :   WeightedVelocitiesUserObject::finalize();
     420        7087 :   PenaltyWeightedGapUserObject::selfFinalize();
     421             : 
     422        7087 :   const bool send_data_back = !constrainedByOwner();
     423             : 
     424       14174 :   Moose::Mortar::Contact::communicateR2T(
     425        7087 :       _dof_to_F, _subproblem.mesh(), _nodal, _communicator, send_data_back);
     426             : 
     427       14174 :   Moose::Mortar::Contact::communicateR2T(
     428        7087 :       _dof_to_F_neighbor, _subproblem.mesh(), _nodal, _communicator, send_data_back);
     429             : 
     430       14174 :   Moose::Mortar::Contact::communicateRealObject(
     431        7087 :       _dof_to_weighted_displacements, _subproblem.mesh(), _nodal, _communicator, send_data_back);
     432        7087 : }
     433             : 
     434             : const ADVariableValue &
     435       31856 : CohesiveZoneModelBase::contactTangentialPressureDirOne() const
     436             : {
     437       31856 :   return _frictional_contact_traction_one;
     438             : }
     439             : 
     440             : const ADVariableValue &
     441           0 : CohesiveZoneModelBase::contactTangentialPressureDirTwo() const
     442             : {
     443           0 :   return _frictional_contact_traction_two;
     444             : }
     445             : 
     446             : void
     447       26886 : CohesiveZoneModelBase::computeGlobalTraction(const Node * const node)
     448             : {
     449             :   // First call does not have maps available
     450       26886 :   const auto it = _dof_to_czm_traction.find(node);
     451       26886 :   if (it == _dof_to_czm_traction.end())
     452           0 :     return;
     453             : 
     454             :   const auto local_traction_vector = it->second;
     455       26886 :   const auto rotation_matrix = libmesh_map_find(_dof_to_rotation_matrix, node);
     456             : 
     457       53772 :   _dof_to_czm_traction[node] = rotation_matrix * local_traction_vector;
     458             : }
     459             : 
     460             : const ADVariableValue &
     461      215088 : CohesiveZoneModelBase::czmGlobalTraction(const unsigned int i) const
     462             : {
     463             :   mooseAssert(i <= 3,
     464             :               "Internal error in czmGlobalTraction. Index exceeds the traction vector size.");
     465             : 
     466      215088 :   return _czm_interpolated_traction[i];
     467             : }

Generated by: LCOV version 1.14