https://mooseframework.inl.gov
PenaltyFrictionUserObject.C
Go to the documentation of this file.
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 
11 #include "MooseVariableFE.h"
12 #include "SystemBase.h"
13 #include "MortarUtils.h"
14 #include "MooseUtils.h"
15 #include "MathUtils.h"
16 #include "MortarContactUtils.h"
17 #include "ADReal.h"
18 
19 #include <Eigen/Core>
20 
22 
25 {
28 
29  params.addClassDescription(
30  "Computes the mortar frictional contact force via a penalty approach.");
31  params.addParam<Real>("penalty_friction",
32  "The penalty factor for frictional interaction. If not provide, the normal "
33  "penalty factor is also used for the frictional problem.");
34  params.addRequiredParam<Real>("friction_coefficient",
35  "The friction coefficient ruling Coulomb friction equations.");
36  params.addRangeCheckedParam<Real>(
37  "slip_tolerance",
38  "slip_tolerance > 0",
39  "Acceptable slip distance at which augmented Lagrange iterations can be stopped");
40  MooseEnum adaptivity_penalty_friction("SIMPLE FRICTION_LIMIT", "FRICTION_LIMIT");
41  adaptivity_penalty_friction.addDocumentation(
42  "SIMPLE", "Keep multiplying by the frictional penalty multiplier between AL iterations");
43  adaptivity_penalty_friction.addDocumentation(
44  "FRICTION_LIMIT",
45  "This strategy will be guided by the Coulomb limit and be less reliant on the initial "
46  "penalty factor provided by the user.");
47  params.addParam<MooseEnum>(
48  "adaptivity_penalty_friction",
49  adaptivity_penalty_friction,
50  "The augmented Lagrange update strategy used on the frictional penalty coefficient.");
51  params.addRangeCheckedParam<Real>(
52  "penalty_multiplier_friction",
53  1.0,
54  "penalty_multiplier_friction > 0",
55  "The penalty growth factor between augmented Lagrange "
56  "iterations for penalizing relative slip distance if the node is under stick conditions.");
57  return params;
58 }
59 
61  /*
62  * We are using virtual inheritance to avoid the "Diamond inheritance" problem. This means that
63  * that we have to construct WeightedGapUserObject explicitly as it will_not_ be constructed in
64  * the intermediate base classes PenaltyWeightedGapUserObject and WeightedVelocitiesUserObject.
65  * Virtual inheritance ensures that only one instance of WeightedGapUserObject is included in this
66  * class. The inheritance diagram is as follows:
67  *
68  * WeightedGapUserObject <----- PenaltyWeightedGapUserObject
69  * ^ ^
70  * | |
71  * WeightedVelocitiesUserObject <----- PenaltyFrictionUserObject
72  *
73  */
74  : WeightedGapUserObject(parameters),
75  PenaltyWeightedGapUserObject(parameters),
76  WeightedVelocitiesUserObject(parameters),
77  _penalty(getParam<Real>("penalty")),
78  _penalty_friction(isParamValid("penalty_friction") ? getParam<Real>("penalty_friction")
79  : getParam<Real>("penalty")),
80  _slip_tolerance(isParamValid("slip_tolerance") ? getParam<Real>("slip_tolerance") : 0.0),
81  _friction_coefficient(getParam<Real>("friction_coefficient")),
82  _penalty_multiplier_friction(getParam<Real>("penalty_multiplier_friction")),
83  _adaptivity_friction(
84  getParam<MooseEnum>("adaptivity_penalty_friction").getEnum<AdaptivityFrictionalPenalty>()),
85  _epsilon_tolerance(1.0e-40)
86 
87 {
88  if (!_augmented_lagrange_problem == isParamValid("slip_tolerance"))
89  paramError("slip_tolerance",
90  "This parameter must be supplied if and only if an augmented Lagrange problem "
91  "object is used.");
92 }
93 
94 const VariableTestValue &
96 {
98 }
99 
100 const ADVariableValue &
102 {
104 }
105 
106 const ADVariableValue &
108 {
110 }
111 
112 void
114 {
115  // these functions do not call WeightedGapUserObject::timestepSetup to avoid double initialization
118 
119  // instead we call it explicitly here
121 
122  // Clear step slip (values used in between AL iterations for penalty adaptivity)
123  for (auto & map_pr : _dof_to_step_slip)
124  {
125  auto & [step_slip, old_step_slip] = map_pr.second;
126  old_step_slip = {0.0, 0.0};
127  step_slip = {0.0, 0.0};
128  }
129 
130  // save off accumulated slip from the last timestep
131  for (auto & map_pr : _dof_to_accumulated_slip)
132  {
133  auto & [accumulated_slip, old_accumulated_slip] = map_pr.second;
134  old_accumulated_slip = accumulated_slip;
135  }
136 
137  for (auto & dof_lp : _dof_to_local_penalty_friction)
138  dof_lp.second = _penalty_friction;
139 
140  // save off tangential traction from the last timestep
141  for (auto & map_pr : _dof_to_tangential_traction)
142  {
143  auto & [tangential_traction, old_tangential_traction] = map_pr.second;
144  old_tangential_traction = {MetaPhysicL::raw_value(tangential_traction(0)),
145  MetaPhysicL::raw_value(tangential_traction(1))};
146  tangential_traction = {0.0, 0.0};
147  }
148 
149  for (auto & [dof_object, delta_tangential_lm] : _dof_to_frictional_lagrange_multipliers)
150  delta_tangential_lm.setZero();
151 }
152 
153 void
155 {
156  // these functions do not call WeightedGapUserObject::initialize to avoid double initialization
159 
160  // instead we call it explicitly here
162 }
163 
164 Real
166  const unsigned int component) const
167 {
168  const auto it = _dof_to_tangential_traction.find(_subproblem.mesh().nodePtr(node->id()));
169 
170  if (it != _dof_to_tangential_traction.end())
171  return MetaPhysicL::raw_value(it->second.first(component));
172  else
173  return 0.0;
174 }
175 
176 Real
178  const unsigned int component) const
179 {
180  const auto it = _dof_to_accumulated_slip.find(_subproblem.mesh().nodePtr(node->id()));
181 
182  if (it != _dof_to_accumulated_slip.end())
183  return MetaPhysicL::raw_value(it->second.first(component));
184  else
185  return 0.0;
186 }
187 
188 Real
190  const unsigned int component) const
191 {
192  const auto it = _dof_to_real_tangential_velocity.find(_subproblem.mesh().nodePtr(node->id()));
193 
194  if (it != _dof_to_real_tangential_velocity.end())
195  return MetaPhysicL::raw_value(it->second[component]);
196  else
197  return 0.0;
198 }
199 
200 Real
202  const unsigned int component) const
203 {
204  const auto it =
206 
208  return MetaPhysicL::raw_value(it->second[component]);
209  else
210  return 0.0;
211 }
212 
213 void
215 {
216  // Normal contact pressure with penalty
218 
219  // Reset frictional pressure
222  for (const auto qp : make_range(_qrule_msm->n_points()))
223  {
226  }
227 
228  // zero vector
229  const static TwoVector zero{0.0, 0.0};
230 
231  // iterate over nodes
232  for (const auto i : make_range(_test->size()))
233  {
234  // current node
235  const Node * const node = _lower_secondary_elem->node_ptr(i);
236 
237  const auto penalty_friction = findValue(
238  _dof_to_local_penalty_friction, static_cast<const DofObject *>(node), _penalty_friction);
239 
240  // utilized quantities
241  const auto & normal_pressure = _dof_to_normal_pressure[node];
242 
243  // map the tangential traction and accumulated slip
244  auto & [tangential_traction, old_tangential_traction] = _dof_to_tangential_traction[node];
245  auto & [accumulated_slip, old_accumulated_slip] = _dof_to_accumulated_slip[node];
246 
247  Real normal_lm = -1;
249  normal_lm = libmesh_map_find(_dof_to_lagrange_multiplier, node);
250 
251  // Keep active set fixed from second Uzawa loop
252  if (normal_lm < -TOLERANCE && normal_pressure > TOLERANCE)
253  {
254  using namespace std;
255 
256  const auto & real_tangential_velocity =
257  libmesh_map_find(_dof_to_real_tangential_velocity, node);
258  const ADTwoVector slip_distance = {real_tangential_velocity[0] * _dt,
259  real_tangential_velocity[1] * _dt};
260 
261  // frictional lagrange multiplier (delta lambda^(k)_T)
262  const auto & tangential_lm =
264 
265  // tangential trial traction (Simo 3.12)
266  // Modified for implementation in MOOSE: Avoid pingponging on frictional sign (max. 0.4
267  // capacity)
268  ADTwoVector inner_iteration_penalty_friction = penalty_friction * slip_distance;
269 
270  const auto slip_metric = MetaPhysicL::raw_value(slip_distance).cwiseAbs().norm();
271 
272  if (slip_metric > _epsilon_tolerance &&
273  penalty_friction * slip_distance.norm() >
274  0.4 * _friction_coefficient * std::abs(normal_pressure))
275  {
276  inner_iteration_penalty_friction =
277  MetaPhysicL::raw_value(0.4 * _friction_coefficient * std::abs(normal_pressure) /
278  (penalty_friction * slip_distance.norm())) *
279  penalty_friction * slip_distance;
280  }
281 
282  ADTwoVector tangential_trial_traction =
283  old_tangential_traction + tangential_lm + inner_iteration_penalty_friction;
284 
285  // Nonlinearity below
286  ADReal tangential_trial_traction_norm = tangential_trial_traction.norm();
287  ADReal phi_trial = tangential_trial_traction_norm - _friction_coefficient * normal_pressure;
288  tangential_traction = tangential_trial_traction;
289 
290  // Simo considers this a 'return mapping'; we are just capping friction to the Coulomb limit.
291  if (phi_trial > 0.0)
292  // Simo 3.13/3.14 (the penalty formulation has an error in the paper)
293  tangential_traction -=
294  phi_trial * tangential_trial_traction / tangential_trial_traction_norm;
295 
296  // track accumulated slip for output purposes
297  accumulated_slip = old_accumulated_slip + MetaPhysicL::raw_value(slip_distance).cwiseAbs();
298 
299  // Keep track of slip vector for adaptive penalty
300  auto & [step_slip, old_step_slip] = _dof_to_step_slip[node];
301  step_slip = MetaPhysicL::raw_value(slip_distance);
302  }
303  else
304  {
305  // reset slip and clear traction
306  accumulated_slip.setZero();
307  tangential_traction.setZero();
308  }
309 
310  // Now that we have consistent nodal frictional values, create an interpolated frictional
311  // pressure variable.
312  const auto & test_i = (*_test)[i];
313  for (const auto qp : make_range(_qrule_msm->n_points()))
314  {
315  _frictional_contact_traction_one[qp] += test_i[qp] * tangential_traction(0);
316  _frictional_contact_traction_two[qp] += test_i[qp] * tangential_traction(1);
317  }
318  }
319 }
320 
321 void
323 {
326 }
327 
328 bool
330 {
331  // save off step slip
332  // This method is called at the beginning of the AL iteration.
333  for (auto & map_pr : _dof_to_step_slip)
334  {
335  auto & [step_slip, old_step_slip] = map_pr.second;
336  old_step_slip = step_slip;
337  }
338 
339  std::pair<Real, dof_id_type> max_slip{0.0, 0};
340 
341  for (const auto & [dof_object, traction_pair] : _dof_to_tangential_traction)
342  {
343  const auto & tangential_traction = traction_pair.first;
344  auto normal_pressure = _dof_to_normal_pressure[dof_object];
345 
346  // We may not find a node in the map because AL machinery gets called at different system
347  // configurations. That is, we may want to find a key from a node that computed a zero traction
348  // on the verge of not projecting. Since, when we computed the velocity, the system was at a
349  // slightly different configuration, that node may not a computed physical weighted velocity.
350  // This doesn't seem an issue at all as non-projecting nodes should be a corner case not
351  // affecting the physics.
352  TwoVector slip_velocity = {0.0, 0.0};
354  {
355  const auto & real_tangential_velocity =
356  libmesh_map_find(_dof_to_real_tangential_velocity, dof_object);
357  slip_velocity = {MetaPhysicL::raw_value(real_tangential_velocity[0]),
358  MetaPhysicL::raw_value(real_tangential_velocity[1])};
359  }
360 
361  // Check slip/stick
362  if (_friction_coefficient * normal_pressure < tangential_traction.norm() * (1 + TOLERANCE))
363  {
364  // If it's slipping, any slip distance is physical.
365  }
366  else if (slip_velocity.norm() * _dt > _slip_tolerance && normal_pressure > TOLERANCE)
367  {
368  const auto new_slip =
369  std::make_pair<Real, dof_id_type>(slip_velocity.norm() * _dt, dof_object->id());
370  if (new_slip > max_slip)
371  max_slip = new_slip;
372  }
373  }
374 
375  // Communicate max_slip here where all ranks get to
376  this->_communicator.max(max_slip);
377 
378  // Check normal contact convergence now, to make sure all ranks get here
380  return false;
381 
382  // Did we observe any above tolerance slip anywhere?
383  if (max_slip.first > _slip_tolerance)
384  {
385  if (this->_communicator.rank() == 0)
386  {
387  mooseInfoRepeated("Stick tolerance fails. Slip distance for sticking node ",
388  max_slip.second,
389  " is: ",
390  max_slip.first,
391  ", but slip tolerance is chosen to be ",
393  }
394  return false;
395  }
396 
397  return true;
398 }
399 
400 void
402 {
404 
405  for (auto & [dof_object, tangential_lm] : _dof_to_frictional_lagrange_multipliers)
406  {
407  auto & penalty_friction = _dof_to_local_penalty_friction[dof_object];
408  if (penalty_friction == 0.0)
409  penalty_friction = _penalty_friction;
410 
411  // normal quantities
412  const auto & normal_lm = libmesh_map_find(_dof_to_lagrange_multiplier, dof_object);
413 
414  // tangential quantities
415  const auto & real_tangential_velocity =
416  libmesh_map_find(_dof_to_real_tangential_velocity, dof_object);
417  const TwoVector slip_velocity = {MetaPhysicL::raw_value(real_tangential_velocity[0]),
418  MetaPhysicL::raw_value(real_tangential_velocity[1])};
419 
420  auto & [tangential_traction, old_tangential_traction] = _dof_to_tangential_traction[dof_object];
421 
422  const TwoVector tangential_trial_traction =
423  old_tangential_traction + tangential_lm + penalty_friction * slip_velocity * _dt;
424  const Real tangential_trial_traction_norm = tangential_trial_traction.norm();
425 
426  // Augment
427  if (tangential_trial_traction_norm * (1 + TOLERANCE) <=
428  std::abs(_friction_coefficient * normal_lm))
429  {
430  tangential_lm += penalty_friction * slip_velocity * _dt;
431  }
432  else
433  {
434  tangential_lm = -tangential_trial_traction / tangential_trial_traction_norm *
435  penalty_friction * normal_lm -
436  old_tangential_traction;
437  }
438 
439  // Update penalty.
441  {
442  if (_slip_tolerance < _dt * slip_velocity.norm())
443  penalty_friction *= _penalty_multiplier_friction;
444 
445  // Provide the user the ability of setting this maximum penalty
446  if (penalty_friction > _penalty_friction * _max_penalty_multiplier)
447  penalty_friction = _penalty_friction * _max_penalty_multiplier;
448  }
450  {
451  const auto & step_slip = libmesh_map_find(_dof_to_step_slip, dof_object);
452  // No change of direction: Adjust penalty factor for the frictional problem
453  if (step_slip.first.dot(step_slip.second) > 0.0 && std::abs(normal_lm) > TOLERANCE &&
454  _slip_tolerance < _dt * slip_velocity.norm())
455  {
456  penalty_friction =
457  (_friction_coefficient * std::abs(normal_lm)) / 2 / (_dt * slip_velocity.norm());
458  // Alternative: accumulated_slip.norm() - old_accumulated_slip.norm()
459  }
460  // Change of direction: Reduce penalty factor to avoid lack of convergence
461  else if (step_slip.first.dot(step_slip.second) < 0.0)
462  penalty_friction /= 2.0;
463 
464  // Heuristics to bound the penalty factor
465  if (penalty_friction < _penalty_friction)
466  penalty_friction = _penalty_friction;
467  else if (penalty_friction > _penalty_friction * _max_penalty_multiplier)
468  penalty_friction = _penalty_friction * _max_penalty_multiplier;
469  }
470  }
471 }
ADVariableValue _frictional_contact_traction_one
The first frictional contact pressure on the mortar segment quadrature points.
virtual void finalize() override
const Real _max_penalty_multiplier
Maximum multiplier applied to the initial penalty factor in AL.
virtual MooseMesh & mesh()=0
ADVariableValue _frictional_contact_traction_two
The second frictional contact pressure on the mortar segment quadrature points.
std::unordered_map< const DofObject *, std::pair< TwoVector, TwoVector > > _dof_to_accumulated_slip
Map from degree of freedom to current and old accumulated slip.
virtual Real getAccumulatedSlip(const Node *const node, const unsigned int component) const override
const Real _epsilon_tolerance
Tolerance to avoid NaN/Inf in automatic differentiation operations.
virtual void updateAugmentedLagrangianMultipliers() override
std::unordered_map< const DofObject *, ADReal > _dof_to_normal_pressure
Map from degree of freedom to normal pressure for reporting.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
virtual bool isAugmentedLagrangianConverged() override
V findValue(const std::unordered_map< K, V > &map, const K &key, const V &default_value=0) const
Find a value in a map or return a default if the key doesn&#39;t exist.
static const std::string component
Definition: NS.h:153
AugmentedLagrangianContactProblemInterface *const _augmented_lagrange_problem
augmented Lagrange problem and iteration number
std::unordered_map< const DofObject *, std::array< ADReal, 2 > > _dof_to_real_tangential_velocity
A map from node to two interpolated, physical tangential velocities.
virtual const ADVariableValue & contactTangentialPressureDirTwo() const override
const Real & _dt
Current delta t... or timestep size.
virtual void updateAugmentedLagrangianMultipliers() override
virtual void initialize() override
auto raw_value(const Eigen::Map< T > &in)
AdaptivityFrictionalPenalty
The adaptivity method for the penalty factor at augmentations.
void mooseInfoRepeated(Args &&... args)
processor_id_type rank() const
PenaltyFrictionUserObject(const InputParameters &parameters)
std::unordered_map< const DofObject *, TwoVector > _dof_to_frictional_lagrange_multipliers
Map from degree of freedom to augmented lagrange multiplier.
std::unordered_map< const DofObject *, std::pair< ADTwoVector, TwoVector > > _dof_to_tangential_traction
Map from degree of freedom to current and old tangential traction.
User object for computing weighted gaps and contact pressure for penalty based mortar constraints...
const Real _penalty_multiplier_friction
Penalty growth factor for augmented Lagrange.
const Parallel::Communicator & _communicator
Creates dof object to weighted tangential velocities map.
const Number zero
DualNumber< Real, DNDerivativeType, true > ADReal
virtual void timestepSetup() override
const libMesh::QBase *const & _qrule_msm
void addRequiredParam(const std::string &name, const std::string &doc_string)
SubProblem & _subproblem
std::unordered_map< const DofObject *, std::pair< TwoVector, TwoVector > > _dof_to_step_slip
Map from degree of freedom to current and old step slip.
bool isParamValid(const std::string &name) const
User object that computes tangential pressures due to friction using a penalty approach, following J.C.
Elem const *const & _lower_secondary_elem
std::unordered_map< const DofObject *, Real > _dof_to_local_penalty_friction
Map from degree of freedom to local friction penalty value.
enum PenaltyFrictionUserObject::AdaptivityFrictionalPenalty _adaptivity_friction
virtual void timestepSetup()
OutputTools< Real >::VariableTestValue VariableTestValue
const VariableTestValue * _test
A pointer to the test function associated with the weighted gap.
virtual const Node * nodePtr(const dof_id_type i) const
const Real _friction_coefficient
The friction coefficient.
unsigned int n_points() const
virtual Real getDeltaTangentialLagrangeMultiplier(const Node *const node, const unsigned int component) const override
virtual const ADVariableValue & contactTangentialPressureDirOne() const override
void paramError(const std::string &param, Args... args) const
virtual bool isAugmentedLagrangianConverged() override
const Real _slip_tolerance
Acceptable slip distance for augmented Lagrange convergence.
const Real _penalty_friction
The penalty factor for the frictional constraints.
static InputParameters validParams()
Creates dof object to weighted gap map.
virtual Real getFrictionalContactPressure(const Node *const node, const unsigned int component) const override
const MooseVariable *const _aux_lm_var
The auxiliary Lagrange multiplier variable (used together whith the Petrov-Galerkin approach) ...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void max(const T &r, T &o, Request &req) const
virtual const FieldVariablePhiValue & phiLower() const override
std::unordered_map< const DofObject *, Real > _dof_to_lagrange_multiplier
Map from degree of freedom to augmented lagrange multiplier.
IntRange< T > make_range(T beg, T end)
void resize(unsigned int size)
registerMooseObject("ContactApp", PenaltyFrictionUserObject)
virtual const VariableTestValue & test() const override
void addClassDescription(const std::string &doc_string)
A two-component zero initialized vector used for tangential quantities.
Definition: TwoVector.h:19
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
const MooseVariable *const _disp_x_var
The x displacement variable.
virtual void initialize() override
virtual Real getTangentialVelocity(const Node *const node, const unsigned int component) const override