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