Line data Source code
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 :
10 : #include "ComputeWeightedGapCartesianLMMechanicalContact.h"
11 : #include "DisplacedProblem.h"
12 : #include "MortarContactUtils.h"
13 : #include "Assembly.h"
14 : #include "AutomaticMortarGeneration.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 :
26 : registerMooseObject("ContactApp", ComputeWeightedGapCartesianLMMechanicalContact);
27 :
28 : namespace
29 : {
30 : const InputParameters &
31 147 : assignVarsInParamsCartesianWeightedGap(const InputParameters & params_in)
32 : {
33 : InputParameters & ret = const_cast<InputParameters &>(params_in);
34 147 : const auto & disp_x_name = ret.get<std::vector<VariableName>>("disp_x");
35 147 : if (disp_x_name.size() != 1)
36 0 : 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 294 : ret.set<VariableName>("secondary_variable") = disp_x_name[0];
40 147 : ret.set<VariableName>("primary_variable") = disp_x_name[0];
41 :
42 147 : return ret;
43 : }
44 : }
45 :
46 : InputParameters
47 294 : ComputeWeightedGapCartesianLMMechanicalContact::validParams()
48 : {
49 294 : InputParameters params = ADMortarConstraint::validParams();
50 294 : params.addClassDescription("Computes the weighted gap that will later be used to enforce the "
51 : "zero-penetration mechanical contact conditions");
52 294 : params.suppressParameter<VariableName>("secondary_variable");
53 294 : params.suppressParameter<VariableName>("primary_variable");
54 588 : params.addRequiredCoupledVar("disp_x", "The x displacement variable");
55 588 : params.addRequiredCoupledVar("disp_y", "The y displacement variable");
56 588 : params.addCoupledVar("disp_z", "The z displacement variable");
57 588 : params.addParam<Real>(
58 588 : "c", 1e6, "Parameter for balancing the size of the gap and contact pressure");
59 588 : params.addRequiredCoupledVar("lm_x",
60 : "Mechanical contact Lagrange multiplier along the x Cartesian axis");
61 588 : params.addRequiredCoupledVar(
62 : "lm_y", "Mechanical contact Lagrange multiplier along the y Cartesian axis.");
63 588 : params.addCoupledVar("lm_z",
64 : "Mechanical contact Lagrange multiplier along the z Cartesian axis.");
65 294 : params.set<bool>("interpolate_normals") = false;
66 588 : params.addParam<bool>(
67 : "normalize_c",
68 588 : false,
69 : "Whether to normalize c by weighting function norm. When unnormalized "
70 : "the value of c effectively depends on element size since in the constraint we compare nodal "
71 : "Lagrange Multiplier values to integrated gap values (LM nodal value is independent of "
72 : "element size, where integrated values are dependent on element size).");
73 294 : params.set<bool>("use_displaced_mesh") = true;
74 294 : return params;
75 0 : }
76 :
77 147 : ComputeWeightedGapCartesianLMMechanicalContact::ComputeWeightedGapCartesianLMMechanicalContact(
78 147 : const InputParameters & parameters)
79 : : ADMortarConstraint(assignVarsInParamsCartesianWeightedGap(parameters)),
80 147 : _secondary_disp_x(adCoupledValue("disp_x")),
81 147 : _primary_disp_x(adCoupledNeighborValue("disp_x")),
82 147 : _secondary_disp_y(adCoupledValue("disp_y")),
83 147 : _primary_disp_y(adCoupledNeighborValue("disp_y")),
84 147 : _has_disp_z(isCoupled("disp_z")),
85 147 : _secondary_disp_z(_has_disp_z ? &adCoupledValue("disp_z") : nullptr),
86 147 : _primary_disp_z(_has_disp_z ? &adCoupledNeighborValue("disp_z") : nullptr),
87 294 : _c(getParam<Real>("c")),
88 294 : _normalize_c(getParam<bool>("normalize_c")),
89 147 : _nodal(getVar("disp_x", 0)->feType().family == LAGRANGE),
90 147 : _disp_x_var(getVar("disp_x", 0)),
91 147 : _disp_y_var(getVar("disp_y", 0)),
92 314 : _disp_z_var(_has_disp_z ? getVar("disp_z", 0) : nullptr)
93 : {
94 147 : _lm_vars.push_back(getVar("lm_x", 0));
95 147 : _lm_vars.push_back(getVar("lm_y", 0));
96 :
97 294 : if (isParamValid("lm_z") ^ _has_disp_z)
98 0 : paramError("lm_z",
99 : "In three-dimensions, both the Z Lagrange multiplier and the Z displacement need to "
100 : "be provided");
101 :
102 147 : if (_has_disp_z)
103 40 : _lm_vars.push_back(getVar("lm_z", 0));
104 :
105 294 : if (!getParam<bool>("use_displaced_mesh"))
106 0 : paramError(
107 : "use_displaced_mesh",
108 : "'use_displaced_mesh' must be true for the ComputeWeightedGapLMMechanicalContact object");
109 :
110 461 : for (const auto i : index_range(_lm_vars))
111 314 : if (!_lm_vars[i]->isNodal())
112 0 : if (_lm_vars[i]->feType().order != static_cast<Order>(0))
113 0 : mooseError("Normal contact constraints only support elemental variables of CONSTANT order");
114 147 : }
115 :
116 : ADReal
117 0 : ComputeWeightedGapCartesianLMMechanicalContact::computeQpResidual(Moose::MortarType)
118 : {
119 0 : mooseError("We should never call computeQpResidual for ComputeWeightedGapLMMechanicalContact");
120 : }
121 :
122 : void
123 1036908 : ComputeWeightedGapCartesianLMMechanicalContact::computeQpProperties()
124 : {
125 : // Trim interior node variable derivatives
126 : const auto & primary_ip_lowerd_map = amg().getPrimaryIpToLowerElementMap(
127 1036908 : *_lower_primary_elem, *_lower_primary_elem->interior_parent(), *_lower_secondary_elem);
128 : const auto & secondary_ip_lowerd_map =
129 1036908 : amg().getSecondaryIpToLowerElementMap(*_lower_secondary_elem);
130 :
131 1036908 : std::array<const MooseVariable *, 3> var_array{{_disp_x_var, _disp_y_var, _disp_z_var}};
132 : std::array<ADReal, 3> primary_disp{
133 1036908 : {_primary_disp_x[_qp], _primary_disp_y[_qp], _has_disp_z ? (*_primary_disp_z)[_qp] : 0}};
134 1036908 : std::array<ADReal, 3> secondary_disp{{_secondary_disp_x[_qp],
135 1036908 : _secondary_disp_y[_qp],
136 1036908 : _has_disp_z ? (*_secondary_disp_z)[_qp] : 0}};
137 :
138 1036908 : trimInteriorNodeDerivatives(primary_ip_lowerd_map, var_array, primary_disp, false);
139 1036908 : trimInteriorNodeDerivatives(secondary_ip_lowerd_map, var_array, secondary_disp, true);
140 :
141 : const ADReal & prim_x = primary_disp[0];
142 : const ADReal & prim_y = primary_disp[1];
143 : const ADReal * prim_z = nullptr;
144 1036908 : if (_has_disp_z)
145 : prim_z = &primary_disp[2];
146 :
147 : const ADReal & sec_x = secondary_disp[0];
148 : const ADReal & sec_y = secondary_disp[1];
149 : const ADReal * sec_z = nullptr;
150 1036908 : if (_has_disp_z)
151 : sec_z = &secondary_disp[2];
152 :
153 : // Compute gap vector
154 1036908 : ADRealVectorValue gap_vec = _phys_points_primary[_qp] - _phys_points_secondary[_qp];
155 :
156 1036908 : gap_vec(0).derivatives() = prim_x.derivatives() - sec_x.derivatives();
157 1036908 : gap_vec(1).derivatives() = prim_y.derivatives() - sec_y.derivatives();
158 1036908 : if (_has_disp_z)
159 430080 : gap_vec(2).derivatives() = prim_z->derivatives() - sec_z->derivatives();
160 :
161 : // Compute integration point quantities
162 1036908 : _qp_gap_nodal = gap_vec * (_JxW_msm[_qp] * _coord[_qp]);
163 :
164 : // To do normalization of constraint coefficient (c_n)
165 1036908 : _qp_factor = _JxW_msm[_qp] * _coord[_qp];
166 1036908 : }
167 :
168 : void
169 2933976 : ComputeWeightedGapCartesianLMMechanicalContact::computeQpIProperties()
170 : {
171 : mooseAssert(_normals.size() == _lower_secondary_elem->n_nodes(),
172 : "Making sure that _normals is the expected size");
173 :
174 : // Get the _dof_to_weighted_gap map
175 2933976 : const DofObject * dof = _lm_vars[0]->isNodal()
176 2933976 : ? static_cast<const DofObject *>(_lower_secondary_elem->node_ptr(_i))
177 0 : : static_cast<const DofObject *>(_lower_secondary_elem);
178 :
179 5867952 : _dof_to_weighted_gap[dof].first += _test[_i][_qp] * _qp_gap_nodal * _normals[_i];
180 :
181 2933976 : if (_normalize_c)
182 0 : _dof_to_weighted_gap[dof].second += _test[_i][_qp] * _qp_factor;
183 2933976 : }
184 :
185 : void
186 1942 : ComputeWeightedGapCartesianLMMechanicalContact::timestepSetup()
187 : {
188 : _dof_to_old_normal_vector.clear();
189 :
190 14450 : for (auto & map_pr : _dof_to_normal_vector)
191 : _dof_to_old_normal_vector.emplace(map_pr);
192 1942 : }
193 :
194 : void
195 27810 : ComputeWeightedGapCartesianLMMechanicalContact::residualSetup()
196 : {
197 : _dof_to_weighted_gap.clear();
198 : _dof_to_normal_vector.clear();
199 : _dof_to_tangent_vectors.clear();
200 27810 : }
201 :
202 : void
203 7598 : ComputeWeightedGapCartesianLMMechanicalContact::jacobianSetup()
204 : {
205 7598 : residualSetup();
206 7598 : }
207 :
208 : void
209 1232802 : ComputeWeightedGapCartesianLMMechanicalContact::computeResidual(const Moose::MortarType mortar_type)
210 : {
211 1232802 : if (mortar_type != Moose::MortarType::Lower)
212 : return;
213 :
214 : for (const auto i : index_range(_lm_vars))
215 : {
216 : mooseAssert(_lm_vars[i], "LM variable is null");
217 : libmesh_ignore(i);
218 : }
219 :
220 1447842 : for (_qp = 0; _qp < _qrule_msm->n_points(); _qp++)
221 : {
222 1036908 : computeQpProperties();
223 3970884 : for (_i = 0; _i < _test.size(); ++_i)
224 : {
225 2933976 : computeQpIProperties();
226 :
227 : // Get the _dof_to_weighted_gap map
228 : const DofObject * dof =
229 2933976 : _lm_vars[0]->isNodal()
230 2933976 : ? static_cast<const DofObject *>(_lower_secondary_elem->node_ptr(_i))
231 0 : : static_cast<const DofObject *>(_lower_secondary_elem);
232 : // We do not interpolate geometry, so just match the local node _i with the corresponding _i
233 2933976 : _dof_to_normal_vector[dof] = _normals[_i];
234 2933976 : const auto & nodal_tangents = amg().getNodalTangents(*_lower_secondary_elem);
235 2933976 : _dof_to_tangent_vectors[dof][0] = nodal_tangents[0][_i];
236 2933976 : _dof_to_tangent_vectors[dof][1] = nodal_tangents[1][_i];
237 : }
238 : }
239 : }
240 :
241 : void
242 495654 : ComputeWeightedGapCartesianLMMechanicalContact::computeJacobian(const Moose::MortarType mortar_type)
243 : {
244 : // During "computeResidual" and "computeJacobian" we are actually just computing properties on the
245 : // mortar segment element mesh. We are *not* actually assembling into the residual/Jacobian. For
246 : // the zero-penetration constraint, the property of interest is the map from node to weighted gap.
247 : // Computation of the properties proceeds identically for residual and Jacobian evaluation hence
248 : // why we simply call computeResidual here. We will assemble into the residual/Jacobian later from
249 : // the post() method
250 495654 : computeResidual(mortar_type);
251 495654 : }
252 :
253 : void
254 1165 : ComputeWeightedGapCartesianLMMechanicalContact::post()
255 : {
256 1165 : Moose::Mortar::Contact::communicateGaps(
257 1165 : _dof_to_weighted_gap, _mesh, _nodal, _normalize_c, _communicator, false);
258 :
259 9942 : for (const auto & pr : _dof_to_weighted_gap)
260 : {
261 8777 : if (pr.first->processor_id() != this->processor_id())
262 212 : continue;
263 :
264 8565 : _weighted_gap_ptr = &pr.second.first;
265 8565 : _normalization_ptr = &pr.second.second;
266 :
267 8565 : enforceConstraintOnDof(pr.first);
268 : }
269 1165 : }
270 :
271 : void
272 20572 : ComputeWeightedGapCartesianLMMechanicalContact::incorrectEdgeDroppingPost(
273 : const std::unordered_set<const Node *> & inactive_lm_nodes)
274 : {
275 20572 : Moose::Mortar::Contact::communicateGaps(
276 20572 : _dof_to_weighted_gap, _mesh, _nodal, _normalize_c, _communicator, false);
277 :
278 77145 : for (const auto & pr : _dof_to_weighted_gap)
279 : {
280 56573 : if ((inactive_lm_nodes.find(static_cast<const Node *>(pr.first)) != inactive_lm_nodes.end()) ||
281 56573 : (pr.first->processor_id() != this->processor_id()))
282 0 : continue;
283 :
284 56573 : _weighted_gap_ptr = &pr.second.first;
285 56573 : _normalization_ptr = &pr.second.second;
286 :
287 56573 : enforceConstraintOnDof(pr.first);
288 : }
289 20572 : }
290 :
291 : void
292 65138 : ComputeWeightedGapCartesianLMMechanicalContact::enforceConstraintOnDof(const DofObject * const dof)
293 : {
294 65138 : const auto & weighted_gap = *_weighted_gap_ptr;
295 65138 : const Real c = _normalize_c ? _c / *_normalization_ptr : _c;
296 :
297 65138 : const auto dof_index_x = dof->dof_number(_sys.number(), _lm_vars[0]->number(), 0);
298 65138 : const auto dof_index_y = dof->dof_number(_sys.number(), _lm_vars[1]->number(), 0);
299 65138 : const Real scaling_factor_x = _lm_vars[0]->scalingFactor();
300 65138 : const Real scaling_factor_y = _lm_vars[1]->scalingFactor();
301 : Real scaling_factor_z = 1;
302 :
303 65138 : ADReal lm_x = (*_sys.currentSolution())(dof_index_x);
304 65138 : ADReal lm_y = (*_sys.currentSolution())(dof_index_y);
305 :
306 : Moose::derivInsert(lm_x.derivatives(), dof_index_x, 1.);
307 : Moose::derivInsert(lm_y.derivatives(), dof_index_y, 1.);
308 :
309 : dof_id_type dof_index_z(-1);
310 : ADReal lm_z;
311 65138 : if (_has_disp_z)
312 : {
313 4064 : dof_index_z = dof->dof_number(_sys.number(), _lm_vars[2]->number(), 0);
314 4064 : lm_z = (*_sys.currentSolution())(dof_index_z);
315 : Moose::derivInsert(lm_z.derivatives(), dof_index_z, 1.);
316 4064 : scaling_factor_z = _lm_vars[2]->scalingFactor();
317 : }
318 :
319 : ADReal normal_pressure_value =
320 65138 : lm_x * _dof_to_normal_vector[dof](0) + lm_y * _dof_to_normal_vector[dof](1);
321 : ADReal tangential_pressure_value =
322 65138 : lm_x * _dof_to_tangent_vectors[dof][0](0) + lm_y * _dof_to_tangent_vectors[dof][0](1);
323 :
324 : ADReal tangential_pressure_value_dir;
325 :
326 65138 : if (_has_disp_z)
327 : {
328 4064 : normal_pressure_value += lm_z * _dof_to_normal_vector[dof](2);
329 4064 : tangential_pressure_value += lm_z * _dof_to_tangent_vectors[dof][0](2);
330 4064 : tangential_pressure_value_dir = lm_x * _dof_to_tangent_vectors[dof][1](0) +
331 4064 : lm_y * _dof_to_tangent_vectors[dof][1](1) +
332 4064 : lm_z * _dof_to_tangent_vectors[dof][1](2);
333 : }
334 :
335 65138 : ADReal normal_dof_residual = std::min(normal_pressure_value, weighted_gap * c);
336 65138 : ADReal tangential_dof_residual = tangential_pressure_value;
337 :
338 : // Get index for normal constraint.
339 : // We do this to get a decent Jacobian structure, which is key for the use of iterative solvers.
340 : // Using old normal vector to avoid changes in the Jacobian structure within one time step
341 :
342 : Real ny, nz;
343 : // Intially, use the current normal vector
344 65138 : if (_dof_to_old_normal_vector[dof].norm() < TOLERANCE)
345 : {
346 3660 : ny = _dof_to_normal_vector[dof](1);
347 3660 : nz = _dof_to_normal_vector[dof](2);
348 : }
349 : else
350 : {
351 61478 : ny = _dof_to_old_normal_vector[dof](1);
352 61478 : nz = _dof_to_old_normal_vector[dof](2);
353 : }
354 : unsigned int component_normal = 0;
355 65138 : if (std::abs(ny) > 0.57735)
356 : component_normal = 1;
357 8565 : else if (std::abs(nz) > 0.57735)
358 : component_normal = 2;
359 :
360 : libmesh_ignore(component_normal);
361 :
362 130276 : addResidualsAndJacobian(
363 : _assembly,
364 130276 : std::array<ADReal, 1>{{normal_dof_residual}},
365 65138 : std::array<dof_id_type, 1>{{component_normal == 0
366 60637 : ? dof_index_x
367 : : (component_normal == 1 ? dof_index_y : dof_index_z)}},
368 : component_normal == 0 ? scaling_factor_x
369 : : (component_normal == 1 ? scaling_factor_y : scaling_factor_z));
370 :
371 65138 : addResidualsAndJacobian(
372 : _assembly,
373 130276 : std::array<ADReal, 1>{{tangential_dof_residual}},
374 65138 : std::array<dof_id_type, 1>{
375 65138 : {(component_normal == 0 || component_normal == 2) ? dof_index_y : dof_index_x}},
376 65138 : (component_normal == 0 || component_normal == 2) ? scaling_factor_y : scaling_factor_x);
377 :
378 65138 : if (_has_disp_z)
379 8128 : addResidualsAndJacobian(
380 : _assembly,
381 8128 : std::array<ADReal, 1>{{tangential_pressure_value_dir}},
382 8128 : std::array<dof_id_type, 1>{
383 : {(component_normal == 0 || component_normal == 1) ? dof_index_z : dof_index_x}},
384 : (component_normal == 0 || component_normal == 1) ? scaling_factor_z : scaling_factor_x);
385 65138 : }
|