https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ComputeDynamicFrictionalForceLMMechanicalContact.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 "DisplacedProblem.h"
12#include "Assembly.h"
13#include "Function.h"
14#include "MortarContactUtils.h"
16
17#include "metaphysicl/metaphysicl_version.h"
18#include "metaphysicl/dualsemidynamicsparsenumberarray.h"
19#include "metaphysicl/parallel_dualnumber.h"
20#if METAPHYSICL_MAJOR_VERSION < 2
21#include "metaphysicl/parallel_dynamic_std_array_wrapper.h"
22#else
23#include "metaphysicl/parallel_dynamic_array_wrapper.h"
24#endif
25#include "metaphysicl/parallel_semidynamicsparsenumberarray.h"
26#include "timpi/parallel_sync.h"
27
28#include <limits>
29
31
34{
36 params.addClassDescription("Computes the tangential frictional forces for dynamic simulations");
37 params.addRequiredCoupledVar("friction_lm", "The frictional Lagrange's multiplier");
38 params.addCoupledVar("friction_lm_dir",
39 "The frictional Lagrange's multiplier for an addtional direction.");
40 params.addParam<FunctionName>(
41 "function_friction",
42 "Coupled function to evaluate friction with values from contact pressure and relative "
43 "tangential velocities (from the previous step).");
44 params.addParam<Real>("c_t", 1e0, "Numerical parameter for tangential constraints");
45 params.addParam<Real>(
46 "epsilon",
47 1.0e-7,
48 "Minimum value of contact pressure that will trigger frictional enforcement");
49 params.addRangeCheckedParam<Real>(
50 "mu", "mu > 0", "The friction coefficient for the Coulomb friction law");
51 MooseEnum friction_projection_degree("ONE TWO", "TWO");
52 friction_projection_degree.addDocumentation(
53 "ONE", "Use the degree-one Alart-Curnier friction residual.");
54 friction_projection_degree.addDocumentation(
55 "TWO", "Use the degree-two Hueber-Stadler-Wohlmuth friction residual.");
56 params.addParam<MooseEnum>(
57 "friction_projection_degree",
58 friction_projection_degree,
59 "Degree of the friction-residual projection; see MortarContactUtils.h.");
60 return params;
61}
62
64 const InputParameters & parameters)
66 _c_t(getParam<Real>("c_t")),
67 _friction_projection_degree(getParam<MooseEnum>("friction_projection_degree")
68 .getEnum<Moose::Mortar::Contact::FrictionProjectionDegree>()),
69 _secondary_x_dot(_secondary_var.adUDot()),
70 _primary_x_dot(_primary_var.adUDotNeighbor()),
71 _secondary_y_dot(adCoupledDot("disp_y")),
72 _primary_y_dot(adCoupledNeighborValueDot("disp_y")),
73 _secondary_z_dot(_has_disp_z ? &adCoupledDot("disp_z") : nullptr),
74 _primary_z_dot(_has_disp_z ? &adCoupledNeighborValueDot("disp_z") : nullptr),
75 _epsilon(getParam<Real>("epsilon")),
76 _mu(isParamValid("mu") ? getParam<Real>("mu") : std::numeric_limits<double>::quiet_NaN()),
77 _function_friction(isParamValid("function_friction") ? &getFunction("function_friction")
78 : nullptr),
79 _has_friction_function(isParamValid("function_friction")),
80 _3d(_has_disp_z)
81{
84 "A coefficient of friction needs to be provided as a constant value of via a function.");
85
87 paramError("mu",
88 "Either provide a constant coefficient of friction or a function defining the "
89 "coefficient of friction. Both inputs cannot be provided simultaneously.");
90
91 if (!getParam<bool>("use_displaced_mesh"))
92 paramError("use_displaced_mesh",
93 "'use_displaced_mesh' must be true for the "
94 "ComputeFrictionalForceLMMechanicalContact object");
95
96 if (_3d && !isParamValid("friction_lm_dir"))
97 paramError("friction_lm_dir",
98 "Three-dimensional mortar frictional contact simulations require an additional "
99 "frictional Lagrange's multiplier to enforce a second tangential pressure");
100
101 _friction_vars.push_back(getVar("friction_lm", 0));
102
103 if (_3d)
104 _friction_vars.push_back(getVar("friction_lm_dir", 0));
105
106 if (!_friction_vars[0]->isNodal())
107 if (_friction_vars[0]->feType().order != static_cast<Order>(0))
109 "friction_lm",
110 "Frictional contact constraints only support elemental variables of CONSTANT order");
111
112 // Request the old solution state in unison
114}
115
116void
118{
119 // Compute the value of _qp_gap
121
122 // It appears that the relative velocity between weighted gap and this class have a sign
123 // difference
125}
126
127void
129{
130 // Get the _dof_to_weighted_gap map
132
133 const auto & nodal_tangents = amg().getNodalTangents(*_lower_secondary_elem);
134
135 // Get the _dof_to_weighted_tangential_velocity map
136 const DofObject * const dof =
137 _friction_vars[0]->isNodal()
138 ? cast_ptr<const DofObject *>(_lower_secondary_elem->node_ptr(_i))
139 : cast_ptr<const DofObject *>(_lower_secondary_elem);
140
142 _test[_i][_qp] * _qp_tangential_velocity_nodal * nodal_tangents[0][_i];
143
144 // Get the _dof_to_weighted_tangential_velocity map for a second direction
145 if (_3d)
146 {
148 _test[_i][_qp] * _qp_tangential_velocity_nodal * nodal_tangents[1][_i];
149 }
150}
151
152void
159
160void
162{
163
165
166 // The base class sets _retried_timestep to indicate whether this call is for a retried
167 // timestep (e.g. --test-restep or a rejected step); if so, the history below has already been
168 // advanced from the accepted state and must not be advanced again.
170 return;
171
173
174 // The nodal slip rate is the weighted tangential velocity divided by the integral of the same
175 // test function, which is the nodal coefficient of the mortar projection of the relative
176 // tangential velocity (Wohlmuth 2011, eq. 3.10a)
177 for (const auto & [dof, weighted_velocities] : _dof_to_weighted_tangential_velocity)
178 {
179 // Only the owning process holds the fully summed values
180 if (dof->processor_id() != this->processor_id())
181 continue;
182
183 const Real normalization = libmesh_map_find(_dof_to_weighted_gap, dof).second;
185 {MetaPhysicL::raw_value(weighted_velocities[0]) / normalization,
186 _3d ? MetaPhysicL::raw_value(weighted_velocities[1]) / normalization : 0.0}};
187 }
188}
189
190void
192{
194
197
198 // Enforce frictional complementarity constraints
199 for (const auto & pr : _dof_to_weighted_tangential_velocity)
200 {
201 const DofObject * const dof = pr.first;
202
203 if (dof->processor_id() != this->processor_id())
204 continue;
205
206 // Use always weighted gap for dynamic PDASS. Omit the dynamic weighted gap approach that is
207 // used in normal contact where the discretized gap velocity is enforced if a node has
208 // identified to be into contact.
209
212 _tangential_vel_ptr[0] = &(pr.second[0]);
213
214 if (_3d)
215 {
216 _tangential_vel_ptr[1] = &(pr.second[1]);
218 }
219 else
221 }
222}
223
224void
226 const std::unordered_set<const Node *> & inactive_lm_nodes)
227{
229
232
233 // Enforce frictional complementarity constraints
234 for (const auto & pr : _dof_to_weighted_tangential_velocity)
235 {
236 const DofObject * const dof = pr.first;
237
238 // If node inactive, skip
239 if ((inactive_lm_nodes.find(static_cast<const Node *>(dof)) != inactive_lm_nodes.end()) ||
240 (dof->processor_id() != this->processor_id()))
241 continue;
242
243 // Use always weighted gap for dynamic PDASS
246 _tangential_vel_ptr[0] = &pr.second[0];
247
248 if (_3d)
249 {
250 _tangential_vel_ptr[1] = &pr.second[1];
252 }
253 else
255 }
256}
257
258void
260 const DofObject * const dof)
261{
262 // Get normal LM
263 const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
264 const ADReal & weighted_gap = *_weighted_gap_ptr;
265 ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
266 Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
267
268 // Get friction LMs
269 std::array<const ADReal *, 2> & tangential_vel = _tangential_vel_ptr;
270 std::array<dof_id_type, 2> friction_dof_indices;
271 std::array<ADReal, 2> friction_lm_values;
272
273 const unsigned int num_tangents = 2;
274 for (const auto i : make_range(num_tangents))
275 {
276 friction_dof_indices[i] = dof->dof_number(_sys.number(), _friction_vars[i]->number(), 0);
277 friction_lm_values[i] = (*_sys.currentSolution())(friction_dof_indices[i]);
278 Moose::derivInsert(friction_lm_values[i].derivatives(), friction_dof_indices[i], 1.);
279 }
280
281 // Get normalized c and c_t values (if normalization specified
282 const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
283 const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
284
285 const Real contact_pressure_old = _sys.solutionOld()(normal_dof_index);
286
287 // Compute the friction coefficient (constant or function)
288 const auto old_slip_rate = oldNodalSlipRate(dof);
289 ADReal mu_ad = computeFrictionValue(contact_pressure_old, old_slip_rate[0], old_slip_rate[1]);
290
291 const std::array<ADReal, 2> tangential_velocity{{*tangential_vel[0], *tangential_vel[1]}};
292
293 const auto residual =
295 tangential_velocity,
296 ADReal(c_t),
297 ADReal(_dt),
298 contact_pressure,
299 c * weighted_gap,
300 mu_ad,
303 const ADReal dof_residual = residual[0];
304 const ADReal dof_residual_dir = residual[1];
305
307 std::array<ADReal, 1>{{dof_residual}},
308 std::array<dof_id_type, 1>{{friction_dof_indices[0]}},
309 _friction_vars[0]->scalingFactor());
311 std::array<ADReal, 1>{{dof_residual_dir}},
312 std::array<dof_id_type, 1>{{friction_dof_indices[1]}},
313 _friction_vars[1]->scalingFactor());
314}
315
316void
318 const DofObject * const dof)
319{
320 // Get friction LM
321 const auto friction_dof_index = dof->dof_number(_sys.number(), _friction_vars[0]->number(), 0);
322 const ADReal & tangential_vel = *_tangential_vel_ptr[0];
323 ADReal friction_lm_value = (*_sys.currentSolution())(friction_dof_index);
324 Moose::derivInsert(friction_lm_value.derivatives(), friction_dof_index, 1.);
325
326 // Get normal LM
327 const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
328 const ADReal & weighted_gap = *_weighted_gap_ptr;
329 ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
330 Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
331
332 const Real contact_pressure_old = _sys.solutionOld()(normal_dof_index);
333
334 // Get normalized c and c_t values (if normalization specified
335 const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
336 const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
337
338 // Compute the friction coefficient (constant or function)
339 ADReal mu_ad = computeFrictionValue(contact_pressure_old, oldNodalSlipRate(dof)[0], 0.0);
340
341 const std::array<ADReal, 1> tangential_pressure{{friction_lm_value}};
342 const std::array<ADReal, 1> tangential_velocity{{tangential_vel}};
343
344 const ADReal dof_residual =
346 tangential_velocity,
347 ADReal(c_t),
348 ADReal(_dt),
349 contact_pressure,
350 c * weighted_gap,
351 mu_ad,
354
356 std::array<ADReal, 1>{{dof_residual}},
357 std::array<dof_id_type, 1>{{friction_dof_index}},
358 _friction_vars[0]->scalingFactor());
359}
360
361ADReal
363 const ADReal & contact_pressure, const Real & tangential_vel, const Real & tangential_vel_dir)
364{
365 using std::sqrt;
366
367 // TODO: Introduce temperature dependence in the function. Do this when we have an example.
368 ADReal mu_ad;
369
371 mu_ad = _mu;
372 else
373 {
374 ADReal tangential_vel_magnitude =
375 sqrt(tangential_vel * tangential_vel + tangential_vel_dir * tangential_vel_dir + 1.0e-24);
376
377 mu_ad = _function_friction->value<ADReal>(0.0, contact_pressure, tangential_vel_magnitude, 0.0);
378 }
379
380 return mu_ad;
381}
382
383std::array<Real, 2>
385 const DofObject * const dof) const
386{
387 mooseAssert(dof->processor_id() == this->processor_id(),
388 "Slip rate history is only stored for locally owned dofs");
389
390 // A dof has no history on the first step or when it just entered the mortar interface; it has
391 // not slipped yet
392 const auto it = _dof_to_old_real_tangential_velocity.find(dof);
393 return it != _dof_to_old_real_tangential_velocity.end() ? it->second
394 : std::array<Real, 2>{{0.0, 0.0}};
395}
DualNumber< Real, DNDerivativeType, true > ADReal
registerMooseObject("ContactApp", ComputeDynamicFrictionalForceLMMechanicalContact)
std::array< MooseUtils::SemidynamicVector< Point, 9 >, 2 > getNodalTangents(const Elem &secondary_elem) const
Computes the mortar tangential frictional forces for dynamic simulations.
const bool _has_friction_function
Boolean to determine whether the friction coefficient is taken from a function.
ADReal computeFrictionValue(const ADReal &contact_pressure, const Real &tangential_vel, const Real &tangential_vel_dir)
Apply constant or function-based friction coefficient.
void incorrectEdgeDroppingPost(const std::unordered_set< const Node * > &inactive_lm_nodes) override
Copy of the post routine but that skips assembling inactive nodes.
virtual void computeQpIProperties() override
Computes properties that are functions both of _qp and _i, for example the weighted gap.
virtual void enforceConstraintOnDof(const DofObject *const dof) override
Method called from post().
virtual void computeQpProperties() override
Computes properties that are functions only of the current quadrature point (_qp),...
std::unordered_map< const DofObject *, std::array< ADReal, 2 > > _dof_to_weighted_tangential_velocity
A map from node to two weighted tangential velocities.
const Real _epsilon
Minimum value of contact pressure that will trigger including tangential forces in the contact residu...
std::vector< MooseVariable * > _friction_vars
Frictional Lagrange's multiplier variable pointers.
std::array< Real, 2 > oldNodalSlipRate(const DofObject *const dof) const
Nodal slip rates of the previous step at a locally owned dof, or zero if the dof has no history yet (...
ADRealVectorValue _qp_tangential_velocity_nodal
The value of the tangential velocity vectors at the current node.
std::array< const ADReal *, 2 > _tangential_vel_ptr
An array of two pointers to avoid copies.
const Moose::Mortar::Contact::FrictionProjectionDegree _friction_projection_degree
Degree of the friction-residual projection used by frictionalContactResidual (see MortarContactUtils....
virtual void enforceConstraintOnDof3d(const DofObject *const dof)
Method called from post().
bool _3d
Automatic flag to determine whether we are doing three-dimensional work.
const Real _c_t
Numerical factor used in the tangential constraints for convergence purposes.
std::unordered_map< const DofObject *, std::array< Real, 2 > > _dof_to_old_real_tangential_velocity
A map from node to the two nodal slip rates of the previous step, used to evaluate the friction funct...
Computes the normal contact mortar constraints for dynamic simulations.
std::unordered_map< const DofObject *, std::pair< ADReal, Real > > _dof_to_weighted_gap
A map from node to weighted gap and normalization (if requested)
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),...
const bool _nodal
Whether the dof objects are nodal; if they're not, then they're elemental.
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.
unsigned int _qp
unsigned int _i
MooseVariable * getVar(const std::string &var_name, unsigned int comp)
virtual Real value(Real t, const Point &p) const
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)
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
void mooseError(Args &&... args) const
bool isParamValid(const std::string &name) const
unsigned int number() const
MooseVariable *const _var
const MooseArray< Real > & _coord
const VariableTestValue & _test
Elem const *const & _lower_secondary_elem
const AutomaticMortarGeneration & amg() const
const std::vector< Real > & _JxW_msm
bool isNodal() const
MooseMesh & _mesh
Assembly & _assembly
SystemBase & _sys
virtual const NumericVector< Number > *const & currentSolution() const=0
unsigned int number() const
NumericVector< Number > & solutionOld()
void addResidualsAndJacobian(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
const Parallel::Communicator & _communicator
processor_id_type processor_id() const
auto raw_value(const Eigen::Map< T > &in)
std::array< T, N > frictionalContactResidual(const std::array< T, N > &tangential_pressure, const std::array< T, N > &tangential_velocity, const T &c_t, const T &dt, const T &normal_pressure, const T &scaled_normal_gap, const T &friction_coefficient, const T &epsilon, const FrictionProjectionDegree projection_degree)
Compute the epsilon-gated frictional residual for a mortar contact node.
void communicateVelocities(std::unordered_map< const DofObject *, T > &dof_map, const MooseMesh &mesh, const bool nodal, const Parallel::Communicator &communicator, const bool send_data_back)
This function is used to communicate velocities across processes.
void derivInsert(SemiDynamicSparseNumberArray< Real, libMesh::dof_id_type, NWrapper< N > > &derivs, libMesh::dof_id_type index, Real value)