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