https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ExplicitDynamicsContactAction.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
12#include "Factory.h"
13#include "FEProblem.h"
14#include "Conversion.h"
15#include "AddVariableAction.h"
16#include "NonlinearSystemBase.h"
17#include "Parser.h"
18
20#include "PointListAdaptor.h"
21
22#include <set>
23#include <algorithm>
24#include <unordered_map>
25#include <limits>
26
27#include "libmesh/petsc_nonlinear_solver.h"
28#include "libmesh/string_to_enum.h"
29
30// Counter for distinct contact action objects
31static unsigned int ed_contact_action_counter = 0;
32
33registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_aux_variable");
34registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_contact_aux_variable");
35registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_aux_kernel");
36
37registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_constraint");
38registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_user_object");
39
42{
45
46 params.addParam<std::vector<BoundaryName>>(
47 "primary", "The list of boundary IDs referring to primary sidesets");
48 params.addParam<std::vector<BoundaryName>>(
49 "secondary", "The list of boundary IDs referring to secondary sidesets");
50 params.addParam<std::vector<SubdomainName>>(
51 "block",
52 {},
53 "Optional list of subdomain names on which to create the contact MooseObjects. If omitted, "
54 "MooseObjects will be created on all subdomains");
55 params.addParam<std::vector<VariableName>>(
56 "displacements",
57 "The displacements appropriate for the simulation geometry and coordinate system");
58 params.addParam<Real>("friction_coefficient", 0, "The friction coefficient");
59 params.addParam<MooseEnum>(
60 "model", ExplicitDynamicsContactAction::getModelEnum(), "The contact model to use");
61 params.addParam<Real>("tangential_tolerance",
62 "Tangential distance to extend edges of contact surfaces");
63 params.addParam<Real>("penalty", 1e8, "Penalty factor for normal contact.");
64 params.addClassDescription("Sets up all objects needed for mechanical contact enforcement in "
65 "explicit dynamics simulations.");
66 params.addParam<std::vector<TagName>>(
67 "extra_vector_tags",
68 "The tag names for extra vectors that residual data should be saved into");
69 params.addParam<std::vector<TagName>>(
70 "absolute_value_vector_tags",
71 "The tags for the vectors this residual object should fill with the "
72 "absolute value of the residual contribution");
73
74 params.addParam<VariableName>("secondary_gap_offset",
75 "Offset to gap distance from secondary side");
76 params.addParam<VariableName>("mapped_primary_gap_offset",
77 "Offset to gap distance mapped from primary side");
78 params.addParam<bool>("verbose", false, "Verbose output. May increase runtime.");
79
80 return params;
81}
82
84 : Action(params),
85 _boundary_pairs(getParam<BoundaryName, BoundaryName>("primary", "secondary")),
86 _model(getParam<MooseEnum>("model").getEnum<ExplicitDynamicsContactModel>()),
87 _verbose(getParam<bool>("verbose")),
88 _blocks(getParam<std::vector<SubdomainName>>("block"))
89{
90 // The resulting velocity of the contact algorithm is applied, as the code stands, by modifying
91 // the old position. This causes artifacts in the internal forces as old position, new position,
92 // and velocities are not fully consistent. Results improve with shorter time steps, but it would
93 // be best to modify all the solution arrays for consistency or replace the way the explicit
94 // system is solved to obtain accelerations (a = M^{-1}F) such that only velocities on the
95 // contacting boundary need to be updated with the contact algorithm output.
96 mooseWarning("Verification of explicit dynamics capabilities is an ongoing effort.");
97}
98
99void
101{
102 // proform problem checks/corrections once during the first feasible task
103 if (_current_task == "add_contact_aux_variable")
104 {
105 if (!_problem->getDisplacedProblem())
107 "Contact requires updated coordinates. Use the 'displacements = ...' parameter in the "
108 "Mesh block.");
109
110 // It is risky to apply this optimization to contact problems
111 // since the problem configuration may be changed during Jacobian
112 // evaluation. We therefore turn it off for all contact problems so that
113 // PETSc-3.8.4 or higher will have the same behavior as PETSc-3.8.3.
114 if (!_problem->isSNESMFReuseBaseSetbyUser())
115 _problem->setSNESMFReuseBase(false, false);
116 }
117
119
120 if (_current_task == "add_aux_kernel" && _verbose)
121 { // Add ContactPenetrationAuxAction.
122 if (!_problem->getDisplacedProblem())
123 mooseError("Contact requires updated coordinates. Use the 'displacements = ...' line in the "
124 "Mesh block.");
125 unsigned int pair_number(0);
126 // Create auxiliary kernels for each contact pairs
127 for (const auto & contact_pair : _boundary_pairs)
128 {
129 {
130 InputParameters params = _factory.getValidParams("PenetrationAux");
132 {"secondary_gap_offset", "mapped_primary_gap_offset", "order"});
133
134 std::vector<VariableName> displacements =
135 getParam<std::vector<VariableName>>("displacements");
136 const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
137 .system()
138 .variable_type(displacements[0])
139 .order.get_order();
140
141 params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
142 params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL, EXEC_LINEAR};
143 params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.second};
144 params.set<BoundaryName>("paired_boundary") = contact_pair.first;
145 params.set<AuxVariableName>("variable") = "penetration";
146 if (isParamValid("secondary_gap_offset"))
147 params.set<std::vector<VariableName>>("secondary_gap_offset") = {
148 getParam<VariableName>("secondary_gap_offset")};
149 if (isParamValid("mapped_primary_gap_offset"))
150 params.set<std::vector<VariableName>>("mapped_primary_gap_offset") = {
151 getParam<VariableName>("mapped_primary_gap_offset")};
152 params.set<bool>("use_displaced_mesh") = true;
153 std::string name = _name + "_contact_" + Moose::stringify(pair_number);
154 pair_number++;
155 _problem->addAuxKernel("PenetrationAux", name, params);
156 }
157 }
158
160 }
161
162 if (_current_task == "add_contact_aux_variable")
163 {
164 std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
165 const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
166 .system()
167 .variable_type(displacements[0])
168 .order.get_order();
169
170 // Add gap rate for output
171 {
172 auto var_params = _factory.getValidParams("MooseVariable");
173 var_params.applyParameters(parameters());
174 var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
175 var_params.set<MooseEnum>("family") = "LAGRANGE";
176 _problem->addAuxVariable("MooseVariable", "gap_rate", var_params);
177 }
178 if (_verbose)
179 {
180 // Add ContactPenetrationVarAction
181 {
182 auto var_params = _factory.getValidParams("MooseVariable");
183 var_params.applyParameters(parameters());
184 var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
185 var_params.set<MooseEnum>("family") = "LAGRANGE";
186 _problem->addAuxVariable("MooseVariable", "penetration", var_params);
187 }
188 {
189 auto var_params = _factory.getValidParams("MooseVariable");
190 var_params.applyParameters(parameters());
191 var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
192 var_params.set<MooseEnum>("family") = "LAGRANGE";
193 _problem->addAuxVariable("MooseVariable", "contact_pressure", var_params);
194 }
195 // Add nodal area contact variable
196 {
197 auto var_params = _factory.getValidParams("MooseVariable");
198 var_params.applyParameters(parameters());
199 var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
200 var_params.set<MooseEnum>("family") = "LAGRANGE";
201 _problem->addAuxVariable("MooseVariable", "nodal_area", var_params);
202 }
203 // Add nodal density variable
204 {
205 auto var_params = _factory.getValidParams("MooseVariable");
206 var_params.applyParameters(parameters());
207 var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
208 var_params.set<MooseEnum>("family") = "LAGRANGE";
209 _problem->addAuxVariable("MooseVariable", "nodal_density", var_params);
210 }
211 // Add nodal wave speed
212 {
213 auto var_params = _factory.getValidParams("MooseVariable");
214 var_params.applyParameters(parameters());
215 var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
216 var_params.set<MooseEnum>("family") = "LAGRANGE";
217 _problem->addAuxVariable("MooseVariable", "nodal_wave_speed", var_params);
218 }
219 }
220 }
221
222 if (_current_task == "add_user_object" && _verbose)
223 {
224 {
225 auto var_params = _factory.getValidParams("NodalArea");
226
227 // Get secondary_boundary_vector from possibly updated set from the
228 // ContactAction constructor cleanup
229 const auto actions = _awh.getActions<ExplicitDynamicsContactAction>();
230
231 std::vector<BoundaryName> secondary_boundary_vector;
232 for (const auto * const action : actions)
233 for (const auto j : index_range(action->_boundary_pairs))
234 secondary_boundary_vector.push_back(action->_boundary_pairs[j].second);
235
236 var_params.set<std::vector<BoundaryName>>("boundary") = secondary_boundary_vector;
237 var_params.set<std::vector<VariableName>>("variable") = {"nodal_area"};
238
239 mooseAssert(_problem, "Problem pointer is NULL");
240 var_params.set<ExecFlagEnum>("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN};
241 var_params.set<bool>("use_displaced_mesh") = true;
242
243 _problem->addUserObject(
244 "NodalArea", "nodal_area_object_" + Moose::stringify(name()), var_params);
245 }
246 // Add nodal density and nodal wave speed user
247 {
248 // Add nodal density
249 auto var_params = _factory.getValidParams("NodalDensity");
250
251 // Get secondary_boundary_vector from possibly updated set from the
252 // ContactAction constructor cleanup
253 const auto actions = _awh.getActions<ExplicitDynamicsContactAction>();
254
255 std::vector<BoundaryName> secondary_boundary_vector;
256 for (const auto * const action : actions)
257 for (const auto j : index_range(action->_boundary_pairs))
258 secondary_boundary_vector.push_back(action->_boundary_pairs[j].second);
259
260 var_params.set<std::vector<BoundaryName>>("boundary") = secondary_boundary_vector;
261 var_params.set<std::vector<VariableName>>("variable") = {"nodal_density"};
262
263 mooseAssert(_problem, "Problem pointer is NULL");
264 var_params.set<ExecFlagEnum>("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN};
265 var_params.set<bool>("use_displaced_mesh") = true;
266
267 _problem->addUserObject(
268 "NodalDensity", "nodal_density_object_" + Moose::stringify(name()), var_params);
269 }
270 {
271 // Add wave speed
272 auto var_params = _factory.getValidParams("NodalWaveSpeed");
273
274 // Get secondary_boundary_vector from possibly updated set from the
275 // ContactAction constructor cleanup
276 const auto actions = _awh.getActions<ExplicitDynamicsContactAction>();
277
278 std::vector<BoundaryName> secondary_boundary_vector;
279 for (const auto * const action : actions)
280 for (const auto j : index_range(action->_boundary_pairs))
281 secondary_boundary_vector.push_back(action->_boundary_pairs[j].second);
282
283 var_params.set<std::vector<BoundaryName>>("boundary") = secondary_boundary_vector;
284 var_params.set<std::vector<VariableName>>("variable") = {"nodal_wave_speed"};
285
286 mooseAssert(_problem, "Problem pointer is NULL");
287 var_params.set<ExecFlagEnum>("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN};
288 var_params.set<bool>("use_displaced_mesh") = true;
289
290 _problem->addUserObject(
291 "NodalWaveSpeed", "nodal_wavespeed_object_" + Moose::stringify(name()), var_params);
292 }
293 }
294}
295
296void
298{
299 // Add ContactPressureAux: Only one object for all contact pairs
300 const auto actions = _awh.getActions<ExplicitDynamicsContactAction>();
301
302 // Increment counter for contact action objects
304
305 // Add auxiliary kernel if we are the last contact action object.
306 if (ed_contact_action_counter == actions.size() && _verbose)
307 {
308 std::vector<BoundaryName> boundary_vector;
309 std::vector<BoundaryName> pair_boundary_vector;
310
311 for (const auto * const action : actions)
312 for (const auto j : index_range(action->_boundary_pairs))
313 {
314 boundary_vector.push_back(action->_boundary_pairs[j].second);
315 pair_boundary_vector.push_back(action->_boundary_pairs[j].first);
316 }
317
318 InputParameters params = _factory.getValidParams("ContactPressureAux");
319 params.applyParameters(parameters(), {"order"});
320
321 std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
322 const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
323 .system()
324 .variable_type(displacements[0])
325 .order.get_order();
326
327 params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
328 params.set<std::vector<BoundaryName>>("boundary") = boundary_vector;
329 params.set<std::vector<BoundaryName>>("paired_boundary") = pair_boundary_vector;
330 params.set<AuxVariableName>("variable") = "contact_pressure";
331 params.addRequiredCoupledVar("nodal_area", "The nodal area");
332 params.set<std::vector<VariableName>>("nodal_area") = {"nodal_area"};
333 params.set<bool>("use_displaced_mesh") = true;
334
335 std::string name = _name + "_contact_pressure";
336 params.set<ExecFlagEnum>("execute_on", true) = {EXEC_TIMESTEP_END};
337 _problem->addAuxKernel("ContactPressureAux", name, params);
338 }
339}
340
341void
343{
344 if (_current_task != "add_constraint")
345 return;
346
347 std::string action_name = MooseUtils::shortName(name());
348 std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
349 const unsigned int ndisp = displacements.size();
350
351 std::string constraint_type;
352
353 constraint_type = "ExplicitDynamicsContactConstraint";
354
355 InputParameters params = _factory.getValidParams(constraint_type);
356
358 {"displacements",
359 "secondary_gap_offset",
360 "mapped_primary_gap_offset",
361 "primary",
362 "secondary"});
363
364 const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
365 .system()
366 .variable_type(displacements[0])
367 .order.get_order();
368
369 params.set<std::vector<VariableName>>("displacements") = displacements;
370 params.set<bool>("use_displaced_mesh") = true;
371 params.set<Real>("penalty") = getParam<Real>("penalty");
372
373 params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
374
375 for (const auto & contact_pair : _boundary_pairs)
376 {
377
378 if (isParamValid("vel_x"))
379 params.set<std::vector<VariableName>>("vel_x") = getParam<std::vector<VariableName>>("vel_x");
380 if (isParamValid("vel_y"))
381 params.set<std::vector<VariableName>>("vel_y") = getParam<std::vector<VariableName>>("vel_y");
382 if (isParamValid("vel_z"))
383 params.set<std::vector<VariableName>>("vel_z") = getParam<std::vector<VariableName>>("vel_z");
384
385 params.set<std::vector<VariableName>>("gap_rate") = {"gap_rate"};
386
387 params.set<BoundaryName>("boundary") = contact_pair.first;
388 if (isParamValid("secondary_gap_offset"))
389 params.set<std::vector<VariableName>>("secondary_gap_offset") = {
390 getParam<VariableName>("secondary_gap_offset")};
391 if (isParamValid("mapped_primary_gap_offset"))
392 params.set<std::vector<VariableName>>("mapped_primary_gap_offset") = {
393 getParam<VariableName>("mapped_primary_gap_offset")};
394
395 for (unsigned int i = 0; i < ndisp; ++i)
396 {
397 std::string name = action_name + "_constraint_" + Moose::stringify(contact_pair, "_") + "_" +
399
400 params.set<unsigned int>("component") = i;
401
402 params.set<BoundaryName>("primary") = contact_pair.first;
403 params.set<BoundaryName>("secondary") = contact_pair.second;
404 params.set<NonlinearVariableName>("variable") = displacements[i];
405 params.set<std::vector<VariableName>>("primary_variable") = {displacements[i]};
407 {"extra_vector_tags", "absolute_value_vector_tags"});
408 _problem->addConstraint(constraint_type, name, params);
409 }
410 }
411}
412
415{
416 return MooseEnum("frictionless frictionless_balance", "frictionless");
417}
418
421{
423
424 params.addParam<MooseEnum>(
425 "model", ExplicitDynamicsContactAction::getModelEnum(), "The contact model to use");
426
427 // Gap rate input
428 params.addCoupledVar("vel_x", "x-component of velocity.");
429 params.addCoupledVar("vel_y", "y-component of velocity.");
430 params.addCoupledVar("vel_z", "z-component of velocity.");
431 // Gap rate output
432 params.addCoupledVar("gap_rate", "Gap rate for output.");
433
434 return params;
435}
static unsigned int ed_contact_action_counter
registerMooseAction("ContactApp", ExplicitDynamicsContactAction, "add_aux_variable")
InputParameters emptyInputParameters()
const ExecFlagType EXEC_TIMESTEP_END
const ExecFlagType EXEC_TIMESTEP_BEGIN
const ExecFlagType EXEC_INITIAL
const ExecFlagType EXEC_LINEAR
std::vector< const T * > getActions()
static InputParameters validParams()
std::shared_ptr< FEProblemBase > & _problem
const std::string & _current_task
ActionWarehouse & _awh
Action class for creating constraints, kernels, and user objects necessary for mechanical contact.
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
ExplicitDynamicsContactAction(const InputParameters &params)
void addNodeFaceContact()
Generate constraints for node to face contact.
void addContactPressureAuxKernel()
Add single contact pressure auxiliary kernel for various contact action objects.
static InputParameters commonParameters()
Define parameters used by multiple contact objects.
const bool _verbose
Verbose output, may increase runtime.
static MooseEnum getModelEnum()
Get contact model.
InputParameters getValidParams(const std::string &name) const
void applySpecificParameters(const InputParameters &common, const std::vector< std::string > &include, bool allow_private=false)
void addRequiredCoupledVar(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)
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)
void applyParameters(const InputParameters &common, const std::vector< std::string > &exclude={}, const bool allow_private=false)
const InputParameters & parameters() const
const std::string & name() const
void mooseError(Args &&... args) const
void mooseWarning(Args &&... args) const
const std::string & _name
bool isParamValid(const std::string &name) const
Factory & _factory
std::string shortName(const std::string &name)
std::string stringify(const T &t)