https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
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),
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 _t_step_old_friction(declareRestartableData<int>("t_step_old_friction", 0))
87
88{
89 if (!_augmented_lagrange_problem == isParamValid("slip_tolerance"))
90 paramError("slip_tolerance",
91 "This parameter must be supplied if and only if an augmented Lagrange problem "
92 "object is used.");
93}
94
100
101const ADVariableValue &
106
107const ADVariableValue &
112
113void
115{
116 // these functions do not call WeightedGapUserObject::timestepSetup to avoid double initialization
119
120 // instead we call it explicitly here
122
123 // timestepSetup() runs once per time step attempt, so on a retried timestep (e.g. --test-restep
124 // or a rejected step) the maps below hold the discarded attempt's values and keys. Restore the
125 // state saved at the start of the first attempt so the retry starts from the accepted state.
126 const bool retried_timestep = _fe_problem.timeStep() == _t_step_old_friction;
128 if (retried_timestep)
129 {
135 return;
136 }
137
138 // Clear step slip (values used in between AL iterations for penalty adaptivity)
139 for (auto & map_pr : _dof_to_step_slip)
140 {
141 auto & [step_slip, old_step_slip] = map_pr.second;
142 old_step_slip = {0.0, 0.0};
143 step_slip = {0.0, 0.0};
144 }
145
146 // save off accumulated slip from the last timestep
147 for (auto & map_pr : _dof_to_accumulated_slip)
148 {
149 auto & [accumulated_slip, old_accumulated_slip] = map_pr.second;
150 old_accumulated_slip = accumulated_slip;
151 }
152
153 for (auto & dof_lp : _dof_to_local_penalty_friction)
154 dof_lp.second = _penalty_friction;
155
156 // save off tangential traction from the last timestep
157 for (auto & map_pr : _dof_to_tangential_traction)
158 {
159 auto & [tangential_traction, old_tangential_traction] = map_pr.second;
160 old_tangential_traction = {MetaPhysicL::raw_value(tangential_traction(0)),
161 MetaPhysicL::raw_value(tangential_traction(1))};
162 tangential_traction = {0.0, 0.0};
163 }
164
165 for (auto & [dof_object, delta_tangential_lm] : _dof_to_frictional_lagrange_multipliers)
166 delta_tangential_lm.setZero();
167
173}
174
175void
177{
178 // these functions do not call WeightedGapUserObject::initialize to avoid double initialization
181
182 // instead we call it explicitly here
184}
185
186Real
188 const unsigned int component) const
189{
190 const auto it = _dof_to_tangential_traction.find(_subproblem.mesh().nodePtr(node->id()));
191
192 if (it != _dof_to_tangential_traction.end())
193 return MetaPhysicL::raw_value(it->second.first(component));
194 else
195 return 0.0;
196}
197
198Real
200 const unsigned int component) const
201{
202 const auto it = _dof_to_accumulated_slip.find(_subproblem.mesh().nodePtr(node->id()));
203
204 if (it != _dof_to_accumulated_slip.end())
205 return MetaPhysicL::raw_value(it->second.first(component));
206 else
207 return 0.0;
208}
209
210Real
212 const unsigned int component) const
213{
214 const auto it = _dof_to_real_tangential_velocity.find(_subproblem.mesh().nodePtr(node->id()));
215
216 if (it != _dof_to_real_tangential_velocity.end())
217 return MetaPhysicL::raw_value(it->second[component]);
218 else
219 return 0.0;
220}
221
222Real
224 const unsigned int component) const
225{
226 const auto it =
228
230 return MetaPhysicL::raw_value(it->second[component]);
231 else
232 return 0.0;
233}
234
235ADReal
237 const unsigned int direction) const
238{
239 mooseAssert(direction < 2, "There are at most two tangent directions.");
240 return libmesh_map_find(_dof_to_tangential_traction, static_cast<const DofObject *>(&node))
241 .first(direction);
242}
243
244void
246{
247 // Normal contact pressure with penalty
249
250 // Reset frictional pressure
253 for (const auto qp : make_range(_qrule_msm->n_points()))
254 {
257 }
258
259 // zero vector
260 const static TwoVector zero{0.0, 0.0};
261
262 // iterate over nodes
263 for (const auto i : make_range(_test->size()))
264 {
265 // current node
266 const Node * const node = _lower_secondary_elem->node_ptr(i);
267
268 const auto penalty_friction = findValue(
269 _dof_to_local_penalty_friction, cast_ptr<const DofObject *>(node), _penalty_friction);
270
271 // utilized quantities
272 const auto & normal_pressure = _dof_to_normal_pressure[node];
273
274 // map the tangential traction and accumulated slip
275 auto & [tangential_traction, old_tangential_traction] = _dof_to_tangential_traction[node];
276 auto & [accumulated_slip, old_accumulated_slip] = _dof_to_accumulated_slip[node];
277
278 Real normal_lm = -1;
280 normal_lm = libmesh_map_find(_dof_to_lagrange_multiplier, node);
281
282 // Keep active set fixed from second Uzawa loop
283 if (normal_lm < -TOLERANCE && normal_pressure > TOLERANCE)
284 {
285 using std::abs;
286
287 const auto & real_tangential_velocity =
288 libmesh_map_find(_dof_to_real_tangential_velocity, node);
289 const ADTwoVector slip_distance = {real_tangential_velocity[0] * _dt,
290 real_tangential_velocity[1] * _dt};
291
292 // frictional lagrange multiplier (delta lambda^(k)_T)
293 const auto & tangential_lm =
295
296 // tangential trial traction (Simo 3.12)
297 // Modified for implementation in MOOSE: Avoid pingponging on frictional sign (max. 0.4
298 // capacity)
299 ADTwoVector inner_iteration_penalty_friction = penalty_friction * slip_distance;
300
301 const auto slip_metric = MetaPhysicL::raw_value(slip_distance).cwiseAbs().norm();
302
303 if (slip_metric > _epsilon_tolerance &&
304 penalty_friction * slip_distance.norm() >
305 0.4 * _friction_coefficient * abs(normal_pressure))
306 {
307 inner_iteration_penalty_friction =
308 MetaPhysicL::raw_value(0.4 * _friction_coefficient * abs(normal_pressure) /
309 (penalty_friction * slip_distance.norm())) *
310 penalty_friction * slip_distance;
311 }
312
313 ADTwoVector tangential_trial_traction =
314 old_tangential_traction + tangential_lm + inner_iteration_penalty_friction;
315
316 // Nonlinearity below
317 ADReal tangential_trial_traction_norm = tangential_trial_traction.norm();
318 ADReal phi_trial = tangential_trial_traction_norm - _friction_coefficient * normal_pressure;
319 tangential_traction = tangential_trial_traction;
320
321 // Simo considers this a 'return mapping'; we are just capping friction to the Coulomb limit.
322 if (phi_trial > 0.0)
323 // Simo 3.13/3.14 (the penalty formulation has an error in the paper)
324 tangential_traction -=
325 phi_trial * tangential_trial_traction / tangential_trial_traction_norm;
326
327 // track accumulated slip for output purposes
328 accumulated_slip = old_accumulated_slip + MetaPhysicL::raw_value(slip_distance).cwiseAbs();
329
330 // Keep track of slip vector for adaptive penalty
331 auto & [step_slip, old_step_slip] = _dof_to_step_slip[node];
332 step_slip = MetaPhysicL::raw_value(slip_distance);
333 }
334 else
335 {
336 // reset slip and clear traction
337 accumulated_slip.setZero();
338 tangential_traction.setZero();
339 }
340
341 // Now that we have consistent nodal frictional values, create an interpolated frictional
342 // pressure variable.
343 const auto & test_i = (*_test)[i];
344 for (const auto qp : make_range(_qrule_msm->n_points()))
345 {
346 _frictional_contact_traction_one[qp] += test_i[qp] * tangential_traction(0);
347 _frictional_contact_traction_two[qp] += test_i[qp] * tangential_traction(1);
348 }
349 }
350}
351
352void
358
359bool
361{
362 // save off step slip
363 // This method is called at the beginning of the AL iteration.
364 for (auto & map_pr : _dof_to_step_slip)
365 {
366 auto & [step_slip, old_step_slip] = map_pr.second;
367 old_step_slip = step_slip;
368 }
369
370 std::pair<Real, dof_id_type> max_slip{0.0, 0};
371
372 for (const auto & [dof_object, traction_pair] : _dof_to_tangential_traction)
373 {
374 const auto & tangential_traction = traction_pair.first;
375 auto normal_pressure = _dof_to_normal_pressure[dof_object];
376
377 // We may not find a node in the map because AL machinery gets called at different system
378 // configurations. That is, we may want to find a key from a node that computed a zero traction
379 // on the verge of not projecting. Since, when we computed the velocity, the system was at a
380 // slightly different configuration, that node may not a computed physical weighted velocity.
381 // This doesn't seem an issue at all as non-projecting nodes should be a corner case not
382 // affecting the physics.
383 TwoVector slip_velocity = {0.0, 0.0};
385 {
386 const auto & real_tangential_velocity =
387 libmesh_map_find(_dof_to_real_tangential_velocity, dof_object);
388 slip_velocity = {MetaPhysicL::raw_value(real_tangential_velocity[0]),
389 MetaPhysicL::raw_value(real_tangential_velocity[1])};
390 }
391
392 // Check slip/stick
393 if (_friction_coefficient * normal_pressure < tangential_traction.norm() * (1 + TOLERANCE))
394 {
395 // If it's slipping, any slip distance is physical.
396 }
397 else if (slip_velocity.norm() * _dt > _slip_tolerance && normal_pressure > TOLERANCE)
398 {
399 const auto new_slip =
400 std::make_pair<Real, dof_id_type>(slip_velocity.norm() * _dt, dof_object->id());
401 if (new_slip > max_slip)
402 max_slip = new_slip;
403 }
404 }
405
406 // Communicate max_slip here where all ranks get to
407 this->_communicator.max(max_slip);
408
409 // Check normal contact convergence now, to make sure all ranks get here
411 return false;
412
413 // Did we observe any above tolerance slip anywhere?
414 if (max_slip.first > _slip_tolerance)
415 {
416 if (this->_communicator.rank() == 0)
417 {
418 mooseInfoRepeated("Stick tolerance fails. Slip distance for sticking node ",
419 max_slip.second,
420 " is: ",
421 max_slip.first,
422 ", but slip tolerance is chosen to be ",
424 }
425 return false;
426 }
427
428 return true;
429}
430
431void
433{
434 using std::abs;
435
437
438 for (auto & [dof_object, tangential_lm] : _dof_to_frictional_lagrange_multipliers)
439 {
440 auto & penalty_friction = _dof_to_local_penalty_friction[dof_object];
441 if (penalty_friction == 0.0)
442 penalty_friction = _penalty_friction;
443
444 // normal quantities
445 const auto & normal_lm = libmesh_map_find(_dof_to_lagrange_multiplier, dof_object);
446
447 // tangential quantities
448 const auto & real_tangential_velocity =
449 libmesh_map_find(_dof_to_real_tangential_velocity, dof_object);
450 const TwoVector slip_velocity = {MetaPhysicL::raw_value(real_tangential_velocity[0]),
451 MetaPhysicL::raw_value(real_tangential_velocity[1])};
452
453 auto & [tangential_traction, old_tangential_traction] = _dof_to_tangential_traction[dof_object];
454
455 const TwoVector tangential_trial_traction =
456 old_tangential_traction + tangential_lm + penalty_friction * slip_velocity * _dt;
457 const Real tangential_trial_traction_norm = tangential_trial_traction.norm();
458
459 // Augment
460 if (tangential_trial_traction_norm * (1 + TOLERANCE) <= abs(_friction_coefficient * normal_lm))
461 {
462 tangential_lm += penalty_friction * slip_velocity * _dt;
463 }
464 else
465 {
466 // Project the total tangential traction onto the Coulomb cone
467 tangential_lm = tangential_trial_traction / tangential_trial_traction_norm *
468 abs(_friction_coefficient * normal_lm) -
469 old_tangential_traction;
470 }
471
472 // Update penalty.
474 {
475 if (_slip_tolerance < _dt * slip_velocity.norm())
476 penalty_friction *= _penalty_multiplier_friction;
477
478 // Provide the user the ability of setting this maximum penalty
479 if (penalty_friction > _penalty_friction * _max_penalty_multiplier)
480 penalty_friction = _penalty_friction * _max_penalty_multiplier;
481 }
483 {
484 const auto & step_slip = libmesh_map_find(_dof_to_step_slip, dof_object);
485 // No change of direction: Adjust penalty factor for the frictional problem
486 if (step_slip.first.dot(step_slip.second) > 0.0 && abs(normal_lm) > TOLERANCE &&
487 _slip_tolerance < _dt * slip_velocity.norm())
488 {
489 penalty_friction =
490 (_friction_coefficient * abs(normal_lm)) / 2 / (_dt * slip_velocity.norm());
491 // Alternative: accumulated_slip.norm() - old_accumulated_slip.norm()
492 }
493 // Change of direction: Reduce penalty factor to avoid lack of convergence
494 else if (step_slip.first.dot(step_slip.second) < 0.0)
495 penalty_friction /= 2.0;
496
497 // Heuristics to bound the penalty factor
498 if (penalty_friction < _penalty_friction)
499 penalty_friction = _penalty_friction;
500 else if (penalty_friction > _penalty_friction * _max_penalty_multiplier)
501 penalty_friction = _penalty_friction * _max_penalty_multiplier;
502 }
503 }
504}
DualNumber< Real, DNDerivativeType, true > ADReal
void mooseInfoRepeated(Args &&... args)
registerMooseObject("ContactApp", PenaltyFrictionUserObject)
void ErrorVector unsigned int
virtual int & timeStep() const
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
void paramError(const std::string &param, Args... args) const
bool isParamValid(const std::string &name) const
virtual const Node * nodePtr(const dof_id_type i) const
virtual const FieldVariablePhiValue & phiLower() const override
Elem const *const & _lower_secondary_elem
const libMesh::QBase *const & _qrule_msm
User object that computes tangential pressures due to friction using a penalty approach,...
virtual Real getDeltaTangentialLagrangeMultiplier(const Node *const node, const unsigned int component) const override
PenaltyFrictionUserObject(const InputParameters &parameters)
std::unordered_map< const DofObject *, Real > _dof_to_local_penalty_friction_start
virtual void timestepSetup() override
virtual void updateAugmentedLagrangianMultipliers() override
virtual ADReal nodalTangentialPressure(const Node &node, unsigned int direction) const override
virtual const ADVariableValue & contactTangentialPressureDirTwo() const override
std::unordered_map< const DofObject *, std::pair< TwoVector, TwoVector > > _dof_to_step_slip
Map from degree of freedom to current and old step slip.
const Real _epsilon_tolerance
Tolerance to avoid NaN/Inf in automatic differentiation operations.
std::unordered_map< const DofObject *, TwoVector > _dof_to_frictional_lagrange_multipliers
Map from degree of freedom to augmented lagrange multiplier.
const Real _penalty_multiplier_friction
Penalty growth factor for augmented Lagrange.
ADVariableValue _frictional_contact_traction_one
The first frictional contact pressure on the mortar segment quadrature points.
std::unordered_map< const DofObject *, std::pair< TwoVector, TwoVector > > _dof_to_step_slip_start
Frictional state at the start of the current timestep, restored when it is retried.
std::unordered_map< const DofObject *, std::pair< TwoVector, TwoVector > > _dof_to_accumulated_slip_start
std::unordered_map< const DofObject *, Real > _dof_to_local_penalty_friction
Map from degree of freedom to local friction penalty value.
ADVariableValue _frictional_contact_traction_two
The second frictional contact pressure on the mortar segment quadrature points.
const Real _friction_coefficient
The friction coefficient.
enum PenaltyFrictionUserObject::AdaptivityFrictionalPenalty _adaptivity_friction
const Real _slip_tolerance
Acceptable slip distance for augmented Lagrange convergence.
virtual Real getFrictionalContactPressure(const Node *const node, const unsigned int component) const override
std::unordered_map< const DofObject *, std::pair< ADTwoVector, TwoVector > > _dof_to_tangential_traction_start
virtual Real getTangentialVelocity(const Node *const node, const unsigned int component) const override
const Real _penalty_friction
The penalty factor for the frictional constraints.
int & _t_step_old_friction
The timestep index at which the accumulated slip and tangential traction history were last advanced.
std::unordered_map< const DofObject *, std::pair< TwoVector, TwoVector > > _dof_to_accumulated_slip
Map from degree of freedom to current and old accumulated slip.
std::unordered_map< const DofObject *, std::pair< ADTwoVector, TwoVector > > _dof_to_tangential_traction
Map from degree of freedom to current and old tangential traction.
virtual const VariableTestValue & test() const override
virtual Real getAccumulatedSlip(const Node *const node, const unsigned int component) const override
std::unordered_map< const DofObject *, TwoVector > _dof_to_frictional_lagrange_multipliers_start
static InputParameters validParams()
AdaptivityFrictionalPenalty
The adaptivity method for the penalty factor at augmentations.
virtual bool isAugmentedLagrangianConverged() override
virtual const ADVariableValue & contactTangentialPressureDirOne() const override
User object for computing weighted gaps and contact pressure for penalty based mortar constraints.
virtual bool isAugmentedLagrangianConverged() override
std::unordered_map< const DofObject *, Real > _dof_to_lagrange_multiplier
Map from degree of freedom to augmented lagrange multiplier.
AugmentedLagrangianContactProblemInterface *const _augmented_lagrange_problem
augmented Lagrange problem and iteration number
const MooseVariable *const _aux_lm_var
The auxiliary Lagrange multiplier variable (used together whith the Petrov-Galerkin approach)
const Real & _dt
Current delta t... or timestep size.
std::unordered_map< const DofObject *, ADReal > _dof_to_normal_pressure
Map from degree of freedom to normal pressure for reporting.
virtual void updateAugmentedLagrangianMultipliers() override
const Real _max_penalty_multiplier
Maximum multiplier applied to the initial penalty factor in AL.
virtual void timestepSetup()
virtual MooseMesh & mesh()=0
void max(const T &r, T &o, Request &req) const
processor_id_type rank() const
SubProblem & _subproblem
Creates dof object to weighted gap map.
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't exist.
const MooseVariable *const _disp_x_var
The x displacement variable.
FEProblemBase & _fe_problem
The base finite element problem.
virtual void initialize() override
const VariableTestValue * _test
A pointer to the test function associated with the weighted gap.
Creates dof object to weighted tangential velocities map.
std::unordered_map< const DofObject *, std::array< ADReal, 2 > > _dof_to_real_tangential_velocity
A map from node to two interpolated, physical tangential velocities.
const Parallel::Communicator & _communicator
unsigned int n_points() const
auto raw_value(const Eigen::Map< T > &in)
VariableShapeValue< true > VariableTestValue
VariableValueTempl< true > ADVariableValue