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 _dof_to_step_slip(
83 declareRestartableData<std::unordered_map<dof_id_type, std::pair<TwoVector, TwoVector>>>(
84 "dof_to_step_slip")),
85 _dof_to_accumulated_slip(
86 declareRestartableData<std::unordered_map<dof_id_type, std::pair<TwoVector, TwoVector>>>(
87 "dof_to_accumulated_slip")),
88 _dof_to_tangential_traction(
89 declareRestartableData<std::unordered_map<dof_id_type, std::pair<ADTwoVector, TwoVector>>>(
90 "dof_to_tangential_traction")),
91 _dof_to_frictional_lagrange_multipliers(
92 declareRestartableData<std::unordered_map<dof_id_type, TwoVector>>(
93 "dof_to_frictional_lagrange_multipliers")),
94 _dof_to_local_penalty_friction(declareRestartableData<std::unordered_map<dof_id_type, Real>>(
95 "dof_to_local_penalty_friction")),
96 _penalty_multiplier_friction(getParam<Real>("penalty_multiplier_friction")),
97 _adaptivity_friction(
98 getParam<MooseEnum>("adaptivity_penalty_friction").getEnum<AdaptivityFrictionalPenalty>()),
99 _t_step_old_friction(declareRestartableData<int>("t_step_old_friction", 0))
100
101{
102 if (!_augmented_lagrange_problem == isParamValid("slip_tolerance"))
103 paramError("slip_tolerance",
104 "This parameter must be supplied if and only if an augmented Lagrange problem "
105 "object is used.");
106}
107
108const VariableTestValue &
113
114const ADVariableValue &
119
120const ADVariableValue &
125
126void
128{
129 // these functions do not call WeightedGapUserObject::timestepSetup to avoid double initialization
132
133 // instead we call it explicitly here
135
136 // timestepSetup() runs once per time step attempt, so on a retried timestep (e.g. --test-restep
137 // or a rejected step) the maps below hold the discarded attempt's values and keys. Restore the
138 // state saved at the start of the first attempt so the retry starts from the accepted state.
139 const bool retried_timestep = _fe_problem.timeStep() == _t_step_old_friction;
141 if (retried_timestep)
142 {
148 return;
149 }
150
151 // Clear step slip (values used in between AL iterations for penalty adaptivity)
152 for (auto & map_pr : _dof_to_step_slip)
153 {
154 auto & [step_slip, old_step_slip] = map_pr.second;
155 old_step_slip = {0.0, 0.0};
156 step_slip = {0.0, 0.0};
157 }
158
159 // save off accumulated slip from the last timestep
160 for (auto & map_pr : _dof_to_accumulated_slip)
161 {
162 auto & [accumulated_slip, old_accumulated_slip] = map_pr.second;
163 old_accumulated_slip = accumulated_slip;
164 }
165
166 for (auto & dof_lp : _dof_to_local_penalty_friction)
167 dof_lp.second = _penalty_friction;
168
169 // save off tangential traction from the last timestep
170 for (auto & map_pr : _dof_to_tangential_traction)
171 {
172 auto & [tangential_traction, old_tangential_traction] = map_pr.second;
173 old_tangential_traction = {MetaPhysicL::raw_value(tangential_traction(0)),
174 MetaPhysicL::raw_value(tangential_traction(1))};
175 tangential_traction = {0.0, 0.0};
176 }
177
178 for (auto & [dof_object, delta_tangential_lm] : _dof_to_frictional_lagrange_multipliers)
179 delta_tangential_lm.setZero();
180
186}
187
188void
190{
191 // these functions do not call WeightedGapUserObject::initialize to avoid double initialization
194
195 // instead we call it explicitly here
197}
198
199Real
201 const unsigned int component) const
202{
203 const auto it = _dof_to_tangential_traction.find(node->id());
204
205 if (it != _dof_to_tangential_traction.end())
206 return MetaPhysicL::raw_value(it->second.first(component));
207 else
208 return 0.0;
209}
210
211Real
213 const unsigned int component) const
214{
215 const auto it = _dof_to_accumulated_slip.find(node->id());
216
217 if (it != _dof_to_accumulated_slip.end())
218 return MetaPhysicL::raw_value(it->second.first(component));
219 else
220 return 0.0;
221}
222
223Real
225 const unsigned int component) const
226{
227 const auto it = _dof_to_real_tangential_velocity.find(_subproblem.mesh().nodePtr(node->id()));
228
229 if (it != _dof_to_real_tangential_velocity.end())
230 return MetaPhysicL::raw_value(it->second[component]);
231 else
232 return 0.0;
233}
234
235Real
237 const unsigned int component) const
238{
239 const auto it = _dof_to_frictional_lagrange_multipliers.find(node->id());
240
242 return MetaPhysicL::raw_value(it->second[component]);
243 else
244 return 0.0;
245}
246
247ADReal
249 const unsigned int direction) const
250{
251 mooseAssert(direction < 2, "There are at most two tangent directions.");
252 return libmesh_map_find(_dof_to_tangential_traction, node.id()).first(direction);
253}
254
255void
257{
258 // Normal contact pressure with penalty
260
261 // Reset frictional pressure
264 for (const auto qp : make_range(_qrule_msm->n_points()))
265 {
268 }
269
270 // zero vector
271 const static TwoVector zero{0.0, 0.0};
272
273 // iterate over nodes
274 for (const auto i : make_range(_test->size()))
275 {
276 // current node
277 const Node * const node = _lower_secondary_elem->node_ptr(i);
278
279 const auto penalty_friction =
281
282 // utilized quantities
283 const auto & normal_pressure = _dof_to_normal_pressure[node];
284
285 // map the tangential traction and accumulated slip
286 auto & [tangential_traction, old_tangential_traction] = _dof_to_tangential_traction[node->id()];
287 auto & [accumulated_slip, old_accumulated_slip] = _dof_to_accumulated_slip[node->id()];
288
289 Real normal_lm = -1;
290 if (_dof_to_lagrange_multiplier.find(node->id()) != _dof_to_lagrange_multiplier.end())
291 normal_lm = libmesh_map_find(_dof_to_lagrange_multiplier, node->id());
292
293 // Keep active set fixed from second Uzawa loop
294 if (normal_lm < -TOLERANCE && normal_pressure > TOLERANCE)
295 {
296 const auto & real_tangential_velocity =
297 libmesh_map_find(_dof_to_real_tangential_velocity, node);
298 const ADTwoVector slip_distance = {real_tangential_velocity[0] * _dt,
299 real_tangential_velocity[1] * _dt};
300
301 // frictional lagrange multiplier (delta lambda^(k)_T)
302 const auto & tangential_lm =
304
305 // tangential trial traction (Simo 3.12)
306 ADTwoVector tangential_trial_traction =
307 old_tangential_traction + tangential_lm + penalty_friction * slip_distance;
308
309 // Nonlinearity below
310 ADReal tangential_trial_traction_norm = tangential_trial_traction.norm();
311 ADReal phi_trial = tangential_trial_traction_norm - _friction_coefficient * normal_pressure;
312 tangential_traction = tangential_trial_traction;
313
314 // Simo considers this a 'return mapping'; we are just capping friction to the Coulomb limit.
315 if (phi_trial > 0.0)
316 // Simo 3.13/3.14 (the penalty formulation has an error in the paper)
317 tangential_traction -=
318 phi_trial * tangential_trial_traction / tangential_trial_traction_norm;
319
320 // track accumulated slip for output purposes
321 accumulated_slip = old_accumulated_slip + MetaPhysicL::raw_value(slip_distance).cwiseAbs();
322
323 // Keep track of slip vector for adaptive penalty
324 auto & [step_slip, old_step_slip] = _dof_to_step_slip[node->id()];
325 step_slip = MetaPhysicL::raw_value(slip_distance);
326 }
327 else
328 {
329 // reset slip and clear traction
330 accumulated_slip.setZero();
331 tangential_traction.setZero();
332 }
333
334 // Now that we have consistent nodal frictional values, create an interpolated frictional
335 // pressure variable.
336 const auto & test_i = (*_test)[i];
337 for (const auto qp : make_range(_qrule_msm->n_points()))
338 {
339 _frictional_contact_traction_one[qp] += test_i[qp] * tangential_traction(0);
340 _frictional_contact_traction_two[qp] += test_i[qp] * tangential_traction(1);
341 }
342 }
343}
344
345void
351
352bool
354{
355 // save off step slip
356 // This method is called at the beginning of the AL iteration.
357 for (auto & map_pr : _dof_to_step_slip)
358 {
359 auto & [step_slip, old_step_slip] = map_pr.second;
360 old_step_slip = step_slip;
361 }
362
363 std::pair<Real, dof_id_type> max_slip{0.0, 0};
364
365 for (const auto & [node_id, traction_pair] : _dof_to_tangential_traction)
366 {
367 const DofObject * const dof_object = _subproblem.mesh().nodePtr(node_id);
368 const auto & tangential_traction = traction_pair.first;
369 auto normal_pressure = _dof_to_normal_pressure[dof_object];
370
371 // We may not find a node in the map because AL machinery gets called at different system
372 // configurations. That is, we may want to find a key from a node that computed a zero traction
373 // on the verge of not projecting. Since, when we computed the velocity, the system was at a
374 // slightly different configuration, that node may not a computed physical weighted velocity.
375 // This doesn't seem an issue at all as non-projecting nodes should be a corner case not
376 // affecting the physics.
377 TwoVector slip_velocity = {0.0, 0.0};
379 {
380 const auto & real_tangential_velocity =
381 libmesh_map_find(_dof_to_real_tangential_velocity, dof_object);
382 slip_velocity = {MetaPhysicL::raw_value(real_tangential_velocity[0]),
383 MetaPhysicL::raw_value(real_tangential_velocity[1])};
384 }
385
386 // Check slip/stick
387 if (_friction_coefficient * normal_pressure < tangential_traction.norm() * (1 + TOLERANCE))
388 {
389 // If it's slipping, any slip distance is physical.
390 }
391 else if (slip_velocity.norm() * _dt > _slip_tolerance && normal_pressure > TOLERANCE)
392 {
393 const auto new_slip =
394 std::make_pair<Real, dof_id_type>(slip_velocity.norm() * _dt, dof_object->id());
395 if (new_slip > max_slip)
396 max_slip = new_slip;
397 }
398 }
399
400 // Communicate max_slip here where all ranks get to
401 this->_communicator.max(max_slip);
402
403 // Check normal contact convergence now, to make sure all ranks get here
405 return false;
406
407 // Did we observe any above tolerance slip anywhere?
408 if (max_slip.first > _slip_tolerance)
409 {
410 if (this->_communicator.rank() == 0)
411 {
412 mooseInfoRepeated("Stick tolerance fails. Slip distance for sticking node ",
413 max_slip.second,
414 " is: ",
415 max_slip.first,
416 ", but slip tolerance is chosen to be ",
418 }
419 return false;
420 }
421
422 return true;
423}
424
425void
427{
428 using std::abs;
429
431
432 for (auto & [node_id, tangential_lm] : _dof_to_frictional_lagrange_multipliers)
433 {
434 const DofObject * const dof_object = _subproblem.mesh().nodePtr(node_id);
435 auto & penalty_friction = _dof_to_local_penalty_friction[node_id];
436 if (penalty_friction == 0.0)
437 penalty_friction = _penalty_friction;
438
439 // normal quantities
440 const auto & normal_lm = libmesh_map_find(_dof_to_lagrange_multiplier, node_id);
441
442 // tangential quantities
443 const auto & real_tangential_velocity =
444 libmesh_map_find(_dof_to_real_tangential_velocity, dof_object);
445 const TwoVector slip_velocity = {MetaPhysicL::raw_value(real_tangential_velocity[0]),
446 MetaPhysicL::raw_value(real_tangential_velocity[1])};
447
448 auto & [tangential_traction, old_tangential_traction] = _dof_to_tangential_traction[node_id];
449
450 const TwoVector tangential_trial_traction =
451 old_tangential_traction + tangential_lm + penalty_friction * slip_velocity * _dt;
452 const Real tangential_trial_traction_norm = tangential_trial_traction.norm();
453
454 // Augment
455 if (tangential_trial_traction_norm * (1 + TOLERANCE) <= abs(_friction_coefficient * normal_lm))
456 {
457 tangential_lm += penalty_friction * slip_velocity * _dt;
458 }
459 else
460 {
461 // Project the total tangential traction onto the Coulomb cone
462 tangential_lm = tangential_trial_traction / tangential_trial_traction_norm *
463 abs(_friction_coefficient * normal_lm) -
464 old_tangential_traction;
465 }
466
467 // Update penalty.
469 {
470 if (_slip_tolerance < _dt * slip_velocity.norm())
471 penalty_friction *= _penalty_multiplier_friction;
472
473 // Provide the user the ability of setting this maximum penalty
474 if (penalty_friction > _penalty_friction * _max_penalty_multiplier)
475 penalty_friction = _penalty_friction * _max_penalty_multiplier;
476 }
478 {
479 const auto & step_slip = libmesh_map_find(_dof_to_step_slip, node_id);
480 // No change of direction: Adjust penalty factor for the frictional problem
481 if (step_slip.first.dot(step_slip.second) > 0.0 && abs(normal_lm) > TOLERANCE &&
482 _slip_tolerance < _dt * slip_velocity.norm())
483 {
484 penalty_friction =
485 (_friction_coefficient * abs(normal_lm)) / 2 / (_dt * slip_velocity.norm());
486 // Alternative: accumulated_slip.norm() - old_accumulated_slip.norm()
487 }
488 // Change of direction: Reduce penalty factor to avoid lack of convergence
489 else if (step_slip.first.dot(step_slip.second) < 0.0)
490 penalty_friction /= 2.0;
491
492 // Heuristics to bound the penalty factor
493 if (penalty_friction < _penalty_friction)
494 penalty_friction = _penalty_friction;
495 else if (penalty_friction > _penalty_friction * _max_penalty_multiplier)
496 penalty_friction = _penalty_friction * _max_penalty_multiplier;
497 }
498 }
499}
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)
virtual void timestepSetup() override
virtual void updateAugmentedLagrangianMultipliers() override
std::unordered_map< dof_id_type, std::pair< TwoVector, TwoVector > > & _dof_to_accumulated_slip
Map from node id to current and old accumulated slip.
std::unordered_map< dof_id_type, std::pair< ADTwoVector, TwoVector > > & _dof_to_tangential_traction
Map from node id to current and old tangential traction.
std::unordered_map< dof_id_type, TwoVector > & _dof_to_frictional_lagrange_multipliers
Map from node id to augmented lagrange multiplier.
virtual ADReal nodalTangentialPressure(const Node &node, unsigned int direction) const override
virtual const ADVariableValue & contactTangentialPressureDirTwo() const override
std::unordered_map< dof_id_type, TwoVector > _dof_to_frictional_lagrange_multipliers_start
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.
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
std::unordered_map< dof_id_type, std::pair< TwoVector, TwoVector > > _dof_to_accumulated_slip_start
const Real _slip_tolerance
Acceptable slip distance for augmented Lagrange convergence.
virtual Real getFrictionalContactPressure(const Node *const node, const unsigned int component) const override
virtual Real getTangentialVelocity(const Node *const node, const unsigned int component) const override
std::unordered_map< dof_id_type, Real > & _dof_to_local_penalty_friction
Map from node id to local friction penalty value.
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< dof_id_type, std::pair< TwoVector, TwoVector > > & _dof_to_step_slip
Map from node id to current and old step slip.
std::unordered_map< dof_id_type, Real > _dof_to_local_penalty_friction_start
virtual const VariableTestValue & test() const override
virtual Real getAccumulatedSlip(const Node *const node, const unsigned int component) const override
std::unordered_map< dof_id_type, std::pair< ADTwoVector, TwoVector > > _dof_to_tangential_traction_start
static InputParameters validParams()
AdaptivityFrictionalPenalty
The adaptivity method for the penalty factor at augmentations.
virtual bool isAugmentedLagrangianConverged() override
std::unordered_map< dof_id_type, std::pair< TwoVector, TwoVector > > _dof_to_step_slip_start
Frictional state at the start of the current timestep, restored when it is retried.
virtual const ADVariableValue & contactTangentialPressureDirOne() const override
User object for computing weighted gaps and contact pressure for penalty based mortar constraints.
virtual bool isAugmentedLagrangianConverged() override
AugmentedLagrangianContactProblemInterface *const _augmented_lagrange_problem
augmented Lagrange problem and iteration number
std::unordered_map< dof_id_type, Real > & _dof_to_lagrange_multiplier
Map from node id to augmented lagrange multiplier.
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