https://mooseframework.inl.gov
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 
53  return params;
54 }
55 
57  const InputParameters & parameters)
59  _weighted_velocities_uo(getUserObject<WeightedVelocitiesUserObject>("weighted_velocities_uo")),
60  _c_t(getParam<Real>("c_t")),
61  _secondary_x_dot(_secondary_var.adUDot()),
62  _primary_x_dot(_primary_var.adUDotNeighbor()),
63  _secondary_y_dot(adCoupledDot("disp_y")),
64  _primary_y_dot(adCoupledNeighborValueDot("disp_y")),
65  _secondary_z_dot(_has_disp_z ? &adCoupledDot("disp_z") : nullptr),
66  _primary_z_dot(_has_disp_z ? &adCoupledNeighborValueDot("disp_z") : nullptr),
67  _epsilon(getParam<Real>("epsilon")),
68  _mu(isParamValid("function_friction") ? std::numeric_limits<double>::quiet_NaN()
69  : getParam<Real>("mu")),
70  _function_friction(isParamValid("function_friction") ? &getFunction("function_friction")
71  : nullptr),
72  _has_friction_function(isParamValid("function_friction")),
73  _3d(_has_disp_z)
74 
75 {
77  paramError(
78  "mu",
79  "Please only provide friction either as a function or as a constant value, but not both.");
81  paramError("mu", "Please provide a value or a function for the coefficient of friction.");
82 
83  if (!getParam<bool>("use_displaced_mesh"))
84  paramError("use_displaced_mesh",
85  "'use_displaced_mesh' must be true for the "
86  "ComputeFrictionalForceLMMechanicalContact object");
87 
88  if (_3d && !isParamValid("friction_lm_dir"))
89  paramError("friction_lm_dir",
90  "Three-dimensional mortar frictional contact simulations require an additional "
91  "frictional Lagrange's multiplier to enforce a second tangential pressure");
92 
93  _friction_vars.push_back(getVar("friction_lm", 0));
94 
95  if (_3d)
96  _friction_vars.push_back(getVar("friction_lm_dir", 0));
97 
98  if (!_friction_vars[0]->isNodal())
99  if (_friction_vars[0]->feType().order != static_cast<Order>(0))
100  paramError(
101  "friction_lm",
102  "Frictional contact constraints only support elemental variables of CONSTANT order");
103 }
104 
105 void
107 {
108 }
109 
110 void
112 {
113 }
114 
115 void
117 {
118 }
119 
120 void
122 {
123  const auto & dof_to_weighted_tangential_velocity =
125 
126  const std::unordered_map<const DofObject *, std::pair<ADReal, Real>> & dof_to_weighted_gap =
128 
129  // Enforce frictional constraints
130 
131  for (const auto & [dof_object, weighted_velocities_pr] : dof_to_weighted_tangential_velocity)
132  {
133  if (dof_object->processor_id() != this->processor_id())
134  continue;
135 
136  const auto & [weighted_gap_pr, normalization] =
137  libmesh_map_find(dof_to_weighted_gap, dof_object);
138  _weighted_gap_ptr = &weighted_gap_pr;
139  _normalization_ptr = &normalization;
140  _tangential_vel_ptr[0] = &(weighted_velocities_pr[0]);
141 
142  if (_3d)
143  {
144  _tangential_vel_ptr[1] = &(weighted_velocities_pr[1]);
145  enforceConstraintOnDof3d(dof_object);
146  }
147  else
148  enforceConstraintOnDof(dof_object);
149  }
150 }
151 
152 void
154  const std::unordered_set<const Node *> & inactive_lm_nodes)
155 {
156  const auto & dof_to_weighted_tangential_velocity =
158  const auto & dof_to_weighted_gap = _weighted_gap_uo.dofToWeightedGap();
159  // Enforce frictional complementarity constraints
160  for (const auto & [dof_object, weighted_velocities_pr] : dof_to_weighted_tangential_velocity)
161  {
162  // If node inactive, skip
163  if ((inactive_lm_nodes.find(static_cast<const Node *>(dof_object)) !=
164  inactive_lm_nodes.end()) ||
165  (dof_object->processor_id() != this->processor_id()))
166  continue;
167 
168  _weighted_gap_ptr = &dof_to_weighted_gap.at(dof_object).first;
169  _normalization_ptr = &dof_to_weighted_gap.at(dof_object).second;
170  _tangential_vel_ptr[0] = &weighted_velocities_pr[0];
171 
172  if (_3d)
173  {
174  _tangential_vel_ptr[1] = &weighted_velocities_pr[1];
175  enforceConstraintOnDof3d(dof_object);
176  }
177  else
178  enforceConstraintOnDof(dof_object);
179  }
180 }
181 
182 void
184 {
185  using std::max, std::sqrt;
186 
188 
189  // Get normal LM
190  const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
191  const ADReal & weighted_gap = *_weighted_gap_ptr;
192  ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
193  Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
194 
195  // Get friction LMs
196  std::array<const ADReal *, 2> & tangential_vel = _tangential_vel_ptr;
197  std::array<dof_id_type, 2> friction_dof_indices;
198  std::array<ADReal, 2> friction_lm_values;
199 
200  const unsigned int num_tangents = 2;
201  for (const auto i : make_range(num_tangents))
202  {
203  friction_dof_indices[i] = dof->dof_number(_sys.number(), _friction_vars[i]->number(), 0);
204  friction_lm_values[i] = (*_sys.currentSolution())(friction_dof_indices[i]);
205  Moose::derivInsert(friction_lm_values[i].derivatives(), friction_dof_indices[i], 1.);
206  }
207 
208  // Get normalized c and c_t values (if normalization specified
209  const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
210  const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
211 
212  // Compute the friction coefficient (constant or function)
213  ADReal mu_ad = computeFrictionValue(contact_pressure,
216 
217  ADReal dof_residual;
218  ADReal dof_residual_dir;
219 
220  // Primal-dual active set strategy (PDASS)
221  if (contact_pressure < _epsilon)
222  {
223  dof_residual = friction_lm_values[0];
224  dof_residual_dir = friction_lm_values[1];
225  }
226  else
227  {
228  // Espilon to avoid automatic differentiation singularity
229  const Real epsilon_sqrt = 1.0e-48;
230 
231  const auto lamdba_plus_cg = contact_pressure + c * weighted_gap;
232  std::array<ADReal, 2> lambda_t_plus_ctu;
233  lambda_t_plus_ctu[0] = friction_lm_values[0] + c_t * *tangential_vel[0] * _dt;
234  lambda_t_plus_ctu[1] = friction_lm_values[1] + c_t * *tangential_vel[1] * _dt;
235 
236  const auto term_1_x = max(mu_ad * lamdba_plus_cg,
237  sqrt(lambda_t_plus_ctu[0] * lambda_t_plus_ctu[0] +
238  lambda_t_plus_ctu[1] * lambda_t_plus_ctu[1] + epsilon_sqrt)) *
239  friction_lm_values[0];
240 
241  const auto term_1_y = max(mu_ad * lamdba_plus_cg,
242  sqrt(lambda_t_plus_ctu[0] * lambda_t_plus_ctu[0] +
243  lambda_t_plus_ctu[1] * lambda_t_plus_ctu[1] + epsilon_sqrt)) *
244  friction_lm_values[1];
245 
246  const auto term_2_x = mu_ad * max(0.0, lamdba_plus_cg) * lambda_t_plus_ctu[0];
247 
248  const auto term_2_y = mu_ad * max(0.0, lamdba_plus_cg) * lambda_t_plus_ctu[1];
249 
250  dof_residual = term_1_x - term_2_x;
251  dof_residual_dir = term_1_y - term_2_y;
252  }
253 
255  std::array<ADReal, 1>{{dof_residual}},
256  std::array<dof_id_type, 1>{{friction_dof_indices[0]}},
257  _friction_vars[0]->scalingFactor());
259  std::array<ADReal, 1>{{dof_residual_dir}},
260  std::array<dof_id_type, 1>{{friction_dof_indices[1]}},
261  _friction_vars[1]->scalingFactor());
262 }
263 
264 void
266 {
267  using std::abs, std::max;
268 
270 
271  // Get friction LM
272  const auto friction_dof_index = dof->dof_number(_sys.number(), _friction_vars[0]->number(), 0);
273  const ADReal & tangential_vel = *_tangential_vel_ptr[0];
274  ADReal friction_lm_value = (*_sys.currentSolution())(friction_dof_index);
275  Moose::derivInsert(friction_lm_value.derivatives(), friction_dof_index, 1.);
276 
277  // Get normal LM
278  const auto normal_dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
279  const ADReal & weighted_gap = *_weighted_gap_ptr;
280  ADReal contact_pressure = (*_sys.currentSolution())(normal_dof_index);
281  Moose::derivInsert(contact_pressure.derivatives(), normal_dof_index, 1.);
282 
283  // Get normalized c and c_t values (if normalization specified
284  const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
285  const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
286 
287  // Compute the friction coefficient (constant or function)
288  ADReal mu_ad =
289  computeFrictionValue(contact_pressure, _dof_to_real_tangential_velocity[dof][0], 0.0);
290 
291  ADReal dof_residual;
292  // Primal-dual active set strategy (PDASS)
293  if (contact_pressure < _epsilon)
294  dof_residual = friction_lm_value;
295  else
296  {
297  const auto term_1 = max(mu_ad * (contact_pressure + c * weighted_gap),
298  abs(friction_lm_value + c_t * tangential_vel * _dt)) *
299  friction_lm_value;
300  const auto term_2 = mu_ad * max(0.0, contact_pressure + c * weighted_gap) *
301  (friction_lm_value + c_t * tangential_vel * _dt);
302 
303  dof_residual = term_1 - term_2;
304  }
305 
307  std::array<ADReal, 1>{{dof_residual}},
308  std::array<dof_id_type, 1>{{friction_dof_index}},
309  _friction_vars[0]->scalingFactor());
310 }
311 
312 ADReal
314  const ADReal & tangential_vel,
315  const ADReal & tangential_vel_dir)
316 {
317  using std::sqrt;
318 
319  // TODO: Introduce temperature dependence in the function. Do this when we have an example.
320  ADReal mu_ad;
321 
323  mu_ad = _mu;
324  else
325  {
326  ADReal tangential_vel_magnitude =
327  sqrt(tangential_vel * tangential_vel + tangential_vel_dir * tangential_vel_dir + 1.0e-24);
328  mu_ad = _function_friction->value<ADReal>(0.0, contact_pressure, tangential_vel_magnitude, 0.0);
329  }
330 
331  return mu_ad;
332 }
ComputeFrictionalForceLMMechanicalContact(const InputParameters &parameters)
virtual void enforceConstraintOnDof(const DofObject *const dof) override
Method called from post().
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
virtual const NumericVector< Number > *const & currentSolution() const=0
void addResidualsAndJacobian(Assembly &assembly, const Residuals &residuals, const Indices &dof_indices, Real scaling_factor)
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 WeightedVelocitiesUserObject & _weighted_velocities_uo
The weighted gap user object.
void paramError(const std::string &param, Args... args) const
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
unsigned int number() const
const InputParameters & parameters() const
const bool _has_friction_function
Boolean to determine whether the friction coefficient is taken from a function.
const Real _c_t
Numerical factor used in the tangential constraints for convergence purposes.
virtual void computeQpIProperties() override
Computes properties that are functions both of _qp and _i, for example the weighted gap...
MooseVariable * getVar(const std::string &var_name, unsigned int comp)
Creates dof object to weighted tangential velocities map.
Computes frictional constraints (and normal contact constraints by calling its parent object) ...
bool _3d
Automatic flag to determine whether we are doing three-dimensional work.
const Real _c
This factor multiplies the weighted gap.
const WeightedGapUserObject & _weighted_gap_uo
The weighted gap user object.
DualNumber< Real, DNDerivativeType, true > ADReal
void addRequiredParam(const std::string &name, const std::string &doc_string)
auto max(const L &left, const R &right)
SystemBase & _sys
const std::unordered_map< const DofObject *, std::array< ADReal, 2 > > & dofToWeightedVelocities() const
Get the degree of freedom to weighted velocities information.
std::vector< MooseVariable * > _friction_vars
Frictional Lagrange&#39;s multiplier variable pointers.
const ADReal * _weighted_gap_ptr
A pointer members that can be used to help avoid copying ADReals.
unsigned int number() const
const std::unordered_map< const DofObject *, std::pair< ADReal, Real > > & dofToWeightedGap() const
Get the degree of freedom to weighted gap information.
const Real _epsilon
Small contact pressure value to trigger computation of frictional forces.
Assembly & _assembly
bool isNodal() const
void addCoupledVar(const std::string &name, const std::string &doc_string)
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
bool isParamSetByUser(const std::string &name) const
MooseVariable *const _var
const bool _normalize_c
Whether to normalize weighted gap by weighting function norm.
ADReal computeFrictionValue(const ADReal &contact_pressure, const ADReal &tangential_vel, const ADReal &tangential_vel_dir)
Compute coefficient of friction.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
IntRange< T > make_range(T beg, T end)
registerMooseObject("ContactApp", ComputeFrictionalForceLMMechanicalContact)
void addClassDescription(const std::string &doc_string)
virtual void enforceConstraintOnDof(const DofObject *const dof)
Method called from post().
void derivInsert(SemiDynamicSparseNumberArray< Real, libMesh::dof_id_type, NWrapper< N >> &derivs, libMesh::dof_id_type index, Real value)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
bool isParamValid(const std::string &name) const
virtual void computeQpProperties() override
Computes properties that are functions only of the current quadrature point (_qp), e.g.
virtual Real value(Real t, const Point &p) const
processor_id_type processor_id() const
void incorrectEdgeDroppingPost(const std::unordered_set< const Node *> &inactive_lm_nodes) override
Copy of the post routine but that skips assembling inactive nodes.
Computes the weighted gap that will later be used to enforce the zero-penetration mechanical contact ...
std::array< const ADReal *, 2 > _tangential_vel_ptr
An array of two pointers to avoid copies.
virtual void enforceConstraintOnDof3d(const DofObject *const dof)
Method called from post().