https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ComputeFrictionalForceLMMechanicalContact.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 "MortarContactUtils.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
27#include <cmath>
28
30
33{
35 params.addClassDescription("Computes the tangential frictional forces");
36 params.addRequiredCoupledVar("friction_lm", "The frictional Lagrange's multiplier");
37 params.addCoupledVar("friction_lm_dir",
38 "The frictional Lagrange's multiplier for an addtional direction.");
39 params.addParam<FunctionName>(
40 "function_friction",
41 "Coupled function to evaluate friction with values from contact pressure and relative "
42 "tangential velocities");
43 params.addParam<Real>("c_t", 1e0, "Numerical parameter for tangential constraints");
44 params.addParam<Real>(
45 "epsilon",
46 1.0e-7,
47 "Minimum value of contact pressure that will trigger frictional enforcement");
48 params.addRangeCheckedParam<Real>(
49 "mu", "mu > 0", "The friction coefficient for the Coulomb friction law");
50 params.addRequiredParam<UserObjectName>("weighted_velocities_uo",
51 "The weighted tangential velocities user object.");
52 MooseEnum friction_projection_degree("ONE TWO", "TWO");
53 friction_projection_degree.addDocumentation(
54 "ONE", "Use the degree-one Alart-Curnier friction residual.");
55 friction_projection_degree.addDocumentation(
56 "TWO", "Use the degree-two Hueber-Stadler-Wohlmuth friction residual.");
57 params.addParam<MooseEnum>(
58 "friction_projection_degree",
59 friction_projection_degree,
60 "Degree of the friction-residual projection; see MortarContactUtils.h.");
61
62 return params;
63}
64
66 const InputParameters & parameters)
68 _weighted_velocities_uo(getUserObject<WeightedVelocitiesUserObject>("weighted_velocities_uo")),
69 _c_t(getParam<Real>("c_t")),
70 _friction_projection_degree(getParam<MooseEnum>("friction_projection_degree")
71 .getEnum<Moose::Mortar::Contact::FrictionProjectionDegree>()),
72 _secondary_x_dot(_secondary_var.adUDot()),
73 _primary_x_dot(_primary_var.adUDotNeighbor()),
74 _secondary_y_dot(adCoupledDot("disp_y")),
75 _primary_y_dot(adCoupledNeighborValueDot("disp_y")),
76 _secondary_z_dot(_has_disp_z ? &adCoupledDot("disp_z") : nullptr),
77 _primary_z_dot(_has_disp_z ? &adCoupledNeighborValueDot("disp_z") : nullptr),
78 _epsilon(getParam<Real>("epsilon")),
79 _mu(isParamValid("function_friction") ? std::numeric_limits<double>::quiet_NaN()
80 : getParam<Real>("mu")),
81 _function_friction(isParamValid("function_friction") ? &getFunction("function_friction")
82 : nullptr),
83 _has_friction_function(isParamValid("function_friction")),
84 _3d(_has_disp_z)
85
86{
88 paramError("weighted_velocities_uo",
89 "Nodal-normal derivatives are not supported by user object '",
91 "'.");
92
93 // The weighted-gap and weighted-velocity parameters may identify different user objects, so each
94 // object's interface and displacement configuration must be validated.
97 paramError("weighted_velocities_uo",
98 "'weighted_velocities_uo' must be defined on the same secondary/primary subdomain "
99 "pair as this constraint when nodal-normal derivatives are enabled.");
100
101 const std::array<std::pair<const MooseVariable *, unsigned int>, 3>
102 velocity_displacement_variables{{{_disp_x_var, 0}, {_disp_y_var, 1}, {_disp_z_var, 2}}};
103 for (const auto & [variable, component] : velocity_displacement_variables)
104 if (variable != _weighted_velocities_uo.dispVar(component))
105 paramError("weighted_velocities_uo",
106 "'weighted_velocities_uo' must use the same displacement variables as this "
107 "constraint when nodal-normal derivatives are enabled.");
108
111 "mu",
112 "Please only provide friction either as a function or as a constant value, but not both.");
114 paramError("mu", "Please provide a value or a function for the coefficient of friction.");
115
116 if (!getParam<bool>("use_displaced_mesh"))
117 paramError("use_displaced_mesh",
118 "'use_displaced_mesh' must be true for the "
119 "ComputeFrictionalForceLMMechanicalContact object");
120
121 if (_3d && !isParamValid("friction_lm_dir"))
122 paramError("friction_lm_dir",
123 "Three-dimensional mortar frictional contact simulations require an additional "
124 "frictional Lagrange's multiplier to enforce a second tangential pressure");
125
126 _friction_vars.push_back(getVar("friction_lm", 0));
127
128 if (_3d)
129 _friction_vars.push_back(getVar("friction_lm_dir", 0));
130
131 if (!_friction_vars[0]->isNodal())
132 if (_friction_vars[0]->feType().order != static_cast<Order>(0))
134 "friction_lm",
135 "Frictional contact constraints only support elemental variables of CONSTANT order");
136}
137
138void
142
143void
147
148void
152
153void
155{
156 const auto & dof_to_weighted_tangential_velocity =
158
159 const std::unordered_map<const DofObject *, std::pair<ADReal, Real>> & dof_to_weighted_gap =
161
162 // Enforce frictional constraints
163
164 for (const auto & [dof_object, weighted_velocities_pr] : dof_to_weighted_tangential_velocity)
165 {
166 if (dof_object->processor_id() != this->processor_id())
167 continue;
168
169 const auto & [weighted_gap_pr, normalization] =
170 libmesh_map_find(dof_to_weighted_gap, dof_object);
171 _weighted_gap_ptr = &weighted_gap_pr;
172 _normalization_ptr = &normalization;
173 _tangential_vel_ptr[0] = &(weighted_velocities_pr[0]);
174
175 if (_3d)
176 {
177 _tangential_vel_ptr[1] = &(weighted_velocities_pr[1]);
178 enforceConstraintOnDof3d(dof_object);
179 }
180 else
181 enforceConstraintOnDof(dof_object);
182 }
183}
184
185void
187 const std::unordered_set<const Node *> & inactive_lm_nodes)
188{
189 const auto & dof_to_weighted_tangential_velocity =
191 const auto & dof_to_weighted_gap = _weighted_gap_uo.dofToWeightedGap();
192 // Enforce frictional complementarity constraints
193 for (const auto & [dof_object, weighted_velocities_pr] : dof_to_weighted_tangential_velocity)
194 {
195 // If node inactive, skip
196 if ((inactive_lm_nodes.find(static_cast<const Node *>(dof_object)) !=
197 inactive_lm_nodes.end()) ||
198 (dof_object->processor_id() != this->processor_id()))
199 continue;
200
201 _weighted_gap_ptr = &dof_to_weighted_gap.at(dof_object).first;
202 _normalization_ptr = &dof_to_weighted_gap.at(dof_object).second;
203 _tangential_vel_ptr[0] = &weighted_velocities_pr[0];
204
205 if (_3d)
206 {
207 _tangential_vel_ptr[1] = &weighted_velocities_pr[1];
208 enforceConstraintOnDof3d(dof_object);
209 }
210 else
211 enforceConstraintOnDof(dof_object);
212 }
213}
214
215void
217{
219
220 // Get normal LM
221 const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
222 const ADReal & weighted_gap = *_weighted_gap_ptr;
223 ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
224 Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
225 // The stored multiplier is zhat = kappa*lambda; recover lambda for the Coulomb bound
226 contact_pressure /= _weighted_gap_uo.nodalScale(dof);
227
228 // Get friction LMs
229 std::array<const ADReal *, 2> & tangential_vel = _tangential_vel_ptr;
230 std::array<dof_id_type, 2> friction_dof_indices;
231 std::array<ADReal, 2> friction_lm_values;
232
233 const unsigned int num_tangents = 2;
234 for (const auto i : make_range(num_tangents))
235 {
236 friction_dof_indices[i] = dof->dof_number(_sys.number(), _friction_vars[i]->number(), 0);
237 friction_lm_values[i] = (*_sys.currentSolution())(friction_dof_indices[i]);
238 Moose::derivInsert(friction_lm_values[i].derivatives(), friction_dof_indices[i], 1.);
239 }
240
241 // Get normalized c and c_t values (if normalization specified
242 const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
243 const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
244
245 // Compute the friction coefficient (constant or function). The nodal slip rate is the weighted
246 // tangential velocity divided by the integral of the same test function, which is the nodal
247 // coefficient of the mortar projection of the relative tangential velocity
248 // (Wohlmuth 2011, eq. 3.10a).
249 ADReal mu_ad = computeFrictionValue(contact_pressure,
250 *tangential_vel[0] / *_normalization_ptr,
251 *tangential_vel[1] / *_normalization_ptr);
252
253 const std::array<ADReal, 2> tangential_velocity{{*tangential_vel[0], *tangential_vel[1]}};
254
255 const auto residual =
257 tangential_velocity,
258 ADReal(c_t),
259 ADReal(_dt),
260 contact_pressure,
261 c * weighted_gap,
262 mu_ad,
265 const ADReal dof_residual = residual[0];
266 const ADReal dof_residual_dir = residual[1];
267
269 std::array<ADReal, 1>{{dof_residual}},
270 std::array<dof_id_type, 1>{{friction_dof_indices[0]}},
271 _friction_vars[0]->scalingFactor());
273 std::array<ADReal, 1>{{dof_residual_dir}},
274 std::array<dof_id_type, 1>{{friction_dof_indices[1]}},
275 _friction_vars[1]->scalingFactor());
276}
277
278void
280{
282
283 // Get friction LM
284 const auto friction_dof_index = dof->dof_number(_sys.number(), _friction_vars[0]->number(), 0);
285 const ADReal & tangential_vel = *_tangential_vel_ptr[0];
286 ADReal friction_lm_value = (*_sys.currentSolution())(friction_dof_index);
287 Moose::derivInsert(friction_lm_value.derivatives(), friction_dof_index, 1.);
288
289 // Get normal LM
290 const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
291 const ADReal & weighted_gap = *_weighted_gap_ptr;
292 ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
293 Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
294 // The stored multiplier is zhat = kappa*lambda; recover lambda for the Coulomb bound
295 contact_pressure /= _weighted_gap_uo.nodalScale(dof);
296
297 // Get normalized c and c_t values (if normalization specified
298 const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
299 const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
300
301 // Compute the friction coefficient (constant or function) from the projected nodal slip rate, as
302 // in enforceConstraintOnDof3d()
303 ADReal mu_ad = computeFrictionValue(contact_pressure, tangential_vel / *_normalization_ptr, 0.0);
304
305 const std::array<ADReal, 1> tangential_pressure{{friction_lm_value}};
306 const std::array<ADReal, 1> tangential_velocity{{tangential_vel}};
307
308 const ADReal dof_residual =
310 tangential_velocity,
311 ADReal(c_t),
312 ADReal(_dt),
313 contact_pressure,
314 c * weighted_gap,
315 mu_ad,
318
320 std::array<ADReal, 1>{{dof_residual}},
321 std::array<dof_id_type, 1>{{friction_dof_index}},
322 _friction_vars[0]->scalingFactor());
323}
324
325ADReal
327 const ADReal & tangential_vel,
328 const ADReal & tangential_vel_dir)
329{
330 using std::sqrt;
331
332 // TODO: Introduce temperature dependence in the function. Do this when we have an example.
333 ADReal mu_ad;
334
336 mu_ad = _mu;
337 else
338 {
339 ADReal tangential_vel_magnitude =
340 sqrt(tangential_vel * tangential_vel + tangential_vel_dir * tangential_vel_dir + 1.0e-24);
341 mu_ad = _function_friction->value<ADReal>(0.0, contact_pressure, tangential_vel_magnitude, 0.0);
342 }
343
344 return mu_ad;
345}
DualNumber< Real, DNDerivativeType, true > ADReal
registerMooseObject("ContactApp", ComputeFrictionalForceLMMechanicalContact)
Computes frictional constraints (and normal contact constraints by calling its parent object)
std::array< const ADReal *, 2 > _tangential_vel_ptr
An array of two pointers to avoid copies.
const Real _epsilon
Minimum value of contact pressure that will trigger including tangential forces in the contact residu...
virtual void enforceConstraintOnDof3d(const DofObject *const dof)
Method called from post().
virtual void computeQpIProperties() override
Computes properties that are functions both of _qp and _i, for example the weighted gap.
ComputeFrictionalForceLMMechanicalContact(const InputParameters &parameters)
std::vector< MooseVariable * > _friction_vars
Frictional Lagrange's multiplier variable pointers.
ADReal computeFrictionValue(const ADReal &contact_pressure, const ADReal &tangential_vel, const ADReal &tangential_vel_dir)
Compute coefficient of friction.
const bool _has_friction_function
Boolean to determine whether the friction coefficient is taken from a function.
const Moose::Mortar::Contact::FrictionProjectionDegree _friction_projection_degree
Degree of the friction-residual projection used by frictionalContactResidual (see MortarContactUtils....
virtual void enforceConstraintOnDof(const DofObject *const dof) override
Method called from post().
const WeightedVelocitiesUserObject & _weighted_velocities_uo
The weighted gap user object.
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 computeQpProperties() override
Computes properties that are functions only of the current quadrature point (_qp),...
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.
Computes the weighted gap that will later be used to enforce the zero-penetration mechanical contact ...
const bool _normalize_c
Whether to normalize weighted gap by weighting function norm.
const WeightedGapUserObject & _weighted_gap_uo
The weighted gap user object.
const MooseVariable *const _disp_z_var
The z displacement variable.
const ADReal * _weighted_gap_ptr
A pointer members that can be used to help avoid copying ADReals.
virtual void enforceConstraintOnDof(const DofObject *const dof)
Method called from post().
const MooseVariable *const _disp_x_var
The x displacement variable.
const Real _c
This factor multiplies the weighted gap.
const MooseVariable *const _disp_y_var
The y displacement variable.
MooseVariable * getVar(const std::string &var_name, unsigned int comp)
virtual Real value(Real t, const Point &p) const
bool isParamSetByUser(const std::string &name) const
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
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)
const InputParameters & parameters() const
const std::string & name() const
void paramError(const std::string &param, Args... args) const
bool isParamValid(const std::string &name) const
unsigned int number() const
MooseVariable *const _var
const MooseVariable & variable() const override
SubdomainID primarySubdomain() const
SubdomainID secondarySubdomain() const
bool isNodal() const
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)
virtual bool usesNodalNormalDerivatives() const =0
Whether this formulation includes displacement derivatives of the secondary nodal normals.
const std::unordered_map< const DofObject *, std::pair< ADReal, Real > > & dofToWeightedGap() const
Get the degree of freedom to weighted gap information.
virtual Real nodalScale(const DofObject *const) const
Node-based Lagrange-multiplier scaling factor kappa_j of Popp et al.
const MooseVariable * dispVar(unsigned int component) const
Creates dof object to weighted tangential velocities map.
const std::unordered_map< const DofObject *, std::array< ADReal, 2 > > & dofToWeightedVelocities() const
Get the degree of freedom to weighted velocities information.
processor_id_type processor_id() const
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 derivInsert(SemiDynamicSparseNumberArray< Real, libMesh::dof_id_type, NWrapper< N > > &derivs, libMesh::dof_id_type index, Real value)