https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ComputeWeightedGapLMMechanicalContact.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#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 &
31assignVarsInParams(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 // Mechanical contact residuals use only primal values and normals
51 params.set<bool>("_compute_primal_gradients") = false;
52 params.addClassDescription("Computes the weighted gap that will later be used to enforce the "
53 "zero-penetration mechanical contact conditions");
54 params.suppressParameter<VariableName>("secondary_variable");
55 params.suppressParameter<VariableName>("primary_variable");
56 params.addRequiredCoupledVar("disp_x", "The x displacement variable");
57 params.addRequiredCoupledVar("disp_y", "The y displacement variable");
58 params.addCoupledVar("disp_z", "The z displacement variable");
59 params.addParam<Real>(
60 "c", 1e6, "Parameter for balancing the size of the gap and contact pressure");
61 params.addParam<bool>(
62 "normalize_c",
63 false,
64 "Whether to normalize c by weighting function norm. When unnormalized "
65 "the value of c effectively depends on element size since in the constraint we compare nodal "
66 "Lagrange Multiplier values to integrated gap values (LM nodal value is independent of "
67 "element size, where integrated values are dependent on element size).");
68 // The nodal normal at a secondary node depends on every face incident to it, so the Jacobian
69 // needs the secondary face point-neighbor ghosted and coupled.
70 params.set<bool>("ghost_point_neighbors") = true;
71 params.set<bool>("use_displaced_mesh") = true;
72 params.set<bool>("interpolate_normals") = false;
73 params.addRequiredParam<UserObjectName>("weighted_gap_uo", "The weighted gap user object");
74 return params;
75}
76
78 const InputParameters & parameters)
79 : ADMortarConstraint(assignVarsInParams(parameters)),
80 _secondary_disp_x(adCoupledValue("disp_x")),
81 _primary_disp_x(adCoupledNeighborValue("disp_x")),
82 _secondary_disp_y(adCoupledValue("disp_y")),
83 _primary_disp_y(adCoupledNeighborValue("disp_y")),
84 _has_disp_z(isCoupled("disp_z")),
85 _secondary_disp_z(_has_disp_z ? &adCoupledValue("disp_z") : nullptr),
86 _primary_disp_z(_has_disp_z ? &adCoupledNeighborValue("disp_z") : nullptr),
87 _c(getParam<Real>("c")),
88 _normalize_c(getParam<bool>("normalize_c")),
89 _nodal(getVar("disp_x", 0)->feType().family == LAGRANGE),
90 _disp_x_var(getVar("disp_x", 0)),
91 _disp_y_var(getVar("disp_y", 0)),
92 _disp_z_var(_has_disp_z ? getVar("disp_z", 0) : nullptr),
93 _weighted_gap_uo(getUserObject<WeightedGapUserObject>("weighted_gap_uo"))
94{
96 paramError("weighted_gap_uo",
97 "Nodal-normal derivatives are not supported by user object '",
99 "'.");
100
101 // The user object and constraint must assemble the same interface and displacement variables.
104 paramError("weighted_gap_uo",
105 "'weighted_gap_uo' must be defined on the same secondary/primary subdomain pair as "
106 "this constraint when nodal-normal derivatives are enabled.");
107
108 const std::array<std::pair<const MooseVariable *, unsigned int>, 3> displacement_variables{
109 {{_disp_x_var, 0}, {_disp_y_var, 1}, {_disp_z_var, 2}}};
110 for (const auto & [variable, component] : displacement_variables)
111 if (variable != _weighted_gap_uo.dispVar(component))
112 paramError("weighted_gap_uo",
113 "'weighted_gap_uo' must use the same displacement variables as this constraint "
114 "when nodal-normal derivatives are enabled.");
115
116 if (!getParam<bool>("use_displaced_mesh"))
118 "use_displaced_mesh",
119 "'use_displaced_mesh' must be true for the ComputeWeightedGapLMMechanicalContact object");
120
121 if (getParam<bool>("interpolate_normals"))
122 paramError("interpolate_normals",
123 "Mechanical mortar contact uses normalized secondary nodal normals and cannot be "
124 "combined with quadrature-point normal interpolation.");
125
126 if (!_var->isNodal())
127 if (_var->feType().order != static_cast<Order>(0))
128 mooseError("Normal contact constraints only support elemental variables of CONSTANT order");
129}
130
131ADReal
133{
134 mooseError("We should never call computeQpResidual for ComputeWeightedGapLMMechanicalContact");
135}
136
137void
141
142void
146
147void
151
152void
156
157void
161
162void
164{
165 // During "computeResidual" and "computeJacobian" we are actually just computing properties on the
166 // mortar segment element mesh. We are *not* actually assembling into the residual/Jacobian. For
167 // the zero-penetration constraint, the property of interest is the map from node to weighted gap.
168 // Computation of the properties proceeds identically for residual and Jacobian evaluation hence
169 // why we simply call computeResidual here. We will assemble into the residual/Jacobian later from
170 // the post() method
171 computeResidual(mortar_type);
172}
173
174void
176{
177 parallel_object_only();
178
179 const auto & dof_to_weighted_gap = _weighted_gap_uo.dofToWeightedGap();
180
181 for (const auto & [dof_object, weighted_gap_pr] : dof_to_weighted_gap)
182 {
183 if (dof_object->processor_id() != this->processor_id())
184 continue;
185
186 const auto & [weighted_gap, normalization] = weighted_gap_pr;
187 _weighted_gap_ptr = &weighted_gap;
188 _normalization_ptr = &normalization;
189
190 enforceConstraintOnDof(dof_object);
191 }
192}
193
194void
196 const std::unordered_set<const Node *> & inactive_lm_nodes)
197{
198 const auto & dof_to_weighted_gap = _weighted_gap_uo.dofToWeightedGap();
199
200 for (const auto & [dof_object, weighted_gap_pr] : dof_to_weighted_gap)
201 {
202 if ((inactive_lm_nodes.find(static_cast<const Node *>(dof_object)) !=
203 inactive_lm_nodes.end()) ||
204 (dof_object->processor_id() != this->processor_id()))
205 continue;
206
207 const auto & [weighted_gap, normalization] = weighted_gap_pr;
208 _weighted_gap_ptr = &weighted_gap;
209 _normalization_ptr = &normalization;
210
211 enforceConstraintOnDof(dof_object);
212 }
213}
214
215void
217{
218 const auto & weighted_gap = *_weighted_gap_ptr;
219 // normalizeCDivisor() returns *_normalization_ptr unchanged unless node-based scaling is active,
220 // in which case it substitutes a coverage-independent (full-element) integral so the two
221 // mechanisms don't double-count coverage (PR review discussion; not from Popp 2013).
222 const Real c =
224
225 // Scaling factor kappa_j (Popp 2013 eq. 34; 1 when disabled/fully covered): dividing the gap by
226 // kappa_j gives g_bar/kappa_j, preserving the complementarity root with zhat_j = kappa_j
227 // lambda_j.
228 const Real kappa = _weighted_gap_uo.nodalScale(dof);
229
230 const auto dof_index = dof->dof_number(_sys.number(), _var->number(), 0);
231 ADReal lm_value = (*_sys.currentSolution())(dof_index);
232 Moose::derivInsert(lm_value.derivatives(), dof_index, 1.);
233
234 const ADReal dof_residual = std::min(lm_value, weighted_gap * c / kappa);
235
237 std::array<ADReal, 1>{{dof_residual}},
238 std::array<dof_id_type, 1>{{dof_index}},
240}
DualNumber< Real, DNDerivativeType, true > ADReal
registerMooseObject("ContactApp", ComputeWeightedGapLMMechanicalContact)
void mooseError(Args &&... args)
static InputParameters validParams()
Computes the weighted gap that will later be used to enforce the zero-penetration mechanical contact ...
const bool _normalize_c
Whether to normalize weighted gap by weighting function norm.
const WeightedGapUserObject & _weighted_gap_uo
The weighted gap user object.
virtual void computeQpIProperties()
Computes properties that are functions both of _qp and _i, for example the weighted gap.
virtual void computeJacobian() override
const MooseVariable *const _disp_z_var
The z displacement variable.
const ADReal * _weighted_gap_ptr
A pointer members that can be used to help avoid copying ADReals.
void incorrectEdgeDroppingPost(const std::unordered_set< const Node * > &inactive_lm_nodes) override
Copy of the post routine but that skips assembling inactive nodes.
ADReal computeQpResidual(Moose::MortarType mortar_type) final
ComputeWeightedGapLMMechanicalContact(const InputParameters &parameters)
virtual void computeQpProperties()
Computes properties that are functions only of the current quadrature point (_qp),...
virtual void enforceConstraintOnDof(const DofObject *const dof)
Method called from post().
const MooseVariable *const _disp_x_var
The x displacement variable.
const Real _c
This factor multiplies the weighted gap.
const MooseVariable *const _disp_y_var
The y displacement variable.
virtual void computeResidual() override
void suppressParameter(const std::string &name)
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
void addRequiredParam(const std::string &name, const std::string &doc_string)
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 addCoupledVar(const std::string &name, const std::string &doc_string)
const std::string & name() const
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
const libMesh::FEType & feType() const
void scalingFactor(const std::vector< Real > &factor)
unsigned int number() const
bool isNodal() const override
MooseVariable *const _var
const MooseVariable & variable() const override
SubdomainID primarySubdomain() const
SubdomainID secondarySubdomain() const
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)
Creates dof object to weighted gap map.
virtual bool usesNodalNormalDerivatives() const =0
Whether this formulation includes displacement derivatives of the secondary nodal normals.
const std::unordered_map< const DofObject *, std::pair< ADReal, Real > > & dofToWeightedGap() const
Get the degree of freedom to weighted gap information.
virtual Real nodalScale(const DofObject *const) const
Node-based Lagrange-multiplier scaling factor kappa_j of Popp et al.
const MooseVariable * dispVar(unsigned int component) const
virtual Real normalizeCDivisor(const DofObject *const, const Real covered_normalization) const
OrderWrapper order
processor_id_type processor_id() const
void derivInsert(SemiDynamicSparseNumberArray< Real, libMesh::dof_id_type, NWrapper< N > > &derivs, libMesh::dof_id_type index, Real value)