https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ComputeDynamicWeightedGapLMMechanicalContact.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 "MortarContactUtils.h"
12#include "DisplacedProblem.h"
13#include "Assembly.h"
15
16#include "metaphysicl/metaphysicl_version.h"
17#include "metaphysicl/dualsemidynamicsparsenumberarray.h"
18#include "metaphysicl/parallel_dualnumber.h"
19#if METAPHYSICL_MAJOR_VERSION < 2
20#include "metaphysicl/parallel_dynamic_std_array_wrapper.h"
21#else
22#include "metaphysicl/parallel_dynamic_array_wrapper.h"
23#endif
24#include "metaphysicl/parallel_semidynamicsparsenumberarray.h"
25#include "timpi/parallel_sync.h"
26
28
29namespace
30{
31const InputParameters &
32assignVarsInParamsDyn(const InputParameters & params_in)
33{
34 InputParameters & ret = const_cast<InputParameters &>(params_in);
35 const auto & disp_x_name = ret.get<std::vector<VariableName>>("disp_x");
36 if (disp_x_name.size() != 1)
37 mooseError("We require that the disp_x parameter have exactly one coupled name");
38
39 // We do this so we don't get any variable errors during MortarConstraint(Base) construction
40 ret.set<VariableName>("secondary_variable") = disp_x_name[0];
41 ret.set<VariableName>("primary_variable") = disp_x_name[0];
42
43 return ret;
44}
45}
48{
50 // Mechanical contact residuals use only primal values and normals
51 params.set<bool>("_compute_primal_gradients") = false;
53 "Computes the normal contact mortar constraints for dynamic simulations");
54 params.addRangeCheckedParam<Real>("capture_tolerance",
55 1.0e-5,
56 "capture_tolerance>=0",
57 "Parameter describing a gap threshold for the application of "
58 "the persistency constraint in dynamic simulations.");
59 params.addCoupledVar("wear_depth",
60 "The name of the mortar auxiliary variable that is used to modify the "
61 "weighted gap definition");
63 "newmark_beta", "newmark_beta > 0", "Beta parameter for the Newmark time integrator");
65 "newmark_gamma", "newmark_gamma >= 0.0", "Gamma parameter for the Newmark time integrator");
66 params.suppressParameter<VariableName>("secondary_variable");
67 params.suppressParameter<VariableName>("primary_variable");
68 params.addRequiredCoupledVar("disp_x", "The x displacement variable");
69 params.addRequiredCoupledVar("disp_y", "The y displacement variable");
70 params.addCoupledVar("disp_z", "The z displacement variable");
71 params.addParam<Real>(
72 "c", 1e6, "Parameter for balancing the size of the gap and contact pressure");
73 params.addParam<bool>(
74 "normalize_c",
75 false,
76 "Whether to normalize c by weighting function norm. When unnormalized "
77 "the value of c effectively depends on element size since in the constraint we compare nodal "
78 "Lagrange Multiplier values to integrated gap values (LM nodal value is independent of "
79 "element size, where integrated values are dependent on element size).");
80 params.set<bool>("use_displaced_mesh") = true;
81 params.set<bool>("interpolate_normals") = false;
82 return params;
83}
84
86 const InputParameters & parameters)
87 : ADMortarConstraint(assignVarsInParamsDyn(parameters)),
88 _secondary_disp_x(adCoupledValue("disp_x")),
89 _primary_disp_x(adCoupledNeighborValue("disp_x")),
90 _secondary_disp_y(adCoupledValue("disp_y")),
91 _primary_disp_y(adCoupledNeighborValue("disp_y")),
92 _has_disp_z(isCoupled("disp_z")),
93 _secondary_disp_z(_has_disp_z ? &adCoupledValue("disp_z") : nullptr),
94 _primary_disp_z(_has_disp_z ? &adCoupledNeighborValue("disp_z") : nullptr),
95 _c(getParam<Real>("c")),
96 _normalize_c(getParam<bool>("normalize_c")),
97 _nodal(getVar("disp_x", 0)->feType().family == LAGRANGE),
98 _disp_x_var(getVar("disp_x", 0)),
99 _disp_y_var(getVar("disp_y", 0)),
100 _disp_z_var(_has_disp_z ? getVar("disp_z", 0) : nullptr),
101 _capture_tolerance(getParam<Real>("capture_tolerance")),
102 _secondary_x_dot(adCoupledDot("disp_x")),
103 _primary_x_dot(adCoupledNeighborValueDot("disp_x")),
104 _secondary_y_dot(adCoupledDot("disp_y")),
105 _primary_y_dot(adCoupledNeighborValueDot("disp_y")),
106 _secondary_z_dot(_has_disp_z ? &adCoupledDot("disp_z") : nullptr),
107 _primary_z_dot(_has_disp_z ? &adCoupledNeighborValueDot("disp_z") : nullptr),
108 _has_wear(isParamValid("wear_depth")),
109 _wear_depth(_has_wear ? coupledValueLower("wear_depth") : _zero),
110 _newmark_beta(getParam<Real>("newmark_beta")),
111 _newmark_gamma(getParam<Real>("newmark_gamma")),
112 _t_step_old(declareRestartableData<int>("t_step_old", 0)),
113 _retried_timestep(false)
114{
115 if (!useDual())
116 mooseError("Dynamic mortar contact constraints requires the use of Lagrange multipliers dual "
117 "interpolation");
118}
119
120void
122{
123 // Trim interior node variable derivatives
124 const auto & primary_ip_lowerd_map = amg().getPrimaryIpToLowerElementMap(
126 const auto & secondary_ip_lowerd_map =
128
129 std::array<const MooseVariable *, 3> var_array{{_disp_x_var, _disp_y_var, _disp_z_var}};
130 std::array<ADReal, 3> primary_disp{
131 {_primary_disp_x[_qp], _primary_disp_y[_qp], _has_disp_z ? (*_primary_disp_z)[_qp] : 0}};
132 std::array<ADReal, 3> secondary_disp{{_secondary_disp_x[_qp],
134 _has_disp_z ? (*_secondary_disp_z)[_qp] : 0}};
135
136 trimInteriorNodeDerivatives(primary_ip_lowerd_map, var_array, primary_disp, false);
137 trimInteriorNodeDerivatives(secondary_ip_lowerd_map, var_array, secondary_disp, true);
138
139 const ADReal & prim_x = primary_disp[0];
140 const ADReal & prim_y = primary_disp[1];
141 const ADReal * prim_z = nullptr;
142 if (_has_disp_z)
143 prim_z = &primary_disp[2];
144
145 const ADReal & sec_x = secondary_disp[0];
146 const ADReal & sec_y = secondary_disp[1];
147 const ADReal * sec_z = nullptr;
148 if (_has_disp_z)
149 sec_z = &secondary_disp[2];
150
151 std::array<ADReal, 3> primary_disp_dot{
152 {_primary_x_dot[_qp], _primary_y_dot[_qp], _has_disp_z ? (*_primary_z_dot)[_qp] : 0}};
153 std::array<ADReal, 3> secondary_disp_dot{
154 {_secondary_x_dot[_qp], _secondary_y_dot[_qp], _has_disp_z ? (*_secondary_z_dot)[_qp] : 0}};
155
156 trimInteriorNodeDerivatives(primary_ip_lowerd_map, var_array, primary_disp_dot, false);
157 trimInteriorNodeDerivatives(secondary_ip_lowerd_map, var_array, secondary_disp_dot, true);
158
159 const ADReal & prim_x_dot = primary_disp_dot[0];
160 const ADReal & prim_y_dot = primary_disp_dot[1];
161 const ADReal * prim_z_dot = nullptr;
162 if (_has_disp_z)
163 prim_z_dot = &primary_disp_dot[2];
164
165 const ADReal & sec_x_dot = secondary_disp_dot[0];
166 const ADReal & sec_y_dot = secondary_disp_dot[1];
167 const ADReal * sec_z_dot = nullptr;
168 if (_has_disp_z)
169 sec_z_dot = &secondary_disp_dot[2];
170
171 // Compute dynamic constraint-related quantities
173 gap_vec(0).derivatives() = prim_x.derivatives() - sec_x.derivatives();
174 gap_vec(1).derivatives() = prim_y.derivatives() - sec_y.derivatives();
175
176 _relative_velocity = ADRealVectorValue(prim_x_dot - sec_x_dot, prim_y_dot - sec_y_dot, 0.0);
177
178 if (_has_disp_z)
179 {
180 gap_vec(2).derivatives() = prim_z->derivatives() - sec_z->derivatives();
181 _relative_velocity(2) = *prim_z_dot - *sec_z_dot;
182 }
183
184 _qp_gap_nodal = gap_vec * (_JxW_msm[_qp] * _coord[_qp]);
186
187 // Current part of the gap velocity Newmark-beta time discretization
189 (_newmark_gamma / _newmark_beta * gap_vec / _dt) * (_JxW_msm[_qp] * _coord[_qp]);
190
191 // To do normalization of constraint coefficient (c_n)
193}
194
195ADReal
197{
199 "We should never call computeQpResidual for ComputeDynamicWeightedGapLMMechanicalContact");
200}
201
202void
204{
205 mooseAssert(_normals.size() == _lower_secondary_elem->n_nodes(),
206 "Making sure that _normals is the expected size");
207
208 // Get the _dof_to_weighted_gap map
209 const DofObject * dof = _var->isNodal()
210 ? cast_ptr<const DofObject *>(_lower_secondary_elem->node_ptr(_i))
211 : cast_ptr<const DofObject *>(_lower_secondary_elem);
212
213 // Regular normal contact constraint: Use before contact is established for contact detection
215
216 // Integrated part of the "persistency" constraint
219
221
222 // Always accumulated: also used to compute nodal slip rates for friction
223 _dof_to_weighted_gap[dof].second += _test[_i][_qp] * _qp_factor;
224}
225
226void
228{
229 // These dof maps are not recoverable as they are maps of pointers, and recovering old pointers
230 // would be wrong. We would need to create a custom dataStore() and dataLoad()
231 if (_app.isRecovering())
232 mooseError("This object does not support recovering");
233
234 // timestepSetup() runs once per time step attempt, so on a retried timestep (e.g. --test-restep
235 // or a rejected step) the history below has already been advanced from the accepted state;
236 // advancing it again would overwrite that history with the discarded attempt's last values.
240 return;
241
243 _dof_to_old_velocity.clear();
245
246 for (auto & map_pr : _dof_to_weighted_gap)
247 _dof_to_old_weighted_gap.emplace(map_pr.first, std::move(map_pr.second.first));
248
249 for (auto & map_pr : _dof_to_velocity)
250 _dof_to_old_velocity.emplace(map_pr);
251
252 for (auto & map_pr : _dof_to_nodal_wear_depth)
253 _dof_to_nodal_old_wear_depth.emplace(map_pr);
254}
255
256void
266
267void
269{
271 _dof_to_weighted_gap, _mesh, _nodal, /*normalize_c*/ true, _communicator, false);
272
273 if (_has_wear)
275
276 // There is a need for the dynamic constraint to uncouple the computation of the weighted gap from
277 // the computation of the constraint itself since we are switching from gap constraint to
278 // persistency constraint.
279 for (const auto & pr : _dof_to_weighted_gap)
280 {
281 if (pr.first->processor_id() != this->processor_id())
282 continue;
283
284 //
285 _dof_to_weighted_gap[pr.first].first += _dof_to_nodal_wear_depth[pr.first];
288 //
289
290 const auto is_dof_on_map = _dof_to_old_weighted_gap.find(pr.first);
291
292 // If is_dof_on_map isn't on map, it means it's an initial step
293 if (is_dof_on_map == _dof_to_old_weighted_gap.end() ||
295 _weighted_gap_ptr = &pr.second.first;
296 else
297 {
299 term += _dof_to_old_velocity[pr.first];
300 _dof_to_weighted_gap_dynamics[pr.first] += term;
302 }
303
304 _normalization_ptr = &pr.second.second;
305
307 }
308}
309
310void
312 const std::unordered_set<const Node *> & inactive_lm_nodes)
313{
315 _dof_to_weighted_gap, _mesh, _nodal, /*normalize_c*/ true, _communicator, false);
316
317 if (_has_wear)
319
320 for (const auto & pr : _dof_to_weighted_gap)
321 {
322 if ((inactive_lm_nodes.find(static_cast<const Node *>(pr.first)) != inactive_lm_nodes.end()) ||
323 (pr.first->processor_id() != this->processor_id()))
324 continue;
325
326 //
327 _dof_to_weighted_gap[pr.first].first += _dof_to_nodal_wear_depth[pr.first];
330 const auto is_dof_on_map = _dof_to_old_weighted_gap.find(pr.first);
331
332 // If is_dof_on_map isn't on map, it means it's an initial step
333 if (is_dof_on_map == _dof_to_old_weighted_gap.end() ||
335 {
336 // If this is the first step or the previous step gap is not identified as in contact, apply
337 // regular conditions
338 _weighted_gap_ptr = &pr.second.first;
339 }
340 else
341 {
342 ADReal term = _dof_to_weighted_gap[pr.first].first * _newmark_gamma / (_newmark_beta * _dt);
344 term -= _dof_to_old_velocity[pr.first];
345 _dof_to_weighted_gap_dynamics[pr.first] = term;
346 // Enable the application of persistency condition
348 }
349
350 _normalization_ptr = &pr.second.second;
351
353 }
354}
355
356void
358{
359 // We may have wear depth information that should go to other processes that own the dofs
360 using Datum = std::pair<dof_id_type, ADReal>;
361 std::unordered_map<processor_id_type, std::vector<Datum>> push_data;
362
363 for (auto & pr : _dof_to_nodal_wear_depth)
364 {
365 const auto * const dof_object = pr.first;
366 const auto proc_id = dof_object->processor_id();
367 if (proc_id == this->processor_id())
368 continue;
369
370 push_data[proc_id].push_back(std::make_pair(dof_object->id(), std::move(pr.second)));
371 }
372
373 const auto & lm_mesh = _mesh.getMesh();
374
375 auto action_functor = [this, &lm_mesh](const processor_id_type libmesh_dbg_var(pid),
376 const std::vector<Datum> & sent_data)
377 {
378 mooseAssert(pid != this->processor_id(), "We do not send messages to ourself here");
379 for (auto & pr : sent_data)
380 {
381 const auto dof_id = pr.first;
382 const auto * const dof_object = _nodal
383 ? cast_ptr<const DofObject *>(lm_mesh.node_ptr(dof_id))
384 : cast_ptr<const DofObject *>(lm_mesh.elem_ptr(dof_id));
385 mooseAssert(dof_object, "This should be non-null");
386 _dof_to_nodal_wear_depth[dof_object] += std::move(pr.second);
387 }
388 };
389
390 TIMPI::push_parallel_vector_data(_communicator, push_data, action_functor);
391}
392
393void
395{
396 const auto & weighted_gap = *_weighted_gap_ptr;
397 const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
398
399 const auto dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
400 ADReal lm_value = (*_sys.currentSolution())(dof_index);
401 Moose::derivInsert(lm_value.derivatives(), dof_index, 1.);
402
403 const ADReal dof_residual = std::min(lm_value, weighted_gap * c);
404
406 std::array<ADReal, 1>{{dof_residual}},
407 std::array<dof_id_type, 1>{{dof_index}},
409}
410
411void
413{
414 if (mortar_type != Moose::MortarType::Lower)
415 return;
416
417 mooseAssert(_var, "LM variable is null");
418
419 for (_qp = 0; _qp < _qrule_msm->n_points(); _qp++)
420 {
422 for (_i = 0; _i < _test.size(); ++_i)
424 }
425}
426
427void
432
433void
435{
436 // During "computeResidual" and "computeJacobian" we are actually just computing properties on the
437 // mortar segment element mesh. We are *not* actually assembling into the residual/Jacobian. For
438 // the zero-penetration constraint, the property of interest is the map from node to weighted gap.
439 // Computation of the properties proceeds identically for residual and Jacobian evaluation hence
440 // why we simply call computeResidual here. We will assemble into the residual/Jacobian later from
441 // the post() method
442 computeResidual(mortar_type);
443}
DualNumber< Real, DNDerivativeType, true > ADReal
registerMooseObject("ContactApp", ComputeDynamicWeightedGapLMMechanicalContact)
void mooseError(Args &&... args)
void ErrorVector unsigned int
static InputParameters validParams()
std::map< unsigned int, unsigned int > getSecondaryIpToLowerElementMap(const Elem &lower_secondary_elem) const
std::map< unsigned int, unsigned int > getPrimaryIpToLowerElementMap(const Elem &primary_elem, const Elem &primary_elem_ip, const Elem &lower_secondary_elem) const
Computes the normal contact mortar constraints for dynamic simulations.
const bool _has_disp_z
For 2D mortar contact no displacement will be specified, so const pointers used.
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_y_var
The y displacement variable.
const bool _has_wear
Flag to determine whether wear needs to be included in the contact constraints.
const ADVariableValue & _secondary_disp_x
x-displacement on the secondary face
const ADVariableValue & _secondary_disp_y
y-displacement on the secondary face
std::unordered_map< const DofObject *, ADReal > _dof_to_nodal_wear_depth
A map from node to wear in this step.
const Real _capture_tolerance
A small threshold gap value to consider that a node needs a "persistency" constraint.
const ADVariableValue & _primary_disp_x
x-displacement on the primary face
std::unordered_map< const DofObject *, ADReal > _dof_to_weighted_gap_dynamics
A map from node to weighted gap velocity times _dt.
ADRealVectorValue _qp_gap_nodal
Vector for computation of weighted gap with nodal normals.
std::unordered_map< const DofObject *, ADReal > _dof_to_nodal_old_wear_depth
A map from node to wear in old step.
const MooseVariable *const _disp_x_var
The x displacement variable.
virtual void incorrectEdgeDroppingPost(const std::unordered_set< const Node * > &inactive_lm_nodes) override
bool _retried_timestep
Set by timestepSetup() to indicate whether the current call is for a retried timestep,...
virtual void computeQpProperties()
Computes properties that are functions only of the current quadrature point (_qp),...
ADRealVectorValue _qp_velocity
Vector for computation of weighted gap velocity to fulfill "persistency" condition.
const bool _nodal
Whether the dof objects are nodal; if they're not, then they're elemental.
virtual void enforceConstraintOnDof(const DofObject *const dof)
Method called from post().
const ADReal * _weighted_gap_ptr
A pointer members that can be used to help avoid copying ADReals.
bool _normalize_c
Whether to normalize weighted gap by weighting function norm.
const VariableValue & _wear_depth
Wear depth to include contact.
int & _t_step_old
The timestep index at which the history maps (_dof_to_old_weighted_gap, _dof_to_old_velocity,...
std::unordered_map< const DofObject *, ADReal > _dof_to_velocity
A map from node to weighted gap velocity times _dt.
ADRealVectorValue _qp_gap_nodal_dynamics
Vector for computation of weighted gap velocity to fulfill "persistency" condition.
std::unordered_map< const DofObject *, ADReal > _dof_to_old_weighted_gap
A map from dof-object to the old weighted gap.
Real _qp_factor
The value of the LM at the current quadrature point.
std::unordered_map< const DofObject *, ADReal > _dof_to_old_velocity
A map from node to weighted gap velocity times _dt.
const ADVariableValue & _primary_disp_y
y-displacement on the primary face
const MooseVariable *const _disp_z_var
The z displacement variable.
unsigned int _qp
unsigned int _i
void suppressParameter(const std::string &name)
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
void addRequiredCoupledVar(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)
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
void addClassDescription(const std::string &doc_string)
T & set(const std::string &name, bool quiet_mode=false)
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)
bool isRecovering() const
void mooseError(Args &&... args) const
MeshBase & getMesh()
void scalingFactor(const std::vector< Real > &factor)
unsigned int number() const
bool isNodal() const override
MooseVariable *const _var
bool useDual() const
const MooseArray< Real > & _coord
const VariableTestValue & _test
Elem const *const & _lower_secondary_elem
const libMesh::QBase *const & _qrule_msm
const AutomaticMortarGeneration & amg() const
Elem const *const & _lower_primary_elem
const MooseArray< Point > & _phys_points_secondary
const MooseArray< Point > & _phys_points_primary
std::vector< Point > _normals
static void trimInteriorNodeDerivatives(const std::map< unsigned int, unsigned int > &primary_ip_lowerd_map, const Variables &moose_var, DualNumbers &ad_vars, const bool is_secondary)
const std::vector< Real > & _JxW_msm
MooseMesh & _mesh
Assembly & _assembly
SystemBase & _sys
virtual const NumericVector< Number > *const & currentSolution() const=0
unsigned int number() const
void addResidualsAndJacobian(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
const Parallel::Communicator & _communicator
processor_id_type processor_id() const
unsigned int n_points() const
void communicateGaps(std::unordered_map< const DofObject *, std::pair< ADReal, Real > > &dof_to_weighted_gap, const MooseMesh &mesh, bool nodal, bool normalize_c, const Parallel::Communicator &communicator, bool send_data_back)
This function is used to communicate gaps across processes.
void derivInsert(SemiDynamicSparseNumberArray< Real, libMesh::dof_id_type, NWrapper< N > > &derivs, libMesh::dof_id_type index, Real value)
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)