https://mooseframework.inl.gov
Loading...
Searching...
No Matches
PenaltyWeightedGapUserObject.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
13#include "MooseVariableFE.h"
14#include "SystemBase.h"
15
17
19
22{
24 // The weighted-gap object supplies penalty contact's nodal-normal point-neighbor matrix coupling.
26 params.suppressParameter<bool>("ghost_point_neighbors");
27 params.addClassDescription("Computes the mortar normal contact force via a penalty approach.");
28 params.addRequiredParam<Real>("penalty", "The penalty factor");
29 params.addRangeCheckedParam<Real>(
30 "penalty_multiplier",
31 1.0,
32 "penalty_multiplier > 0",
33 "The penalty growth factor between augmented Lagrange "
34 "iterations if weighted gap does not get closed fast enough. For frictional simulations, "
35 "values smaller than 100 are recommended, e.g. 5.");
36 params.addParam<bool>(
37 "use_physical_gap",
38 false,
39 "Whether to compute the normal pressure as (penalty / A) * (physical gap) instead of penalty "
40 "* (weighted gap), where A is the lower-dimensional area assigned to the node on the "
41 "contacting boundary. This parameter is defaulted to 'true' in the contact action.");
42 params.addRangeCheckedParam<Real>(
43 "penetration_tolerance",
44 "penetration_tolerance > 0",
45 "Acceptable penetration distance at which augmented Lagrange iterations can be stopped");
46 params.addRangeCheckedParam<Real>(
47 "max_penalty_multiplier",
48 1.0e3,
49 "max_penalty_multiplier >= 1.0",
50 "Maximum multiplier applied to penalty factors when adaptivity is used in an augmented "
51 "Lagrange setting. The penalty factor supplied by the user is used as a reference.");
52 MooseEnum adaptivity_penalty_normal("SIMPLE BUSSETTA", "SIMPLE");
53 adaptivity_penalty_normal.addDocumentation(
54 "SIMPLE", "Keep multiplying by the penalty multiplier between AL iterations");
55 adaptivity_penalty_normal.addDocumentation(
56 "BUSSETTA",
57 "Modify the penalty using an algorithm from Bussetta et al, 2012, Comput Mech 49:259-275 "
58 "between AL iterations.");
59 params.addParam<MooseEnum>(
60 "adaptivity_penalty_normal",
61 adaptivity_penalty_normal,
62 "The augmented Lagrange update strategy used on the normal penalty coefficient.");
63 params.addCoupledVar(
64 "aux_lm",
65 "Auxiliary variable that is utilized together with the "
66 "penalty approach to interpolate the resulting contact tractions using dual bases.");
67 params.addParamNamesToGroup("penalty_multiplier penetration_tolerance max_penalty_multiplier",
68 "Augmented Lagrange");
69
70 return params;
71}
72
74 : WeightedGapUserObject(parameters),
76 _penalty(getParam<Real>("penalty")),
77 _penalty_multiplier(getParam<Real>("penalty_multiplier")),
78 _penetration_tolerance(
79 isParamValid("penetration_tolerance") ? getParam<Real>("penetration_tolerance") : 0.0),
80 _augmented_lagrange_problem(
81 dynamic_cast<AugmentedLagrangianContactProblemInterface *>(&_fe_problem)),
82 _lagrangian_iteration_number(_augmented_lagrange_problem
83 ? _augmented_lagrange_problem->getLagrangianIterationNumber()
84 : _no_iterations),
85 _dof_to_lagrange_multiplier(declareRestartableData<std::unordered_map<dof_id_type, Real>>(
86 "dof_to_lagrange_multiplier")),
87 _t_step_old_normal(declareRestartableData<int>("t_step_old_normal", 0)),
88 _dt(_fe_problem.dt()),
89 _use_physical_gap(getParam<bool>("use_physical_gap")),
90 _aux_lm_var(isCoupled("aux_lm") ? getVar("aux_lm", 0) : nullptr),
91 _max_penalty_multiplier(getParam<Real>("max_penalty_multiplier")),
92 _adaptivity_normal(
93 getParam<MooseEnum>("adaptivity_penalty_normal").getEnum<AdaptivityNormalPenalty>())
94{
95 auto check_type = [this](const auto & var, const auto & var_name)
96 {
97 if (!var.isNodal())
98 paramError(var_name,
99 "The displacement variables must have degrees of freedom exclusively on "
100 "nodes, e.g. they should probably be of finite element type 'Lagrange'.");
101 };
102 check_type(*_disp_x_var, "disp_x");
103 check_type(*_disp_y_var, "disp_y");
104 if (_has_disp_z)
105 check_type(*_disp_z_var, "disp_z");
106
107 if (!_augmented_lagrange_problem == isParamValid("penetration_tolerance"))
108 paramError("penetration_tolerance",
109 "This parameter must be supplied if and only if an augmented Lagrange problem "
110 "object is used.");
111
112 // The penalty pressure is applied on every process contributing to a node's weighted gap (see
113 // constrainedByOwner()), so with point-neighbor coupling those processes need the couplings of
114 // nonlocal secondary elements. Cohesive zone models derive from this class and disable
115 // point-neighbor coupling.
116 if (getParam<bool>("ghost_point_neighbors"))
120 getParam<bool>("use_displaced_mesh"))
122}
123
124const VariableTestValue &
129
130const VariableTestValue &
132{
133 // Penalty contact interpolates traction and weighted gap with the same basis.
134 return test();
135}
136
137const ADVariableValue &
142
143void
145{
146 for (auto & dof_pn : _dof_to_normal_pressure)
147 dof_pn.second = 0.0;
148}
149
150void
156
157void
159{
160 using std::min;
161
162 // compute new normal pressure for each node
163 for (const auto & [dof_object, wgap] : _dof_to_weighted_gap)
164 {
165 auto penalty = findValue(_dof_to_local_penalty, dof_object, _penalty);
166
167 const auto lagrange_multiplier =
169
170 // keep the negative normal pressure (compressive per convention here)
171 auto normal_pressure = min(0.0, penaltyPressure(wgap, penalty) + lagrange_multiplier);
172
173 // we switch conventins here and consider positive normal pressure as compressive
174 _dof_to_normal_pressure[dof_object] = -normal_pressure;
175 }
176}
177
178void
184
185Real
187{
188 const auto it = _dof_to_normal_pressure.find(_subproblem.mesh().nodePtr(node->id()));
189
190 if (it != _dof_to_normal_pressure.end())
191 return MetaPhysicL::raw_value(it->second);
192 else
193 return 0.0;
194}
195
196Real
198{
199 const auto it = _dof_to_lagrange_multiplier.find(node->id());
200
201 if (it != _dof_to_lagrange_multiplier.end())
202 return -MetaPhysicL::raw_value(it->second);
203 else
204 return 0.0;
205}
206
207void
209{
211 for (const auto qp : make_range(_qrule_msm->n_points()))
212 _contact_pressure[qp] = 0.0;
213
214 for (const auto i : make_range(_test->size()))
215 {
216 const Node * const node = _lower_secondary_elem->node_ptr(i);
217
218 for (const auto qp : make_range(_qrule_msm->n_points()))
219 _contact_pressure[qp] += (*_test)[i][qp] * _dof_to_normal_pressure[node];
220 }
221}
222
223ADReal
225{
226 // A node only enters this map once it has received a weighted-gap contribution, so a node on a
227 // partially integrated element may be absent. Absent means no contact pressure, matching how the
228 // interpolated pressure in reinit() treats it.
229 return findValue(_dof_to_normal_pressure, cast_ptr<const DofObject *>(&node), ADReal(0));
230}
231
232void
234{
235 // Let's not clear the LMs for improved performance in
236 // nonlinear problems
237
238 // timestepSetup() runs once per time step attempt, so on a retried timestep (e.g. --test-restep
239 // or a rejected step) the LMs hold the discarded attempt's values; restore the accepted ones
240 const bool retried_timestep = _fe_problem.timeStep() == _t_step_old_normal;
242 if (retried_timestep)
244 else
246
247 // reset penalty
248 for (auto & dof_lp : _dof_to_local_penalty)
249 dof_lp.second = _penalty;
250
251 // clear previous gap
252 for (auto & dof_pg : _dof_to_previous_gap)
253 dof_pg.second = 0.0;
254
255 // save old timestep
256 _dt_old = _dt;
257}
258
259void
264
265bool
267{
268 using std::max, std::min, std::abs;
269
270 Real max_positive_gap = 0.0;
271 Real min_negative_gap = 0.0;
272
273 // Get maximum gap to ascertain whether we are converged.
274 for (auto & [dof_object, wgap] : _dof_to_weighted_gap)
275 {
276 const auto gap = physicalGap(wgap);
277 {
278 // Check condition for nodes that are active
279 if (gap < 0 || _dof_to_lagrange_multiplier[dof_object->id()] < 0.0)
280 {
281 max_positive_gap = max(max_positive_gap, gap);
282 min_negative_gap = min(min_negative_gap, gap);
283 }
284 }
285 }
286
287 // Reduce the extreme gap values on all ranks so that every rank returns the same result
288 this->_communicator.max(max_positive_gap);
289 this->_communicator.min(min_negative_gap);
290
291 // report the gap value with the largest magnitude
292 const Real reported_gap =
293 -min_negative_gap > max_positive_gap ? min_negative_gap : max_positive_gap;
294 if (abs(reported_gap) > _penetration_tolerance)
295 {
296 mooseInfoRepeated("Gap tolerance fail max_gap = ",
297 reported_gap,
298 " (gap_tol=",
300 "). Iteration number is: ",
302 ".");
303 return false;
304 }
305 else
306 mooseInfoRepeated("Gap tolerance success max_gap = ",
307 reported_gap,
308 " (gap_tol=",
310 "). Iteration number is: ",
312 ".");
313
314 return true;
315}
316
317void
319{
320 // Loop over all nodes for which a gap has been computed
321 for (auto & [dof_object, wgap] : _dof_to_weighted_gap)
322 {
323 const Real gap = physicalGap(wgap);
324 // Store previous augmented lagrange iteration gap
325 _dof_to_previous_gap[dof_object] = gap;
326 }
327}
328
329void
331{
332 using std::abs;
333
334 for (const auto & [dof_object, wgap] : _dof_to_weighted_gap)
335 {
336 auto & penalty = _dof_to_local_penalty[dof_object];
337 if (penalty == 0.0)
338 penalty = _penalty;
339
340 const auto gap = getNormalGap(static_cast<const Node *>(dof_object));
341 auto & lagrange_multiplier = _dof_to_lagrange_multiplier[dof_object->id()];
342
343 // Increment the multiplier by the penalty pressure computed in selfFinalize
344 const Real penalty_pressure = MetaPhysicL::raw_value(penaltyPressure(wgap, penalty));
345
346 // Update penalty (the factor of 1/4 is suggested in the literature, the limit on AL iteration
347 // caps the penalty increase)
348 // Before we were updating the LM before adapting the penalty factor
349 if (lagrange_multiplier + penalty_pressure <= 0)
350 lagrange_multiplier += penalty_pressure;
351 else
352 lagrange_multiplier = 0.0;
353
354 const auto previous_gap = _dof_to_previous_gap[dof_object];
355 Real eval_tn = 0;
356
358 {
359 if (abs(gap) > 0.25 * abs(previous_gap) && abs(gap) > _penetration_tolerance)
360 penalty *= _penalty_multiplier;
361 }
363 bussettaAdaptivePenalty(previous_gap, gap, penalty, eval_tn);
364
365 // Heuristics to bound the penalty factor
366 if (penalty < _penalty)
367 penalty = _penalty;
368 else if (penalty > _max_penalty_multiplier * _penalty)
370 }
371}
372
373void
375 const Real gap,
376 Real & penalty,
377 Real & eval_tn)
378{
379 // Positive gaps means no contact
380 if (previous_gap > 0.0)
381 {
382 penalty = _penalty;
383 eval_tn = 0.0;
384 }
385 else
386 {
387 if (previous_gap * gap < 0)
388 eval_tn = 0.0;
389 else
390 eval_tn = penalty * previous_gap;
391
392 adaptiveNormalPenalty(previous_gap, gap, penalty);
393 }
394}
395
396void
398 const Real gap,
399 Real & penalty)
400{
401 using std::abs, std::max, std::sqrt;
402
403 const bool condition_one = abs(abs(previous_gap / gap) - 1.0) < 0.01;
404 const bool condition_two = abs(gap) > 1.01 * abs(previous_gap);
405
406 if (previous_gap * gap < 0)
407 {
408 if (previous_gap > _penetration_tolerance)
409 penalty = abs(penalty * previous_gap / gap * (abs(gap) + _penetration_tolerance) /
410 (gap - previous_gap));
411 else
412 penalty = abs(penalty * previous_gap / 10.0 / gap);
413 }
414 else if (abs(gap) > _penetration_tolerance)
415 {
416 if (abs(gap - previous_gap) >
417 max(gap / 10.0, max(previous_gap / 10.0, 5.0 * _penetration_tolerance)))
418 penalty *= 10.0;
419 else if (condition_one && gap < 10.0 * _penetration_tolerance)
420 penalty *= MathUtils::pow(sqrt(abs(gap) / _penetration_tolerance - 1.0) + 1.0, 2);
421 else if (condition_two)
422 penalty *= 10.0 * previous_gap / gap;
423 else
424 penalty *= sqrt(abs(gap) / _penetration_tolerance - 1.0) + 1.0;
425 }
426}
DualNumber< Real, DNDerivativeType, true > ADReal
void mooseInfoRepeated(Args &&... args)
registerMooseObject("ContactApp", PenaltyWeightedGapUserObject)
void ErrorVector unsigned int
Interface class for user objects that support the augmented Lagrange formalism as implemented in Augm...
Class to provide an interface for parameters and routines required to check convergence for the augme...
void requestNonlocalSecondaryCouplings()
const AutomaticMortarGeneration & getMortarInterface(const std::pair< BoundaryID, BoundaryID > &primary_secondary_boundary_pair, const std::pair< SubdomainID, SubdomainID > &primary_secondary_subdomain_pair, bool on_displaced) const
virtual int & timeStep() const
void suppressParameter(const std::string &name)
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
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 addCoupledVar(const std::string &name, 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
const BoundaryID _primary_id
const SubdomainID _secondary_subdomain_id
const BoundaryID _secondary_id
const SubdomainID _primary_subdomain_id
User object for computing weighted gaps and contact pressure for penalty based mortar constraints.
const Real _penalty
The penalty factor.
std::unordered_map< const DofObject *, Real > _dof_to_previous_gap
Map from degree of freedom to previous AL iteration gap values.
void adaptiveNormalPenalty(const Real previous_gap, const Real gap, Real &penalty)
See Algorithm 3 of 'The adapted augmented Lagrangian method: a new method for the resolution of the m...
virtual bool isAugmentedLagrangianConverged() override
PenaltyWeightedGapUserObject(const InputParameters &parameters)
virtual ADReal nodalContactPressure(const Node &node) const override
const Real _penalty_multiplier
penalty growth factor for augmented Lagrange
std::unordered_map< const DofObject *, Real > _dof_to_local_penalty
Map from degree of freedom to local penalty value.
enum PenaltyWeightedGapUserObject::AdaptivityNormalPenalty _adaptivity_normal
virtual const VariableTestValue & tractionBasis() const override
virtual Real getNormalContactPressure(const Node *const node) const override
virtual void augmentedLagrangianSetup() override
std::unordered_map< dof_id_type, Real > _dof_to_lagrange_multiplier_start
Augmented lagrange multipliers at the start of the current timestep, restored when the timestep is re...
const Real _penetration_tolerance
penetration tolerance for augmented Lagrange contact
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.
virtual const VariableTestValue & test() const override
ADVariableValue _contact_pressure
The contact pressure on the mortar segument quadrature points.
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
virtual const ADVariableValue & contactPressure() const override
int & _t_step_old_normal
The timestep index at which _dof_to_lagrange_multiplier_start was last saved.
AdaptivityNormalPenalty
The adaptivity method for the penalty factor at augmentations.
const Real _max_penalty_multiplier
Maximum multiplier applied to the initial penalty factor in AL.
virtual Real getNormalLagrangeMultiplier(const Node *const node) const
ADReal penaltyPressure(const std::pair< ADReal, Real > &wgap, const Real penalty) const
Penalty normal pressure at a node, before the augmented Lagrange multiplier is added.
void bussettaAdaptivePenalty(const Real previous_gap, const Real gap, Real &penalty, Real &eval_tn)
Adaptive, local penalty for AL.
virtual MooseMesh & mesh()=0
void max(const T &r, T &o, Request &req) const
void min(const T &r, T &o, Request &req) const
SubProblem & _subproblem
Creates dof object to weighted gap map.
static InputParameters validParams()
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.
const bool _has_disp_z
For 2D mortar contact no displacement will be specified, so const pointers used.
virtual void finalize() override
virtual Real getNormalGap(const Node *const) const
FEProblemBase & _fe_problem
The base finite element problem.
static void addNodalNormalCoupling(InputParameters &params)
Request the secondary face point-neighbor matrix coupling that nodal-normal derivatives need.
const MooseVariable *const _disp_y_var
The y displacement variable.
std::unordered_map< const DofObject *, std::pair< ADReal, Real > > _dof_to_weighted_gap
A map from node to weighted gap and normalization (if requested)
const MooseVariable *const _disp_z_var
The z displacement variable.
virtual void initialize() override
Real physicalGap(const std::pair< ADReal, Real > &gap) const
Compute physical gap from integration gap quantity.
const VariableTestValue * _test
A pointer to the test function associated with the weighted gap.
const Parallel::Communicator & _communicator
unsigned int n_points() const
T pow(T x, int e)
auto raw_value(const Eigen::Map< T > &in)
VariableShapeValue< true > VariableTestValue
VariableValueTempl< true > ADVariableValue