https://mooseframework.inl.gov
Loading...
Searching...
No Matches
WeightedGapUserObject.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 "MooseVariableField.h"
12#include "SubProblem.h"
13#include "MortarUtils.h"
14#include "MooseUtils.h"
15#include "MortarContactUtils.h"
17#include "ADUtils.h"
18
19#include "libmesh/quadrature.h"
20
21#include <limits>
22
25{
29 params.addRequiredCoupledVar("disp_x", "The x displacement variable");
30 params.addRequiredCoupledVar("disp_y", "The y displacement variable");
31 params.addCoupledVar("disp_z", "The z displacement variable");
32 params.set<bool>("use_displaced_mesh") = true;
33 params.set<bool>("interpolate_normals") = false;
34 params.set<ExecFlagEnum>("execute_on") = {EXEC_LINEAR, EXEC_NONLINEAR};
35 params.suppressParameter<ExecFlagEnum>("execute_on");
36 return params;
37}
38
39void
41{
42 params.set<bool>("ghost_point_neighbors") = true;
43 const auto configure_point_neighbors =
44 [](const InputParameters & obj_params, InputParameters & rm_params)
45 {
46 rm_params.set<bool>("use_displaced_mesh") = obj_params.get<bool>("use_displaced_mesh");
47 rm_params.set<BoundaryName>("secondary_boundary") =
48 obj_params.get<BoundaryName>("secondary_boundary");
49 rm_params.set<BoundaryName>("primary_boundary") =
50 obj_params.get<BoundaryName>("primary_boundary");
51 rm_params.set<SubdomainName>("secondary_subdomain") =
52 obj_params.get<SubdomainName>("secondary_subdomain");
53 rm_params.set<SubdomainName>("primary_subdomain") =
54 obj_params.get<SubdomainName>("primary_subdomain");
55 // Objects that disable ghost_point_neighbors do not include nodal-normal derivatives.
56 rm_params.set<bool>("ghost_point_neighbors") = obj_params.get<bool>("ghost_point_neighbors");
57 };
58 params.addRelationshipManager("AugmentSparsityOnInterface",
59 Moose::RelationshipManagerType::COUPLING,
60 configure_point_neighbors);
61}
62
64 : MortarUserObject(parameters),
65 _fe_problem(*getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")),
66 _nodal(getVar("disp_x", 0)->feType().family == LAGRANGE),
67 _disp_x_var(getVar("disp_x", 0)),
68 _disp_y_var(getVar("disp_y", 0)),
69 _has_disp_z(isCoupled("disp_z")),
70 _disp_z_var(_has_disp_z ? getVar("disp_z", 0) : nullptr),
71 _secondary_disp_x(_disp_x_var->adSln()),
72 _primary_disp_x(_disp_x_var->adSlnNeighbor()),
73 _secondary_disp_y(_disp_y_var->adSln()),
74 _primary_disp_y(_disp_y_var->adSlnNeighbor()),
75 _secondary_disp_z(_has_disp_z ? &_disp_z_var->adSln() : nullptr),
76 _primary_disp_z(_has_disp_z ? &_disp_z_var->adSlnNeighbor() : nullptr),
77 _coord(_assembly.mortarCoordTransformation())
78{
79 if (!getParam<bool>("use_displaced_mesh"))
80 paramError("use_displaced_mesh",
81 "'use_displaced_mesh' must be true for the WeightedGapUserObject object");
82}
83
86{
87 mooseError("Nodal contact pressure is not available for user object '", name(), "'.");
88}
89
92{
93 return test();
94}
95
96bool
101
102const MooseVariable *
103WeightedGapUserObject::dispVar(unsigned int component) const
104{
105 switch (component)
106 {
107 case 0:
108 return _disp_x_var;
109 case 1:
110 return _disp_y_var;
111 case 2:
112 return _disp_z_var;
113 default:
114 mooseError("Invalid displacement component '", component, "' requested.");
115 }
116}
117
118void
120{
122 _test = &test();
123
125 return;
126
127 if (getParam<bool>("interpolate_normals"))
128 paramError("interpolate_normals",
129 "Nodal-normal derivatives require normalized secondary nodal normals and cannot be "
130 "combined with quadrature-point normal interpolation.");
131
132 const std::array<std::pair<const MooseVariable *, const char *>, 3> displacement_variables{
133 {{_disp_x_var, "disp_x"}, {_disp_y_var, "disp_y"}, {_disp_z_var, "disp_z"}}};
134 for (const auto & [variable, parameter_name] : displacement_variables)
135 if (variable)
136 {
137 if (!variable->isNodal())
138 paramError(parameter_name,
139 "Nodal-normal derivatives require a nodal displacement variable.");
140 if (&variable->sys() != &_sys)
141 paramError(parameter_name,
142 "Nodal-normal derivatives require displacement variables in the nonlinear "
143 "system assembled by this contact object.");
144 }
145}
146
147void
149{
150 // Trim interior node variable derivatives
151 const auto & primary_ip_lowerd_map = amg().getPrimaryIpToLowerElementMap(
153 const auto & secondary_ip_lowerd_map =
155
156 std::array<const MooseVariable *, 3> var_array{{_disp_x_var, _disp_y_var, _disp_z_var}};
157 std::array<ADReal, 3> primary_disp{
158 {_primary_disp_x[_qp], _primary_disp_y[_qp], _has_disp_z ? (*_primary_disp_z)[_qp] : 0}};
159 std::array<ADReal, 3> secondary_disp{{_secondary_disp_x[_qp],
161 _has_disp_z ? (*_secondary_disp_z)[_qp] : 0}};
162
163 trimInteriorNodeDerivatives(primary_ip_lowerd_map, var_array, primary_disp, false);
164 trimInteriorNodeDerivatives(secondary_ip_lowerd_map, var_array, secondary_disp, true);
165
166 const ADReal & prim_x = primary_disp[0];
167 const ADReal & prim_y = primary_disp[1];
168 const ADReal * prim_z = nullptr;
169 if (_has_disp_z)
170 prim_z = &primary_disp[2];
171
172 const ADReal & sec_x = secondary_disp[0];
173 const ADReal & sec_y = secondary_disp[1];
174 const ADReal * sec_z = nullptr;
175 if (_has_disp_z)
176 sec_z = &secondary_disp[2];
177
178 // Compute gap vector
180
181 // Generic displacement for interface problems
182 _qp_displacement_nodal(0) = prim_x - sec_x;
183 _qp_displacement_nodal(1) = prim_y - sec_y;
184 if (_has_disp_z)
185 _qp_displacement_nodal(2) = *prim_z - *sec_z;
186
188
189 gap_vec(0).derivatives() = prim_x.derivatives() - sec_x.derivatives();
190 gap_vec(1).derivatives() = prim_y.derivatives() - sec_y.derivatives();
191 if (_has_disp_z)
192 gap_vec(2).derivatives() = prim_z->derivatives() - sec_z->derivatives();
193
194 // Compute integration point quantities: Normals (geometry) is averaged at the node, but not
195 // interpolated within the weak integration.
196 _qp_gap_nodal = gap_vec * (_JxW_msm[_qp] * _coord[_qp]);
197
198 // To do normalization of constraint coefficient (c_n)
200}
201
202void
204{
205 mooseAssert(_normals.size() == _lower_secondary_elem->n_nodes(),
206 "The nodal normals and secondary element must have the same number of nodes.");
207
208 // Get the _dof_to_weighted_gap map
209 const auto * const dof = cast_ptr<const DofObject *>(_lower_secondary_elem->node_ptr(_i));
210
211 auto & [weighted_gap, normalization] = _dof_to_weighted_gap[dof];
212
214 weighted_gap += (*_test)[_i][_qp] * _qp_gap_nodal * contactNormal(*_lower_secondary_elem, _i);
215 else
216 weighted_gap += (*_test)[_i][_qp] * _qp_gap_nodal * _normals[_i];
217
218 normalization += (*_test)[_i][_qp] * _qp_factor;
219}
220
221const ADRealVectorValue &
222WeightedGapUserObject::contactNormal(const Elem & lower_secondary_elem,
223 const unsigned int nodal_index) const
224{
225 mooseAssert(nodal_index < lower_secondary_elem.n_nodes(),
226 "Nodal normal index must refer to a node on the secondary element.");
228 "AD contact normals should only be requested while recording nodal-normal "
229 "derivatives.");
230 const Node * const node = lower_secondary_elem.node_ptr(nodal_index);
231 const auto normal_it = _ad_nodal_normals.find(node);
232 if (normal_it != _ad_nodal_normals.end())
233 return normal_it->second;
234
235 amg().computeADNodalNormals([this](const Node & coordinate_node)
236 { return nodalCoordinate(coordinate_node); },
238 return libmesh_map_find(_ad_nodal_normals, node);
239}
240
243{
245 "AD nodal coordinates should only be requested while recording nodal-normal "
246 "derivatives.");
248 "AD nodal coordinates require derivative recording.");
249 ADPoint point = node;
250
251 const std::array<std::pair<const MooseVariable *, unsigned int>, 3> displacement_variables{
252 {{_disp_x_var, 0}, {_disp_y_var, 1}, {_disp_z_var, 2}}};
253 for (const auto & [variable, component] : displacement_variables)
254 if (variable)
255 {
256 const auto sys_num = variable->sys().number();
257 const auto var_num = variable->number();
258 // Enriched elements can have mapping nodes without independent displacement degrees of
259 // freedom. Their positions enter the geometric map, but they contribute no independent
260 // displacement derivative.
261 if (node.n_dofs(sys_num, var_num))
263 point(component).derivatives(), node.dof_number(sys_num, var_num, 0), 1.0);
264 }
265
266 return point;
267}
268
269void
276
277void
279{
280 // If the constraint is performed by the owner, then we don't need any data sent back; the owner
281 // will take care of it. But if the constraint is not performed by the owner and we might have to
282 // do some of the constraining ourselves, then we need data sent back to us
283 const bool send_data_back = !constrainedByOwner();
286 _nodal,
287 /*normalize_c*/ true,
289 send_data_back);
290}
291
292void
294{
295 for (_qp = 0; _qp < _qrule_msm->n_points(); _qp++)
296 {
298 for (_i = 0; _i < _test->size(); ++_i)
300 }
301}
302
303Real
304WeightedGapUserObject::getNormalGap(const Node * const node) const
305{
306 const auto it = _dof_to_weighted_gap.find(_subproblem.mesh().nodePtr(node->id()));
307
308 // We are returning the physical weighted gap for analysis purposes
309 if (it != _dof_to_weighted_gap.end())
310 return physicalGap(it->second);
311 else
312 return 0.0;
313}
DualNumber< Real, DNDerivativeType, true > ADReal
const ExecFlagType EXEC_LINEAR
const ExecFlagType EXEC_NONLINEAR
std::map< unsigned int, unsigned int > getSecondaryIpToLowerElementMap(const Elem &lower_secondary_elem) const
void computeADNodalNormals(const std::function< ADPoint(const Node &)> &coordinate, std::unordered_map< const Node *, ADRealVectorValue > &nodal_normals) const
std::map< unsigned int, unsigned int > getPrimaryIpToLowerElementMap(const Elem &primary_elem, const Elem &primary_elem_ip, const Elem &lower_secondary_elem) const
void suppressParameter(const std::string &name)
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
void addRelationshipManager(const std::string &name, Moose::RelationshipManagerType rm_type, Moose::RelationshipManagerInputParameterCallback input_parameter_callback=nullptr)
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
virtual const Node * nodePtr(const dof_id_type i) const
SystemBase & sys()
static InputParameters validParams()
Elem const *const & _lower_secondary_elem
const libMesh::QBase *const & _qrule_msm
const AutomaticMortarGeneration & amg() const
Elem const *const & _lower_primary_elem
const MooseArray< Point > & _phys_points_secondary
const MooseArray< Point > & _phys_points_primary
std::vector< Point > _normals
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
static InputParameters validParams()
virtual void initialSetup()
virtual MooseMesh & mesh()=0
unsigned int number() const
static InputParameters validParams()
SubProblem & _subproblem
SystemBase & _sys
static InputParameters validParams()
virtual bool usesNodalNormalDerivatives() const =0
Whether this formulation includes displacement derivatives of the secondary nodal normals.
bool shouldRecordNodalNormalDerivatives() const
Whether the current assembly records nodal-normal derivatives for this object's system.
unsigned int _i
Test function index.
const ADVariableValue & _secondary_disp_x
x-displacement on the secondary face
const MooseVariable *const _disp_x_var
The x displacement variable.
virtual void computeQpProperties()
Computes properties that are functions only of the current quadrature point (_qp),...
virtual const VariableTestValue & tractionBasis() const
const bool _has_disp_z
For 2D mortar contact no displacement will be specified, so const pointers used.
virtual void finalize() override
virtual void execute() override
ADRealVectorValue _qp_gap_nodal
Vector for computation of weighted gap with nodal normals.
virtual void initialSetup() override
unsigned int _qp
Quadrature point index for the mortar segments.
Real _qp_factor
The value of the LM at the current quadrature point.
virtual Real getNormalGap(const Node *const) const
ADPoint nodalCoordinate(const Node &node) const
Add displacement derivatives to the node's current coordinate.
static void addNodalNormalCoupling(InputParameters &params)
Request the secondary face point-neighbor matrix coupling that nodal-normal derivatives need.
const ADVariableValue & _primary_disp_y
y-displacement on the primary face
const MooseArray< Real > & _coord
Member for handling change of coordinate systems (xyz, rz, spherical)
const ADVariableValue & _secondary_disp_y
y-displacement on the secondary face
const MooseVariable * dispVar(unsigned int component) const
const ADVariableValue & _primary_disp_x
x-displacement on the primary face
const MooseVariable *const _disp_y_var
The y displacement variable.
virtual ADReal nodalContactPressure(const Node &node) const
const ADRealVectorValue & contactNormal(const Elem &lower_secondary_elem, unsigned int nodal_index) const
Return the cached contact normal for the supplied lower-dimensional secondary element node.
std::unordered_map< const DofObject *, std::pair< ADReal, Real > > _dof_to_weighted_gap
A map from node to weighted gap and normalization (if requested)
virtual const VariableTestValue & test() const =0
WeightedGapUserObject(const InputParameters &parameters)
std::unordered_map< const Node *, ADRealVectorValue > _ad_nodal_normals
AD nodal normals, each averaged over the secondary faces containing that node.
const MooseVariable *const _disp_z_var
The z displacement variable.
ADRealVectorValue _qp_displacement_nodal
Vector for computation of relative displacement (determines mixity ratio in interface problems)
virtual void computeQpIProperties()
Computes properties that are functions both of _qp and _i, for example the weighted gap.
virtual void initialize() override
std::unordered_map< const DofObject *, ADRealVectorValue > _dof_to_weighted_displacements
A map from node to weighted displacements.
virtual bool constrainedByOwner() const =0
Real physicalGap(const std::pair< ADReal, Real > &gap) const
Compute physical gap from integration gap quantity.
const VariableTestValue * _test
A pointer to the test function associated with the weighted gap.
const bool _nodal
Whether the dof objects are nodal; if they're not, then they're elemental.
const Parallel::Communicator & _communicator
unsigned int n_points() const
VariableShapeValue< true > VariableTestValue
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.
bool doDerivatives(const SubProblem &subproblem, const SystemBase &sys)
void derivInsert(SemiDynamicSparseNumberArray< Real, libMesh::dof_id_type, NWrapper< N > > &derivs, libMesh::dof_id_type index, Real value)