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 return params;
59}
60
63 : ComputeWeightedGapCartesianLMMechanicalContact(assignVarsInParamsFriction(parameters)),
64 _c_t(getParam<Real>("c_t")),
65 _secondary_x_dot(adCoupledDot("disp_x")),
66 _primary_x_dot(adCoupledNeighborValueDot("disp_x")),
67 _secondary_y_dot(adCoupledDot("disp_y")),
68 _primary_y_dot(adCoupledNeighborValueDot("disp_y")),
69 _secondary_z_dot(_has_disp_z ? &adCoupledDot("disp_z") : nullptr),
70 _primary_z_dot(_has_disp_z ? &adCoupledNeighborValueDot("disp_z") : nullptr),
71 _mu(getParam<Real>("mu")),
72 _epsilon(getParam<Real>("epsilon"))
73{
74}
75
76void
78{
80
81 // Trim derivatives
82 const auto & primary_ip_lowerd_map = amg().getPrimaryIpToLowerElementMap(
84 const auto & secondary_ip_lowerd_map =
86
87 std::array<const MooseVariable *, 3> var_array{{_disp_x_var, _disp_y_var, _disp_z_var}};
88 std::array<ADReal, 3> primary_disp_dot{
89 {_primary_x_dot[_qp], _primary_y_dot[_qp], _has_disp_z ? (*_primary_z_dot)[_qp] : 0}};
90 std::array<ADReal, 3> secondary_disp_dot{
91 {_secondary_x_dot[_qp], _secondary_y_dot[_qp], _has_disp_z ? (*_secondary_z_dot)[_qp] : 0}};
92
93 trimInteriorNodeDerivatives(primary_ip_lowerd_map, var_array, primary_disp_dot, false);
94 trimInteriorNodeDerivatives(secondary_ip_lowerd_map, var_array, secondary_disp_dot, true);
95
96 const ADReal & prim_x_dot = primary_disp_dot[0];
97 const ADReal & prim_y_dot = primary_disp_dot[1];
98 const ADReal * prim_z_dot = nullptr;
99 if (_has_disp_z)
100 prim_z_dot = &primary_disp_dot[2];
101
102 const ADReal & sec_x_dot = secondary_disp_dot[0];
103 const ADReal & sec_y_dot = secondary_disp_dot[1];
104 const ADReal * sec_z_dot = nullptr;
105 if (_has_disp_z)
106 sec_z_dot = &secondary_disp_dot[2];
107
108 // Build relative velocity vector
109 ADRealVectorValue relative_velocity;
110
111 if (_has_disp_z)
112 relative_velocity = {sec_x_dot - prim_x_dot, sec_y_dot - prim_y_dot, *sec_z_dot - *prim_z_dot};
113 else
114 relative_velocity = {sec_x_dot - prim_x_dot, sec_y_dot - prim_y_dot, 0.0};
115
116 _qp_tangential_velocity_nodal = relative_velocity * (_JxW_msm[_qp] * _coord[_qp]);
117}
118
119void
121{
123
124 const auto & nodal_tangents = amg().getNodalTangents(*_lower_secondary_elem);
125 // Get the _dof_to_weighted_tangential_velocity map
126 const DofObject * const dof =
127 _lm_vars[0]->isNodal() ? static_cast<const DofObject *>(_lower_secondary_elem->node_ptr(_i))
128 : static_cast<const DofObject *>(_lower_secondary_elem);
129
131 _test[_i][_qp] * _qp_tangential_velocity_nodal * nodal_tangents[0][_i];
132
133 // Get the _dof_to_weighted_tangential_velocity map for a second direction
134 if (_has_disp_z)
136 _test[_i][_qp] * _qp_tangential_velocity_nodal * nodal_tangents[1][_i];
137}
138
139void
145
146void
148{
153
154 // Enforce frictional complementarity constraints
155 for (const auto & pr : _dof_to_weighted_tangential_velocity)
156 {
157 const DofObject * const dof = pr.first;
158
159 if (dof->processor_id() != this->processor_id())
160 continue;
161
162 auto & weighted_gap_pr = _dof_to_weighted_gap[dof];
163 _weighted_gap_ptr = &weighted_gap_pr.first;
164 _normalization_ptr = &weighted_gap_pr.second;
165 _tangential_vel_ptr[0] = &(pr.second[0]);
166
167 if (_has_disp_z)
168 _tangential_vel_ptr[1] = &(pr.second[1]);
169
171 }
172}
173
174void
176 const std::unordered_set<const Node *> & inactive_lm_nodes)
177{
182
183 // Enforce frictional complementarity constraints
184 for (const auto & pr : _dof_to_weighted_tangential_velocity)
185 {
186 const DofObject * const dof = pr.first;
187
188 // If node inactive, skip
189 if ((inactive_lm_nodes.find(static_cast<const Node *>(dof)) != inactive_lm_nodes.end()) ||
190 (dof->processor_id() != this->processor_id()))
191 continue;
192
195 _tangential_vel_ptr[0] = &pr.second[0];
196
197 if (_has_disp_z)
198 _tangential_vel_ptr[1] = &pr.second[1];
199
201 }
202}
203
204void
206 const DofObject * const dof)
207{
208 using std::abs, std::sqrt, std::max, std::min;
209
210 const auto & weighted_gap = *_weighted_gap_ptr;
211 const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
212 const Real c_t = _normalize_c ? _c_t / *_normalization_ptr : _c_t;
213
214 const auto dof_index_x = dof->dof_number(_sys.number(), _lm_vars[0]->number(), 0);
215 const auto dof_index_y = dof->dof_number(_sys.number(), _lm_vars[1]->number(), 0);
216 const Real scaling_factor_x = _lm_vars[0]->scalingFactor();
217 const Real scaling_factor_y = _lm_vars[1]->scalingFactor();
218 Real scaling_factor_z = 1;
219
220 ADReal lm_x = (*_sys.currentSolution())(dof_index_x);
221 ADReal lm_y = (*_sys.currentSolution())(dof_index_y);
222
223 Moose::derivInsert(lm_x.derivatives(), dof_index_x, 1.);
224 Moose::derivInsert(lm_y.derivatives(), dof_index_y, 1.);
225
226 dof_id_type dof_index_z(-1);
227 ADReal lm_z;
228 if (_has_disp_z)
229 {
230 dof_index_z = dof->dof_number(_sys.number(), _lm_vars[2]->number(), 0);
231 lm_z = (*_sys.currentSolution())(dof_index_z);
232 Moose::derivInsert(lm_z.derivatives(), dof_index_z, 1.);
233 scaling_factor_z = _lm_vars[2]->scalingFactor();
234 }
235
236 ADReal normal_pressure_value =
237 lm_x * _dof_to_normal_vector[dof](0) + lm_y * _dof_to_normal_vector[dof](1);
238 ADReal tangential_pressure_value =
239 lm_x * _dof_to_tangent_vectors[dof][0](0) + lm_y * _dof_to_tangent_vectors[dof][0](1);
240
241 ADReal tangential_pressure_value_dir;
242
243 if (_has_disp_z)
244 {
245 normal_pressure_value += lm_z * _dof_to_normal_vector[dof](2);
246 tangential_pressure_value += lm_z * _dof_to_tangent_vectors[dof][0](2);
247 tangential_pressure_value_dir = lm_x * _dof_to_tangent_vectors[dof][1](0) +
248 lm_y * _dof_to_tangent_vectors[dof][1](1) +
249 lm_z * _dof_to_tangent_vectors[dof][1](2);
250 }
251
252 ADReal normal_dof_residual = min(normal_pressure_value, weighted_gap * c);
253 ADReal tangential_dof_residual;
254 ADReal tangential_dof_residual_dir;
255
256 const ADReal & tangential_vel = *_tangential_vel_ptr[0];
257
258 // Primal-dual active set strategy (PDASS)
259 if (!_has_disp_z)
260 {
261 if (normal_pressure_value.value() < _epsilon)
262 tangential_dof_residual = tangential_pressure_value;
263 else
264 {
265 const auto term_1 = max(_mu * (normal_pressure_value + c * weighted_gap),
266 abs(tangential_pressure_value + c_t * tangential_vel * _dt)) *
267 tangential_pressure_value;
268 const auto term_2 = _mu * max(0.0, normal_pressure_value + c * weighted_gap) *
269 (tangential_pressure_value + c_t * tangential_vel * _dt);
270
271 tangential_dof_residual = term_1 - term_2;
272 }
273 }
274 else
275 {
276 if (normal_pressure_value.value() < _epsilon)
277 {
278 tangential_dof_residual = tangential_pressure_value;
279 tangential_dof_residual_dir = tangential_pressure_value_dir;
280 }
281 else
282 {
283 const Real epsilon_sqrt = 1.0e-48;
284
285 const auto lamdba_plus_cg = normal_pressure_value + c * weighted_gap;
286
287 std::array<ADReal, 2> lambda_t_plus_ctu;
288 lambda_t_plus_ctu[0] = tangential_pressure_value + c_t * (*_tangential_vel_ptr[0]) * _dt;
289 lambda_t_plus_ctu[1] = tangential_pressure_value_dir + c_t * (*_tangential_vel_ptr[1]) * _dt;
290
291 const auto term_1_x = max(_mu * lamdba_plus_cg,
292 sqrt(lambda_t_plus_ctu[0] * lambda_t_plus_ctu[0] +
293 lambda_t_plus_ctu[1] * lambda_t_plus_ctu[1] + epsilon_sqrt)) *
294 tangential_pressure_value;
295
296 const auto term_1_y = max(_mu * lamdba_plus_cg,
297 sqrt(lambda_t_plus_ctu[0] * lambda_t_plus_ctu[0] +
298 lambda_t_plus_ctu[1] * lambda_t_plus_ctu[1] + epsilon_sqrt)) *
299 tangential_pressure_value_dir;
300
301 const auto term_2_x = _mu * max(0.0, lamdba_plus_cg) * lambda_t_plus_ctu[0];
302
303 const auto term_2_y = _mu * max(0.0, lamdba_plus_cg) * lambda_t_plus_ctu[1];
304
305 tangential_dof_residual = term_1_x - term_2_x;
306 tangential_dof_residual_dir = term_1_y - term_2_y;
307 }
308 }
309
310 // Compute the friction coefficient (constant or function)
311
312 // Get index for normal constraint.
313 // We do this to get a decent Jacobian structure, which is key for the use of iterative solvers.
314 // Using old normal vector to avoid changes in the Jacobian structure within one time step
315
316 Real ny, nz;
317 // Intially, use the current normal vector
318 if (_dof_to_old_normal_vector[dof].norm() < TOLERANCE)
319 {
320 ny = _dof_to_normal_vector[dof](1);
321 nz = _dof_to_normal_vector[dof](2);
322 }
323 else
324 {
325 ny = _dof_to_old_normal_vector[dof](1);
326 nz = _dof_to_old_normal_vector[dof](2);
327 }
328
329 unsigned int component_normal = 0;
330
331 // Consider constraint orientation to improve Jacobian structure
332 const Real threshold_for_Jacobian = _has_disp_z ? 1.0 / sqrt(3.0) : 1.0 / sqrt(2.0);
333
334 if (abs(ny) > threshold_for_Jacobian)
335 component_normal = 1;
336 else if (abs(nz) > threshold_for_Jacobian)
337 component_normal = 2;
338
340 _assembly,
341 std::array<ADReal, 1>{{normal_dof_residual}},
342 std::array<dof_id_type, 1>{{component_normal == 0
343 ? dof_index_x
344 : (component_normal == 1 ? dof_index_y : dof_index_z)}},
345 component_normal == 0 ? scaling_factor_x
346 : (component_normal == 1 ? scaling_factor_y : scaling_factor_z));
347
349 _assembly,
350 std::array<ADReal, 1>{{tangential_dof_residual}},
351 std::array<dof_id_type, 1>{
352 {(component_normal == 0 || component_normal == 2) ? dof_index_y : dof_index_x}},
353 (component_normal == 0 || component_normal == 2) ? scaling_factor_y : scaling_factor_x);
354
355 if (_has_disp_z)
357 _assembly,
358 std::array<ADReal, 1>{{tangential_dof_residual_dir}},
359 std::array<dof_id_type, 1>{
360 {(component_normal == 0 || component_normal == 1) ? dof_index_z : dof_index_x}},
361 (component_normal == 0 || component_normal == 1) ? scaling_factor_z : scaling_factor_x);
362}
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
Small contact pressure value to trigger computation of frictional forces.
const ADVariableValue & _secondary_x_dot
x-velocity on the secondary face
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
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)