https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ComputeFrictionalForceCartesianLMMechanicalContact.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#include "metaphysicl/metaphysicl_version.h"
16#include "metaphysicl/dualsemidynamicsparsenumberarray.h"
17#include "metaphysicl/parallel_dualnumber.h"
18#if METAPHYSICL_MAJOR_VERSION < 2
19#include "metaphysicl/parallel_dynamic_std_array_wrapper.h"
20#else
21#include "metaphysicl/parallel_dynamic_array_wrapper.h"
22#endif
23#include "metaphysicl/parallel_semidynamicsparsenumberarray.h"
24#include "timpi/parallel_sync.h"
25
27
28namespace
29{
30const InputParameters &
31assignVarsInParamsFriction(const InputParameters & params_in)
32{
33 InputParameters & ret = const_cast<InputParameters &>(params_in);
34 const auto & disp_x_name = ret.get<std::vector<VariableName>>("disp_x");
35 if (disp_x_name.size() != 1)
36 mooseError("We require that the disp_x parameter have exactly one coupled name");
37
38 // We do this so we don't get any variable errors during MortarConstraint(Base) construction
39 ret.set<VariableName>("secondary_variable") = disp_x_name[0];
40 ret.set<VariableName>("primary_variable") = disp_x_name[0];
41
42 return ret;
43}
44}
45
48{
50 params.addClassDescription("Computes mortar frictional forces.");
51 params.addParam<Real>("c_t", 1e0, "Numerical parameter for tangential constraints");
52 params.addParam<Real>(
53 "epsilon",
54 1.0e-7,
55 "Minimum value of contact pressure that will trigger frictional enforcement");
56 params.addRangeCheckedParam<Real>(
57 "mu", "mu > 0", "The friction coefficient for the Coulomb friction law");
58 MooseEnum friction_projection_degree("ONE TWO", "TWO");
59 friction_projection_degree.addDocumentation(
60 "ONE", "Use the degree-one Alart-Curnier friction residual.");
61 friction_projection_degree.addDocumentation(
62 "TWO", "Use the degree-two Hueber-Stadler-Wohlmuth friction residual.");
63 params.addParam<MooseEnum>(
64 "friction_projection_degree",
65 friction_projection_degree,
66 "Degree of the friction-residual projection; see MortarContactUtils.h.");
67 return params;
68}
69
72 : ComputeWeightedGapCartesianLMMechanicalContact(assignVarsInParamsFriction(parameters)),
73 _c_t(getParam<Real>("c_t")),
74 _friction_projection_degree(getParam<MooseEnum>("friction_projection_degree")
75 .getEnum<Moose::Mortar::Contact::FrictionProjectionDegree>()),
76 _secondary_x_dot(adCoupledDot("disp_x")),
77 _primary_x_dot(adCoupledNeighborValueDot("disp_x")),
78 _secondary_y_dot(adCoupledDot("disp_y")),
79 _primary_y_dot(adCoupledNeighborValueDot("disp_y")),
80 _secondary_z_dot(_has_disp_z ? &adCoupledDot("disp_z") : nullptr),
81 _primary_z_dot(_has_disp_z ? &adCoupledNeighborValueDot("disp_z") : nullptr),
82 _mu(getParam<Real>("mu")),
83 _epsilon(getParam<Real>("epsilon"))
84{
85}
86
87void
89{
91
92 // Trim derivatives
93 const auto & primary_ip_lowerd_map = amg().getPrimaryIpToLowerElementMap(
95 const auto & secondary_ip_lowerd_map =
97
98 std::array<const MooseVariable *, 3> var_array{{_disp_x_var, _disp_y_var, _disp_z_var}};
99 std::array<ADReal, 3> primary_disp_dot{
100 {_primary_x_dot[_qp], _primary_y_dot[_qp], _has_disp_z ? (*_primary_z_dot)[_qp] : 0}};
101 std::array<ADReal, 3> secondary_disp_dot{
102 {_secondary_x_dot[_qp], _secondary_y_dot[_qp], _has_disp_z ? (*_secondary_z_dot)[_qp] : 0}};
103
104 trimInteriorNodeDerivatives(primary_ip_lowerd_map, var_array, primary_disp_dot, false);
105 trimInteriorNodeDerivatives(secondary_ip_lowerd_map, var_array, secondary_disp_dot, true);
106
107 const ADReal & prim_x_dot = primary_disp_dot[0];
108 const ADReal & prim_y_dot = primary_disp_dot[1];
109 const ADReal * prim_z_dot = nullptr;
110 if (_has_disp_z)
111 prim_z_dot = &primary_disp_dot[2];
112
113 const ADReal & sec_x_dot = secondary_disp_dot[0];
114 const ADReal & sec_y_dot = secondary_disp_dot[1];
115 const ADReal * sec_z_dot = nullptr;
116 if (_has_disp_z)
117 sec_z_dot = &secondary_disp_dot[2];
118
119 // Build relative velocity vector
120 ADRealVectorValue relative_velocity;
121
122 if (_has_disp_z)
123 relative_velocity = {sec_x_dot - prim_x_dot, sec_y_dot - prim_y_dot, *sec_z_dot - *prim_z_dot};
124 else
125 relative_velocity = {sec_x_dot - prim_x_dot, sec_y_dot - prim_y_dot, 0.0};
126
127 _qp_tangential_velocity_nodal = relative_velocity * (_JxW_msm[_qp] * _coord[_qp]);
128}
129
130void
132{
134
135 const auto & nodal_tangents = amg().getNodalTangents(*_lower_secondary_elem);
136 // Get the _dof_to_weighted_tangential_velocity map
137 const DofObject * const dof =
138 _lm_vars[0]->isNodal() ? 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 (_has_disp_z)
147 _test[_i][_qp] * _qp_tangential_velocity_nodal * nodal_tangents[1][_i];
148}
149
150void
156
157void
159{
164
165 // Enforce frictional complementarity constraints
166 for (const auto & pr : _dof_to_weighted_tangential_velocity)
167 {
168 const DofObject * const dof = pr.first;
169
170 if (dof->processor_id() != this->processor_id())
171 continue;
172
173 auto & weighted_gap_pr = _dof_to_weighted_gap[dof];
174 _weighted_gap_ptr = &weighted_gap_pr.first;
175 _normalization_ptr = &weighted_gap_pr.second;
176 _tangential_vel_ptr[0] = &(pr.second[0]);
177
178 if (_has_disp_z)
179 _tangential_vel_ptr[1] = &(pr.second[1]);
180
182 }
183}
184
185void
187 const std::unordered_set<const Node *> & inactive_lm_nodes)
188{
193
194 // Enforce frictional complementarity constraints
195 for (const auto & pr : _dof_to_weighted_tangential_velocity)
196 {
197 const DofObject * const dof = pr.first;
198
199 // If node inactive, skip
200 if ((inactive_lm_nodes.find(static_cast<const Node *>(dof)) != inactive_lm_nodes.end()) ||
201 (dof->processor_id() != this->processor_id()))
202 continue;
203
206 _tangential_vel_ptr[0] = &pr.second[0];
207
208 if (_has_disp_z)
209 _tangential_vel_ptr[1] = &pr.second[1];
210
212 }
213}
214
215void
217 const DofObject * const dof)
218{
219 using std::abs, std::sqrt, std::min;
220
221 const auto & weighted_gap = *_weighted_gap_ptr;
222 const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
223 const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
224
225 const auto dof_index_x = dof->dof_number(_sys.number(), _lm_vars[0]->number(), 0);
226 const auto dof_index_y = dof->dof_number(_sys.number(), _lm_vars[1]->number(), 0);
227 const Real scaling_factor_x = _lm_vars[0]->scalingFactor();
228 const Real scaling_factor_y = _lm_vars[1]->scalingFactor();
229 Real scaling_factor_z = 1;
230
231 ADReal lm_x = (*_sys.currentSolution())(dof_index_x);
232 ADReal lm_y = (*_sys.currentSolution())(dof_index_y);
233
234 Moose::derivInsert(lm_x.derivatives(), dof_index_x, 1.);
235 Moose::derivInsert(lm_y.derivatives(), dof_index_y, 1.);
236
237 dof_id_type dof_index_z(-1);
238 ADReal lm_z;
239 if (_has_disp_z)
240 {
241 dof_index_z = dof->dof_number(_sys.number(), _lm_vars[2]->number(), 0);
242 lm_z = (*_sys.currentSolution())(dof_index_z);
243 Moose::derivInsert(lm_z.derivatives(), dof_index_z, 1.);
244 scaling_factor_z = _lm_vars[2]->scalingFactor();
245 }
246
247 ADReal normal_pressure_value =
248 lm_x * _dof_to_normal_vector[dof](0) + lm_y * _dof_to_normal_vector[dof](1);
249 ADReal tangential_pressure_value =
250 lm_x * _dof_to_tangent_vectors[dof][0](0) + lm_y * _dof_to_tangent_vectors[dof][0](1);
251
252 ADReal tangential_pressure_value_dir;
253
254 if (_has_disp_z)
255 {
256 normal_pressure_value += lm_z * _dof_to_normal_vector[dof](2);
257 tangential_pressure_value += lm_z * _dof_to_tangent_vectors[dof][0](2);
258 tangential_pressure_value_dir = lm_x * _dof_to_tangent_vectors[dof][1](0) +
259 lm_y * _dof_to_tangent_vectors[dof][1](1) +
260 lm_z * _dof_to_tangent_vectors[dof][1](2);
261 }
262
263 ADReal normal_dof_residual = min(normal_pressure_value, weighted_gap * c);
264 ADReal tangential_dof_residual;
265 ADReal tangential_dof_residual_dir;
266
267 if (!_has_disp_z)
268 {
269 const std::array<ADReal, 1> tangential_pressure{{tangential_pressure_value}};
270 const std::array<ADReal, 1> tangential_velocity{{*_tangential_vel_ptr[0]}};
271
272 tangential_dof_residual =
274 tangential_velocity,
275 ADReal(c_t),
276 ADReal(_dt),
277 normal_pressure_value,
278 c * weighted_gap,
279 ADReal(_mu),
282 }
283 else
284 {
285 const std::array<ADReal, 2> tangential_pressure{
286 {tangential_pressure_value, tangential_pressure_value_dir}};
287 const std::array<ADReal, 2> tangential_velocity{
289
290 const auto residual =
292 tangential_velocity,
293 ADReal(c_t),
294 ADReal(_dt),
295 normal_pressure_value,
296 c * weighted_gap,
297 ADReal(_mu),
300 tangential_dof_residual = residual[0];
301 tangential_dof_residual_dir = residual[1];
302 }
303
304 // Compute the friction coefficient (constant or function)
305
306 // Get index for normal constraint.
307 // We do this to get a decent Jacobian structure, which is key for the use of iterative solvers.
308 // Using old normal vector to avoid changes in the Jacobian structure within one time step
309
310 Real ny, nz;
311 // Intially, use the current normal vector
312 if (_dof_to_old_normal_vector[dof].norm() < TOLERANCE)
313 {
314 ny = _dof_to_normal_vector[dof](1);
315 nz = _dof_to_normal_vector[dof](2);
316 }
317 else
318 {
319 ny = _dof_to_old_normal_vector[dof](1);
320 nz = _dof_to_old_normal_vector[dof](2);
321 }
322
323 unsigned int component_normal = 0;
324
325 // Consider constraint orientation to improve Jacobian structure
326 const Real threshold_for_Jacobian = _has_disp_z ? 1.0 / sqrt(3.0) : 1.0 / sqrt(2.0);
327
328 if (abs(ny) > threshold_for_Jacobian)
329 component_normal = 1;
330 else if (abs(nz) > threshold_for_Jacobian)
331 component_normal = 2;
332
334 _assembly,
335 std::array<ADReal, 1>{{normal_dof_residual}},
336 std::array<dof_id_type, 1>{{component_normal == 0
337 ? dof_index_x
338 : (component_normal == 1 ? dof_index_y : dof_index_z)}},
339 component_normal == 0 ? scaling_factor_x
340 : (component_normal == 1 ? scaling_factor_y : scaling_factor_z));
341
343 _assembly,
344 std::array<ADReal, 1>{{tangential_dof_residual}},
345 std::array<dof_id_type, 1>{
346 {(component_normal == 0 || component_normal == 2) ? dof_index_y : dof_index_x}},
347 (component_normal == 0 || component_normal == 2) ? scaling_factor_y : scaling_factor_x);
348
349 if (_has_disp_z)
351 _assembly,
352 std::array<ADReal, 1>{{tangential_dof_residual_dir}},
353 std::array<dof_id_type, 1>{
354 {(component_normal == 0 || component_normal == 1) ? dof_index_z : dof_index_x}},
355 (component_normal == 0 || component_normal == 1) ? scaling_factor_z : scaling_factor_x);
356}
DualNumber< Real, DNDerivativeType, true > ADReal
registerMooseObject("ContactApp", ComputeFrictionalForceCartesianLMMechanicalContact)
void mooseError(Args &&... args)
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
std::array< MooseUtils::SemidynamicVector< Point, 9 >, 2 > getNodalTangents(const Elem &secondary_elem) const
Computes the weighted gap that will later be used to enforce the zero-penetration mechanical contact ...
ADRealVectorValue _qp_tangential_velocity_nodal
The value of the tangential velocity vectors at the current node.
void incorrectEdgeDroppingPost(const std::unordered_set< const Node * > &inactive_lm_nodes) override
Copy of the post routine but that skips assembling inactive nodes.
std::array< const ADReal *, 2 > _tangential_vel_ptr
An array of two pointers to avoid copies.
std::unordered_map< const DofObject *, std::array< ADReal, 2 > > _dof_to_weighted_tangential_velocity
A map from node to two weighted tangential velocities.
virtual void computeQpProperties() override
Computes properties that are functions only of the current quadrature point (_qp),...
const Real _epsilon
Minimum value of contact pressure that will trigger including tangential forces in the contact residu...
const ADVariableValue & _secondary_x_dot
x-velocity on the secondary face
const Moose::Mortar::Contact::FrictionProjectionDegree _friction_projection_degree
Degree of the friction-residual projection used by frictionalContactResidual (see MortarContactUtils....
const Real _c_t
Numerical factor used in the tangential constraints for convergence purposes.
const ADVariableValue & _secondary_y_dot
y-velocity on the secondary face
virtual void enforceConstraintOnDof(const DofObject *const dof) override
Method called from post().
virtual void computeQpIProperties() override
Computes properties that are functions both of _qp and _i, for example the weighted gap.
Computes the weighted gap that will later be used to enforce the zero-penetration mechanical contact ...
virtual void computeQpIProperties()
Computes properties that are functions both of _qp and _i, for example the weighted gap.
const MooseVariable *const _disp_y_var
The y displacement variable.
std::unordered_map< const DofObject *, RealVectorValue > _dof_to_normal_vector
A map from node to normal vector (2D)
std::vector< MooseVariable * > _lm_vars
Cartesian Lagrange multipliers for mechanical contact.
const ADReal * _weighted_gap_ptr
A pointer members that can be used to help avoid copying ADReals.
std::unordered_map< const DofObject *, RealVectorValue > _dof_to_old_normal_vector
A map from node to normal vector (2D) - old.
std::unordered_map< const DofObject *, std::pair< ADReal, Real > > _dof_to_weighted_gap
A map from node to weighted gap and normalization (if requested)
bool _normalize_c
Whether to normalize weighted gap by weighting function norm.
const MooseVariable *const _disp_x_var
The x displacement variable.
std::unordered_map< const DofObject *, std::array< RealVectorValue, 2 > > _dof_to_tangent_vectors
A map from node to tangent vector (2D for now)
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 MooseVariable *const _disp_z_var
The z displacement variable.
const bool _has_disp_z
For 2D mortar contact no displacement will be specified, so const pointers used.
unsigned int _qp
unsigned int _i
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 addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
const MooseArray< Real > & _coord
const VariableTestValue & _test
Elem const *const & _lower_secondary_elem
const AutomaticMortarGeneration & amg() const
Elem const *const & _lower_primary_elem
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
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 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)