https://mooseframework.inl.gov
ContactAction.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 
10 #include "ContactAction.h"
11 
12 #include "Factory.h"
13 #include "FEProblem.h"
14 #include "Conversion.h"
15 #include "AddVariableAction.h"
16 #include "MortarConstraintBase.h"
17 #include "NonlinearSystemBase.h"
18 #include "Parser.h"
20 
21 #include "NanoflannMeshAdaptor.h"
22 #include "PointListAdaptor.h"
23 
24 #include <set>
25 #include <algorithm>
26 #include <unordered_map>
27 #include <limits>
28 
29 #include "libmesh/petsc_nonlinear_solver.h"
30 #include "libmesh/string_to_enum.h"
31 
32 // Make newer nanoflann API compatible with older nanoflann versions
33 #if NANOFLANN_VERSION < 0x150
34 namespace nanoflann
35 {
36 typedef SearchParams SearchParameters;
37 
38 template <typename T, typename U>
39 using ResultItem = std::pair<T, U>;
40 }
41 #endif
42 
43 using NodeBoundaryIDInfo = std::pair<const Node *, BoundaryID>;
44 
45 // Counter for naming mortar auxiliary kernels
46 static unsigned int contact_mortar_auxkernel_counter = 0;
47 
48 // Counter for naming auxiliary kernels
49 static unsigned int contact_auxkernel_counter = 0;
50 
51 // Counter for naming nodal area user objects
52 static unsigned int contact_userobject_counter = 0;
53 
54 // Counter for distinct contact action objects
55 static unsigned int contact_action_counter = 0;
56 
57 // For mortar subdomains
58 registerMooseAction("ContactApp", ContactAction, "append_mesh_generator");
59 registerMooseAction("ContactApp", ContactAction, "add_aux_variable");
60 // For mortar Lagrange multiplier
61 registerMooseAction("ContactApp", ContactAction, "add_contact_aux_variable");
62 registerMooseAction("ContactApp", ContactAction, "add_mortar_variable");
63 registerMooseAction("ContactApp", ContactAction, "add_aux_kernel");
64 // For mortar constraint
65 registerMooseAction("ContactApp", ContactAction, "add_constraint");
66 registerMooseAction("ContactApp", ContactAction, "output_penetration_info_vars");
67 registerMooseAction("ContactApp", ContactAction, "add_user_object");
68 // For automatic generation of contact pairs
69 registerMooseAction("ContactApp", ContactAction, "post_mesh_prepared");
70 
73 {
76 
77  params.addParam<std::vector<BoundaryName>>(
78  "primary", "The list of boundary IDs referring to primary sidesets");
79  params.addParam<std::vector<BoundaryName>>(
80  "secondary", "The list of boundary IDs referring to secondary sidesets");
81  params.addParam<std::vector<BoundaryName>>(
82  "automatic_pairing_boundaries",
83  {},
84  "List of boundary IDs for sidesets that are automatically paired with any other boundary in "
85  "this list having a centroid-to-centroid distance less than the value specified in the "
86  "'automatic_pairing_distance' parameter. ");
87  params.addRangeCheckedParam<Real>(
88  "automatic_pairing_distance",
89  "automatic_pairing_distance>=0",
90  "The maximum distance the centroids of the boundaries provided in the "
91  "'automatic_pairing_boundaries' parameter can be to generate a contact pair automatically. "
92  "Due to numerical error in the determination of the centroids, it is encouraged that "
93  "the user adds a tolerance to this distance (e.g. extra 10%) to make sure no suitable "
94  "contact pair is missed. If the 'automatic_pairing_method = NODE' option is chosen instead, "
95  "this distance is recommended to be set to at least twice the minimum distance between "
96  "nodes of boundaries to be paired.");
97  params.addDeprecatedParam<MeshGeneratorName>(
98  "mesh",
99  "The mesh generator for mortar method",
100  "This parameter is not used anymore and can simply be removed");
101  params.addParam<VariableName>("secondary_gap_offset",
102  "Offset to gap distance from secondary side");
103  params.addParam<VariableName>("mapped_primary_gap_offset",
104  "Offset to gap distance mapped from primary side");
105  params.addParam<std::vector<VariableName>>(
106  "displacements",
107  {},
108  "The displacements appropriate for the simulation geometry and coordinate system");
109  params.addParam<Real>(
110  "penalty",
111  1e8,
112  "The penalty to apply. This can vary depending on the stiffness of your materials");
113  params.addParam<Real>(
114  "penalty_friction",
115  1e8,
116  "The penalty factor to apply in mortar penalty frictional constraints. It is applied to the "
117  "tangential accumulated slip to build the frictional force");
118  params.addRangeCheckedParam<Real>(
119  "penalty_multiplier",
120  1.0,
121  "penalty_multiplier > 0",
122  "The growth factor for the penalty applied at the end of each augmented "
123  "Lagrange update iteration (a value larger than one, e.g., 10, tends to speed up "
124  "convergence.)");
125  params.addRangeCheckedParam<Real>(
126  "penalty_multiplier_friction",
127  1.0,
128  "penalty_multiplier_friction > 0",
129  "The penalty growth factor between augmented Lagrange "
130  "iterations for penalizing relative slip distance if the node is under stick conditions.(a "
131  "value larger than one, e.g., 10, tends to speed up convergence.)");
132  params.addParam<Real>("friction_coefficient", 0, "The friction coefficient");
133  params.addParam<Real>("tension_release",
134  0.0,
135  "Tension release threshold. A node in contact "
136  "will not be released if its tensile load is below "
137  "this value. No tension release if negative.");
138  params.addParam<MooseEnum>("model", ContactAction::getModelEnum(), "The contact model to use");
139  params.addParam<Real>("tangential_tolerance",
140  "Tangential distance to extend edges of contact surfaces");
141  params.addParam<Real>("capture_tolerance",
142  0.0,
143  "Normal distance from surface within which nodes are captured. This "
144  "parameter is used for node-face and mortar formulations.");
145  params.addParam<Real>(
146  "normal_smoothing_distance",
147  "Distance from edge in parametric coordinates over which to smooth contact normal");
148 
149  params.addParam<bool>("normalize_penalty",
150  false,
151  "Whether to normalize the penalty parameter with the nodal area.");
152  params.addParam<bool>(
153  "primary_secondary_jacobian",
154  true,
155  "Whether to include Jacobian entries coupling primary and secondary nodes.");
156  params.addParam<Real>("al_penetration_tolerance",
157  "The tolerance of the penetration for augmented Lagrangian method.");
158  params.addParam<Real>("al_incremental_slip_tolerance",
159  "The tolerance of the incremental slip for augmented Lagrangian method.");
160  params.addRangeCheckedParam<Real>(
161  "max_penalty_multiplier",
162  1.0e3,
163  "max_penalty_multiplier >= 1.0",
164  "Maximum multiplier applied to penalty factors when adaptivity is used in an augmented "
165  "Lagrange setting. The penalty factor supplied by the user is used as a reference to "
166  "determine its maximum. If this multiplier is too large, the condition number of the system "
167  "to be solved may be negatively impacted.");
168  MooseEnum adaptivity_penalty_normal("SIMPLE BUSSETTA", "SIMPLE");
169  adaptivity_penalty_normal.addDocumentation(
170  "SIMPLE", "Keep multiplying by the penalty multiplier between AL iterations");
171  adaptivity_penalty_normal.addDocumentation(
172  "BUSSETTA",
173  "Modify the penalty using an algorithm from Bussetta et al, 2012, Comput Mech 49:259-275 "
174  "between AL iterations.");
175  params.addParam<MooseEnum>(
176  "adaptivity_penalty_normal",
177  adaptivity_penalty_normal,
178  "The augmented Lagrange update strategy used on the normal penalty coefficient.");
179  MooseEnum adaptivity_penalty_friction("SIMPLE FRICTION_LIMIT", "FRICTION_LIMIT");
180  adaptivity_penalty_friction.addDocumentation(
181  "SIMPLE", "Keep multiplying by the frictional penalty multiplier between AL iterations");
182  adaptivity_penalty_friction.addDocumentation(
183  "FRICTION_LIMIT",
184  "This strategy will be guided by the Coulomb limit and be less reliant on the initial "
185  "penalty factor provided by the user.");
186  params.addParam<MooseEnum>(
187  "adaptivity_penalty_friction",
188  adaptivity_penalty_friction,
189  "The augmented Lagrange update strategy used on the frictional penalty coefficient.");
190  params.addParam<Real>("al_frictional_force_tolerance",
191  "The tolerance of the frictional force for augmented Lagrangian method.");
192  params.addParam<Real>(
193  "c_normal",
194  1e6,
195  "Parameter for balancing the size of the gap and contact pressure for a mortar formulation. "
196  "This purely numerical "
197  "parameter affects convergence behavior and, in general, should be larger for stiffer "
198  "materials. It is recommended that the user tries out various orders of magnitude for this "
199  "parameter if the default value generates poor contact convergence.");
200  params.addParam<Real>(
201  "c_tangential", 1, "Numerical parameter for nonlinear mortar frictional constraints");
202  params.addParam<bool>("ping_pong_protection",
203  false,
204  "Whether to protect against ping-ponging, e.g. the oscillation of the "
205  "secondary node between two "
206  "different primary faces, by tying the secondary node to the "
207  "edge between the involved primary faces");
208  params.addParam<Real>(
209  "normal_lm_scaling",
210  1.,
211  "Scaling factor to apply to the normal LM variable for a mortar formulation");
212  params.addParam<Real>(
213  "tangential_lm_scaling",
214  1.,
215  "Scaling factor to apply to the tangential LM variable for a mortar formulation");
216  params.addParam<bool>(
217  "normalize_c",
218  false,
219  "Whether to normalize c by weighting function norm for mortar contact. When unnormalized "
220  "the value of c effectively depends on element size since in the constraint we compare nodal "
221  "Lagrange Multiplier values to integrated gap values (LM nodal value is independent of "
222  "element size, where integrated values are dependent on element size).");
223  params.addClassDescription("Sets up all objects needed for mechanical contact enforcement");
224  params.addParam<bool>(
225  "use_dual",
226  "Whether to use the dual mortar approach within a mortar formulation. It is defaulted to "
227  "true for "
228  "weighted quantity approach, and to false for the legacy approach. To avoid instabilities "
229  "in the solution and obtain the full benefits of a variational enforcement,"
230  "use of dual mortar with weighted constraints is strongly recommended. This "
231  "input is only intended for advanced users.");
232  params.addParam<bool>(
233  "correct_edge_dropping",
234  false,
235  "Whether to enable correct edge dropping treatment for mortar constraints. When disabled "
236  "any Lagrange Multiplier degree of freedom on a secondary element without full primary "
237  "contributions will be set (strongly) to 0.");
238  params.addParam<bool>(
239  "generate_mortar_mesh",
240  true,
241  "Whether to generate the mortar mesh from the action. Typically this will be the case, but "
242  "one may also want to reuse an existing lower-dimensional mesh prior to a restart.");
243  params.addParam<MooseEnum>("automatic_pairing_method",
245  "The proximity method used for automatic pairing of boundaries.");
246  params.addParam<bool>(
247  "mortar_dynamics",
248  false,
249  "Whether to use constraints that account for the persistency condition, giving rise to "
250  "smoother normal contact pressure evolution. This flag should only be set to yes for dynamic "
251  "simulations using the Newmark-beta numerical integrator");
252  params.addParam<Real>(
253  "newmark_beta",
254  0.25,
255  "Newmark-beta beta parameter for its inclusion in the weighted gap update formula");
256  params.addParam<Real>(
257  "newmark_gamma",
258  0.5,
259  "Newmark-beta gamma parameter for its inclusion in the weighted gap update formula");
260  params.addCoupledVar("wear_depth",
261  "The name of the mortar auxiliary variable that is used to modify the "
262  "weighted gap definition");
263  params.addParam<std::vector<TagName>>(
264  "extra_vector_tags",
265  "The tag names for extra vectors that residual data should be saved into");
266  params.addParam<std::vector<TagName>>(
267  "absolute_value_vector_tags",
268  "The tags for the vectors this residual object should fill with the "
269  "absolute value of the residual contribution");
270  params.addParam<bool>(
271  "use_petrov_galerkin",
272  false,
273  "Whether to use the Petrov-Galerkin approach for the mortar-based constraints. If set to "
274  "true, we use the standard basis as the test function and dual basis as "
275  "the shape function for the interpolation of the Lagrange multiplier variable.");
276  params.addParam<bool>(
277  "debug_mesh",
278  false,
279  "Whether we are going to enable mortar segment mesh debug information. An exodus"
280  "file will be generated if the user sets this flag to true");
281  return params;
282 }
283 
285  : Action(params),
286  _boundary_pairs(getParam<BoundaryName, BoundaryName>("primary", "secondary")),
287  _model(getParam<MooseEnum>("model").getEnum<ContactModel>()),
288  _formulation(getParam<MooseEnum>("formulation").getEnum<ContactFormulation>()),
289  _generate_mortar_mesh(getParam<bool>("generate_mortar_mesh")),
290  _mortar_dynamics(getParam<bool>("mortar_dynamics"))
291 {
292  // Check for automatic selection of contact pairs.
293  if (getParam<std::vector<BoundaryName>>("automatic_pairing_boundaries").size() > 1)
295  getParam<std::vector<BoundaryName>>("automatic_pairing_boundaries");
296 
297  if (_automatic_pairing_boundaries.size() > 0 && !isParamValid("automatic_pairing_distance"))
298  paramError("automatic_pairing_distance",
299  "For automatic selection of contact pairs (for particular geometries) in contact "
300  "action, 'automatic_pairing_distance' needs to be provided.");
301 
302  if (_automatic_pairing_boundaries.size() > 0 && !isParamValid("automatic_pairing_method"))
303  paramError("automatic_pairing_distance",
304  "For automatic selection of contact pairs (for particular geometries) in contact "
305  "action, 'automatic_pairing_method' needs to be provided.");
306 
307  if (_automatic_pairing_boundaries.size() > 0 && _boundary_pairs.size() != 0)
308  paramError("automatic_pairing_boundaries",
309  "If a boundary list is provided, primary and secondary surfaces will be identified "
310  "automatically. Therefore, one cannot provide an automatic pairing boundary list "
311  "and primary/secondary lists.");
312  else if (_automatic_pairing_boundaries.size() == 0 && _boundary_pairs.size() == 0)
313  paramError("primary",
314  "'primary' and 'secondary' surfaces or a list of boundaries for automatic pair "
315  "generation need to be provided.");
316 
317  // End of checks for automatic selection of contact pairs.
318 
320  paramError("formulation", "When using mortar, a vector of contact pairs cannot be used");
321 
323  paramError("formulation",
324  "The 'tangential_penalty' formulation can only be used with the 'coulomb' model");
325 
327  {
328  // Use dual basis functions for contact traction interpolation
329  if (isParamValid("use_dual"))
330  _use_dual = getParam<bool>("use_dual");
331  else
332  _use_dual = true;
333 
335  paramError("model", "The 'mortar_penalty' formulation does not support glued contact");
336 
337  if (getParam<bool>("mortar_dynamics"))
338  paramError("mortar_dynamics",
339  "The 'mortar_penalty' formulation does not support implicit dynamic simulations");
340 
341  if (getParam<bool>("use_petrov_galerkin"))
342  paramError("use_petrov_galerkin",
343  "The 'mortar_penalty' formulation does not support usage of the Petrov-Galerkin "
344  "flag. The default (use_dual = true) behavior is such that contact tractions are "
345  "interpolated with dual bases whereas mortar or weighted contact quantities are "
346  "interpolated with Lagrange shape functions.");
347  }
348 
350  {
352  paramError("model", "The 'mortar' formulation does not support glued contact (yet)");
353 
354  // use dual basis function for Lagrange multipliers?
355  if (isParamValid("use_dual"))
356  _use_dual = getParam<bool>("use_dual");
357  else
358  _use_dual = true;
359 
360  if (!getParam<bool>("mortar_dynamics"))
361  {
362  if (params.isParamSetByUser("newmark_beta"))
363  paramError("newmark_beta", "newmark_beta can only be used with the mortar_dynamics option");
364 
365  if (params.isParamSetByUser("newmark_gamma"))
366  paramError("newmark_gamma",
367  "newmark_gamma can only be used with the mortar_dynamics option");
368  }
369 
370  if (isParamSetByUser("penalty"))
371  paramError("penalty",
372  "The 'penalty' parameter is not used for the 'mortar' formulation which instead "
373  "uses Lagrange multipliers");
374  }
375  else
376  {
377  if (params.isParamSetByUser("correct_edge_dropping"))
378  paramError(
379  "correct_edge_dropping",
380  "The 'correct_edge_dropping' option can only be used with the 'mortar' formulation "
381  "(weighted)");
382  else if (params.isParamSetByUser("use_dual") &&
384  paramError("use_dual",
385  "The 'use_dual' option can only be used with the 'mortar' formulation");
386  else if (params.isParamSetByUser("c_normal"))
387  paramError("c_normal",
388  "The 'c_normal' option can only be used with the 'mortar' formulation");
389  else if (params.isParamSetByUser("c_tangential"))
390  paramError("c_tangential",
391  "The 'c_tangential' option can only be used with the 'mortar' formulation");
392  else if (params.isParamSetByUser("mortar_dynamics"))
393  paramError("mortar_dynamics",
394  "The 'mortar_dynamics' constraint option can only be used with the 'mortar' "
395  "formulation and in dynamic simulations using Newmark-beta");
396  }
397 
399  {
400  if (isParamValid("secondary_gap_offset"))
401  paramError("secondary_gap_offset",
402  "The 'secondary_gap_offset' option can only be used with the "
403  "'MechanicalContactConstraint'");
404  if (isParamValid("mapped_primary_gap_offset"))
405  paramError("mapped_primary_gap_offset",
406  "The 'mapped_primary_gap_offset' option can only be used with the "
407  "'MechanicalContactConstraint'");
408  }
409  else if (getParam<bool>("ping_pong_protection"))
410  paramError("ping_pong_protection",
411  "The 'ping_pong_protection' option can only be used with the 'ranfs' formulation");
412 
413  // Remove repeated pairs from input file.
415 }
416 
417 void
419 {
420  if (_boundary_pairs.size() == 0 && _automatic_pairing_boundaries.size() == 0)
421  paramError(
422  "primary",
423  "Number of contact pairs in the contact action is zero. Please revise your input file.");
424 
425  // Remove repeated interactions
426  std::vector<std::pair<BoundaryName, BoundaryName>> lean_boundary_pairs;
427 
428  for (const auto & [primary, secondary] : _boundary_pairs)
429  {
430  // Structured bindings are not capturable (primary_copy, secondary_copy)
431  auto it = std::find_if(lean_boundary_pairs.begin(),
432  lean_boundary_pairs.end(),
433  [&, primary_copy = primary, secondary_copy = secondary](
434  const std::pair<BoundaryName, BoundaryName> & lean_pair)
435  {
436  const bool match_one = lean_pair.second == secondary_copy &&
437  lean_pair.first == primary_copy;
438  const bool match_two = lean_pair.second == primary_copy &&
439  lean_pair.first == secondary_copy;
440  const bool exist = match_one || match_two;
441  return exist;
442  });
443 
444  if (it == lean_boundary_pairs.end())
445  lean_boundary_pairs.emplace_back(primary, secondary);
446  else
447  mooseInfo("Contact pair ",
448  primary,
449  "--",
450  secondary,
451  " has been removed from the contact interaction list due to "
452  "duplicates in the input file.");
453  }
454 
455  _boundary_pairs = lean_boundary_pairs;
456 }
457 
458 void
460 {
461  // proform problem checks/corrections once during the first feasible task
462  if (_current_task == "add_contact_aux_variable")
463  {
464  if (!_problem->getDisplacedProblem())
465  mooseError(
466  "Contact requires updated coordinates. Use the 'displacements = ...' parameter in the "
467  "Mesh block.");
468 
469  // It is risky to apply this optimization to contact problems
470  // since the problem configuration may be changed during Jacobian
471  // evaluation. We therefore turn it off for all contact problems so that
472  // PETSc-3.8.4 or higher will have the same behavior as PETSc-3.8.3.
473  if (!_problem->isSNESMFReuseBaseSetbyUser())
474  _problem->setSNESMFReuseBase(false, false);
475  }
476 
480  else
482 
483  if (_current_task == "add_aux_kernel")
484  {
485  if (!_problem->getDisplacedProblem())
486  mooseError("Contact requires updated coordinates. Use the 'displacements = ...' line in the "
487  "Mesh block.");
488 
489  // Create auxiliary kernels for each contact pairs
490  for (const auto & contact_pair : _boundary_pairs)
491  {
492  const auto & [primary_name, secondary_name] = contact_pair;
495  {
496  InputParameters params = _factory.getValidParams("PenetrationAux");
497  params.applyParameters(parameters(),
498  {"secondary_gap_offset", "mapped_primary_gap_offset", "order"});
499 
500  std::vector<VariableName> displacements =
501  getParam<std::vector<VariableName>>("displacements");
502  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
503  .system()
504  .variable_type(displacements[0])
505  .order.get_order();
506 
507  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
508  params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL, EXEC_LINEAR};
509  params.set<std::vector<BoundaryName>>("boundary") = {secondary_name};
510  params.set<BoundaryName>("paired_boundary") = primary_name;
511  params.set<AuxVariableName>("variable") = "penetration";
512  if (isParamValid("secondary_gap_offset"))
513  params.set<std::vector<VariableName>>("secondary_gap_offset") = {
514  getParam<VariableName>("secondary_gap_offset")};
515  if (isParamValid("mapped_primary_gap_offset"))
516  params.set<std::vector<VariableName>>("mapped_primary_gap_offset") = {
517  getParam<VariableName>("mapped_primary_gap_offset")};
518  params.set<bool>("use_displaced_mesh") = true;
519  std::string name = _name + "_contact_" + Moose::stringify(contact_auxkernel_counter++);
520 
521  _problem->addAuxKernel("PenetrationAux", name, params);
522  }
523  else
524  {
525  const auto type = "MortarUserObjectAux";
527  params.set<std::vector<BoundaryName>>("boundary") = {secondary_name};
528  params.set<AuxVariableName>("variable") = "gap";
529  params.set<bool>("use_displaced_mesh") = true; // Unnecessary as this object only operates
530  // on nodes, but we'll do it for consistency
531  params.set<MooseEnum>("contact_quantity") = "normal_gap";
532  const auto & [primary_id, secondary_id, uo_name] =
533  libmesh_map_find(_bnd_pair_to_mortar_info, contact_pair);
534  params.set<UserObjectName>("user_object") = uo_name;
535  std::string name = _name + "_contact_gap_" + std::to_string(primary_id) + "_" +
536  std::to_string(secondary_id);
537 
538  _problem->addAuxKernel(type, name, params);
539  }
540  }
541 
543 
544  const unsigned int ndisp = getParam<std::vector<VariableName>>("displacements").size();
545 
546  // Add MortarFrictionalPressureVectorAux
548  {
549  {
550  InputParameters params = _factory.getValidParams("MortarFrictionalPressureVectorAux");
551 
552  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
553  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
554  params.set<std::vector<BoundaryName>>("boundary") = {_boundary_pairs[0].second};
555  params.set<ExecFlagEnum>("execute_on", true) = {EXEC_NONLINEAR};
556 
557  std::string action_name = MooseUtils::shortName(name());
558  const std::string tangential_lagrange_multiplier_name = action_name + "_tangential_lm";
559  const std::string tangential_lagrange_multiplier_3d_name =
560  action_name + "_tangential_3d_lm";
561 
562  params.set<std::vector<VariableName>>("tangent_one") = {
563  tangential_lagrange_multiplier_name};
564  params.set<std::vector<VariableName>>("tangent_two") = {
565  tangential_lagrange_multiplier_3d_name};
566 
567  std::vector<std::string> disp_components({"x", "y", "z"});
568  unsigned component_index = 0;
569 
570  // Loop over three displacements
571  for (const auto & disp_component : disp_components)
572  {
573  params.set<AuxVariableName>("variable") = _name + "_tangent_" + disp_component;
574  params.set<unsigned int>("component") = component_index;
575 
576  std::string name = _name + "_mortar_frictional_pressure_" + disp_component + "_" +
578 
579  _problem->addAuxKernel("MortarFrictionalPressureVectorAux", name, params);
580  component_index++;
581  }
582  }
583  }
584  }
585 
586  if (_current_task == "add_contact_aux_variable")
587  {
588  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
589  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
590  .system()
591  .variable_type(displacements[0])
592  .order.get_order();
593  std::unique_ptr<InputParameters> current_params;
594  const auto create_aux_var_params = [this, order, &current_params]() -> InputParameters &
595  {
596  current_params = std::make_unique<InputParameters>(_factory.getValidParams("MooseVariable"));
597  current_params->set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
598  current_params->set<MooseEnum>("family") = "LAGRANGE";
599  return *current_params;
600  };
601 
604  {
605  // Add penetration aux variable
606  _problem->addAuxVariable("MooseVariable", "penetration", create_aux_var_params());
607  // Add nodal area aux variable
608  _problem->addAuxVariable("MooseVariable", "nodal_area", create_aux_var_params());
609  }
610  else
611  _problem->addAuxVariable("MooseVariable", "gap", create_aux_var_params());
612 
613  // Add contact pressure aux variable
614  _problem->addAuxVariable("MooseVariable", "contact_pressure", create_aux_var_params());
615 
616  const unsigned int ndisp = getParam<std::vector<VariableName>>("displacements").size();
617 
618  // Add MortarFrictionalPressureVectorAux variables
620  {
621  {
622  std::vector<std::string> disp_components({"x", "y", "z"});
623  // Loop over three displacements
624  for (const auto & disp_component : disp_components)
625  {
626  auto var_params = _factory.getValidParams("MooseVariable");
627  var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
628  var_params.set<MooseEnum>("family") = "LAGRANGE";
629 
630  _problem->addAuxVariable(
631  "MooseVariable", _name + "_tangent_" + disp_component, var_params);
632  }
633  }
634  }
635  }
636 
637  if (_current_task == "add_user_object" && (_formulation != ContactFormulation::MORTAR) &&
639  {
640  auto var_params = _factory.getValidParams("NodalArea");
641 
642  // Get secondary_boundary_vector from possibly updated set from the
643  // ContactAction constructor cleanup
644  const auto actions = _awh.getActions<ContactAction>();
645 
646  std::vector<BoundaryName> secondary_boundary_vector;
647  for (const auto * const action : actions)
648  for (const auto j : index_range(action->_boundary_pairs))
649  secondary_boundary_vector.push_back(action->_boundary_pairs[j].second);
650 
651  var_params.set<std::vector<BoundaryName>>("boundary") = secondary_boundary_vector;
652  var_params.set<std::vector<VariableName>>("variable") = {"nodal_area"};
653 
654  mooseAssert(_problem, "Problem pointer is NULL");
655  var_params.set<ExecFlagEnum>("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN};
656  var_params.set<bool>("use_displaced_mesh") = true;
657 
658  _problem->addUserObject("NodalArea",
659  "nodal_area_object_" + Moose::stringify(contact_userobject_counter++),
660  var_params);
661  }
662 }
663 
664 void
666 {
667  // Increment counter for contact action objects
669 
672  {
673  // Add ContactPressureAux: Only one object for all contact pairs
674  const auto actions = _awh.getActions<ContactAction>();
675 
676  // Add auxiliary kernel if we are the last contact action object.
677  if (contact_action_counter == actions.size())
678  {
679  std::vector<BoundaryName> boundary_vector;
680  std::vector<BoundaryName> pair_boundary_vector;
681 
682  for (const auto * const action : actions)
683  for (const auto j : index_range(action->_boundary_pairs))
684  {
685  boundary_vector.push_back(action->_boundary_pairs[j].second);
686  pair_boundary_vector.push_back(action->_boundary_pairs[j].first);
687  }
688 
689  InputParameters params = _factory.getValidParams("ContactPressureAux");
690  params.applyParameters(parameters(), {"order"});
691 
692  std::vector<VariableName> displacements =
693  getParam<std::vector<VariableName>>("displacements");
694  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
695  .system()
696  .variable_type(displacements[0])
697  .order.get_order();
698 
699  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
700  params.set<std::vector<BoundaryName>>("boundary") = boundary_vector;
701  params.set<std::vector<BoundaryName>>("paired_boundary") = pair_boundary_vector;
702  params.set<AuxVariableName>("variable") = "contact_pressure";
703  params.addRequiredCoupledVar("nodal_area", "The nodal area");
704  params.set<std::vector<VariableName>>("nodal_area") = {"nodal_area"};
705  params.set<bool>("use_displaced_mesh") = true;
706 
707  std::string name = _name + "_contact_pressure";
708  params.set<ExecFlagEnum>("execute_on",
710  _problem->addAuxKernel("ContactPressureAux", name, params);
711  }
712  }
713  else
714  for (const auto & contact_pair : _boundary_pairs)
715  {
716  const auto & [_, secondary_name] = contact_pair;
717  const auto type = "MortarUserObjectAux";
719  params.set<std::vector<BoundaryName>>("boundary") = {secondary_name};
720  params.set<AuxVariableName>("variable") = "contact_pressure";
721  params.set<bool>("use_displaced_mesh") = true; // Unecessary as this object only operates on
722  // nodes, but we'll do it for consistency
723  params.set<MooseEnum>("contact_quantity") = "normal_pressure";
724  const auto & [primary_id, secondary_id, uo_name] =
725  libmesh_map_find(_bnd_pair_to_mortar_info, contact_pair);
726  params.set<UserObjectName>("user_object") = uo_name;
727  const std::string name = _name + "_contact_pressure" + std::to_string(primary_id) + "_" +
728  std::to_string(secondary_id);
729 
730  _problem->addAuxKernel(type, name, params);
731  }
732 }
733 
734 void
736 {
739  {
740  auto params = MortarConstraintBase::validParams();
741  params.set<bool>("use_displaced_mesh") = true;
742  std::string action_name = MooseUtils::shortName(name());
743  const std::string primary_subdomain_name = action_name + "_primary_subdomain";
744  const std::string secondary_subdomain_name = action_name + "_secondary_subdomain";
745  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
746  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
747  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
748  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
749  params.set<bool>("use_petrov_galerkin") = getParam<bool>("use_petrov_galerkin");
750  addRelationshipManagers(input_rm_type, params);
751  }
752 }
753 
754 void
756 {
757  std::string action_name = MooseUtils::shortName(name());
758 
759  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
760  const unsigned int ndisp = displacements.size();
761 
762  // Definitions for mortar contact.
763  const std::string primary_subdomain_name = action_name + "_primary_subdomain";
764  const std::string secondary_subdomain_name = action_name + "_secondary_subdomain";
765  const std::string normal_lagrange_multiplier_name = action_name + "_normal_lm";
766  const std::string tangential_lagrange_multiplier_name = action_name + "_tangential_lm";
767  const std::string tangential_lagrange_multiplier_3d_name = action_name + "_tangential_3d_lm";
768  const std::string auxiliary_lagrange_multiplier_name = action_name + "_aux_lm";
769 
770  if (_current_task == "append_mesh_generator")
771  {
772  // Don't do mesh generators when recovering or when the user has requested for us not to
773  // (presumably because the lower-dimensional blocks are already in the mesh due to manual
774  // addition or because we are restarting)
777  {
778  const MeshGeneratorName primary_name = primary_subdomain_name + "_generator";
779  const MeshGeneratorName secondary_name = secondary_subdomain_name + "_generator";
780 
781  auto primary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator");
782  auto secondary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator");
783 
784  primary_params.set<SubdomainName>("new_block_name") = primary_subdomain_name;
785  secondary_params.set<SubdomainName>("new_block_name") = secondary_subdomain_name;
786 
787  primary_params.set<std::vector<BoundaryName>>("sidesets") = {_boundary_pairs[0].first};
788  secondary_params.set<std::vector<BoundaryName>>("sidesets") = {_boundary_pairs[0].second};
789 
790  _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", primary_name, primary_params);
791  _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", secondary_name, secondary_params);
792  }
793  }
794 
795  // Add the lagrange multiplier on the secondary subdomain.
796  const auto addLagrangeMultiplier =
797  [this, &secondary_subdomain_name, &displacements](const std::string & variable_name,
798  const Real scaling_factor,
799  const bool add_aux_lm,
800  const bool penalty_traction) //
801  {
802  InputParameters params = _factory.getValidParams("MooseVariableBase");
803 
804  // Allow the user to select "weighted" constraints and standard bases (use_dual = false) or
805  // "legacy" constraints and dual bases (use_dual = true). Unless it's for testing purposes,
806  // this combination isn't recommended
807  if (!add_aux_lm || penalty_traction)
808  params.set<bool>("use_dual") = _use_dual;
809 
810  mooseAssert(_problem->systemBaseNonlinear(/*nl_sys_num=*/0).hasVariable(displacements[0]),
811  "Displacement variable is missing");
812  const auto primal_type =
813  _problem->systemBaseNonlinear(/*nl_sys_num=*/0).system().variable_type(displacements[0]);
814 
815  const int lm_order = primal_type.order.get_order();
816 
817  if (primal_type.family == LAGRANGE)
818  {
819  params.set<MooseEnum>("family") = Utility::enum_to_string<FEFamily>(primal_type.family);
820  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{lm_order});
821  }
822  else
823  mooseError("Invalid bases for mortar contact.");
824 
825  params.set<std::vector<SubdomainName>>("block") = {secondary_subdomain_name};
826  if (!(add_aux_lm || penalty_traction))
827  params.set<std::vector<Real>>("scaling") = {scaling_factor};
828 
829  auto fe_type = AddVariableAction::feType(params);
830  auto var_type = AddVariableAction::variableType(fe_type);
831  if (add_aux_lm || penalty_traction)
832  _problem->addAuxVariable(var_type, variable_name, params);
833  else
834  _problem->addVariable(var_type, variable_name, params);
835  };
836 
837  if (_current_task == "add_mortar_variable" && _formulation == ContactFormulation::MORTAR)
838  {
839  addLagrangeMultiplier(
840  normal_lagrange_multiplier_name, getParam<Real>("normal_lm_scaling"), false, false);
841 
843  {
844  addLagrangeMultiplier(tangential_lagrange_multiplier_name,
845  getParam<Real>("tangential_lm_scaling"),
846  false,
847  false);
848  if (ndisp > 2)
849  addLagrangeMultiplier(tangential_lagrange_multiplier_3d_name,
850  getParam<Real>("tangential_lm_scaling"),
851  false,
852  false);
853  }
854 
855  if (getParam<bool>("use_petrov_galerkin"))
856  addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0, true, false);
857  }
858  else if (_current_task == "add_mortar_variable" &&
860  {
861  if (_use_dual)
862  addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0, false, true);
863  }
864 
865  if (_current_task == "add_user_object")
866  {
867  const auto register_mortar_uo_name = [this](const auto & bnd_pair, const auto & uo_prefix)
868  {
869  const auto & [primary_name, secondary_name] = bnd_pair;
870  const auto primary_id = _mesh->getBoundaryID(primary_name);
871  const auto secondary_id = _mesh->getBoundaryID(secondary_name);
872  const auto uo_name = uo_prefix + name();
873  _bnd_pair_to_mortar_info.emplace(bnd_pair, MortarInfo{primary_id, secondary_id, uo_name});
874  return uo_name;
875  };
876 
877  // check if the correct problem class is selected if AL parameters are provided
879  !dynamic_cast<AugmentedLagrangianContactProblemInterface *>(_problem.get()))
880  {
881  const std::vector<std::string> params = {"penalty_multiplier",
882  "penalty_multiplier_friction",
883  "al_penetration_tolerance",
884  "al_incremental_slip_tolerance",
885  "al_frictional_force_tolerance"};
886  for (const auto & param : params)
887  if (parameters().isParamSetByUser(param))
888  paramError(param,
889  "Augmented Lagrange parameter was specified, but the selected problem type "
890  "does not support Augmented Lagrange iterations.");
891  }
892 
894  {
895  auto uo_params = _factory.getValidParams("LMWeightedGapUserObject");
896 
897  uo_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
898  uo_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
899  uo_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
900  uo_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
901  uo_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
902  uo_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
903  if (ndisp > 2)
904  uo_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
905  uo_params.set<bool>("use_displaced_mesh") = true;
906  uo_params.set<std::vector<VariableName>>("lm_variable") = {normal_lagrange_multiplier_name};
907  uo_params.applySpecificParameters(
908  parameters(), {"correct_edge_dropping", "use_petrov_galerkin", "debug_mesh"});
909  if (getParam<bool>("use_petrov_galerkin"))
910  uo_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
911 
912  _problem->addUserObject("LMWeightedGapUserObject",
913  register_mortar_uo_name(_boundary_pairs[0], "lm_weightedgap_object_"),
914  uo_params);
915  }
917  {
918  auto uo_params = _factory.getValidParams("LMWeightedVelocitiesUserObject");
919  uo_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
920  uo_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
921  uo_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
922  uo_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
923  uo_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
924  uo_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
925  if (ndisp > 2)
926  uo_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
927 
928  uo_params.set<VariableName>("secondary_variable") = displacements[0];
929  uo_params.set<bool>("use_displaced_mesh") = true;
930  uo_params.set<std::vector<VariableName>>("lm_variable_normal") = {
931  normal_lagrange_multiplier_name};
932  uo_params.set<std::vector<VariableName>>("lm_variable_tangential_one") = {
933  tangential_lagrange_multiplier_name};
934  if (ndisp > 2)
935  uo_params.set<std::vector<VariableName>>("lm_variable_tangential_two") = {
936  tangential_lagrange_multiplier_3d_name};
937  uo_params.applySpecificParameters(
938  parameters(), {"correct_edge_dropping", "use_petrov_galerkin", "debug_mesh"});
939  if (getParam<bool>("use_petrov_galerkin"))
940  uo_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
941 
942  const auto uo_name = _problem->addUserObject(
943  "LMWeightedVelocitiesUserObject",
944  register_mortar_uo_name(_boundary_pairs[0], "lm_weightedvelocities_object_"),
945  uo_params);
946  }
947 
949  {
950  auto uo_params = _factory.getValidParams("PenaltyWeightedGapUserObject");
951 
952  uo_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
953  uo_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
954  uo_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
955  uo_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
956  uo_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
957  uo_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
958 
959  // AL parameters
960  uo_params.applySpecificParameters(parameters(),
961  {"correct_edge_dropping",
962  "penalty",
963  "debug_mesh",
964  "max_penalty_multiplier",
965  "adaptivity_penalty_normal"});
966 
967  if (isParamValid("al_penetration_tolerance"))
968  uo_params.set<Real>("penetration_tolerance") = getParam<Real>("al_penetration_tolerance");
969  if (isParamValid("penalty_multiplier"))
970  uo_params.set<Real>("penalty_multiplier") = getParam<Real>("penalty_multiplier");
971  // In the contact action, we force the physical value of the normal gap, which also normalizes
972  // the penalty factor with the "area" around the node
973  uo_params.set<bool>("use_physical_gap") = true;
974 
975  if (_use_dual)
976  uo_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
977 
978  if (ndisp > 2)
979  uo_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
980  uo_params.set<bool>("use_displaced_mesh") = true;
981 
982  _problem->addUserObject(
983  "PenaltyWeightedGapUserObject",
984  register_mortar_uo_name(_boundary_pairs[0], "penalty_weightedgap_object_"),
985  uo_params);
986  _problem->haveADObjects(true);
987  }
989  {
990  auto uo_params = _factory.getValidParams("PenaltyFrictionUserObject");
991  uo_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
992  uo_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
993  uo_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
994  uo_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
995  uo_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
996  uo_params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
997  uo_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
998  if (ndisp > 2)
999  uo_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
1000 
1001  uo_params.set<VariableName>("secondary_variable") = displacements[0];
1002  uo_params.set<bool>("use_displaced_mesh") = true;
1003  uo_params.set<Real>("friction_coefficient") = getParam<Real>("friction_coefficient");
1004  uo_params.set<Real>("penalty") = getParam<Real>("penalty");
1005  uo_params.set<Real>("penalty_friction") = getParam<Real>("penalty_friction");
1006 
1007  // AL parameters
1008  uo_params.set<Real>("max_penalty_multiplier") = getParam<Real>("max_penalty_multiplier");
1009  uo_params.set<MooseEnum>("adaptivity_penalty_normal") =
1010  getParam<MooseEnum>("adaptivity_penalty_normal");
1011  uo_params.set<MooseEnum>("adaptivity_penalty_friction") =
1012  getParam<MooseEnum>("adaptivity_penalty_friction");
1013  if (isParamValid("al_penetration_tolerance"))
1014  uo_params.set<Real>("penetration_tolerance") = getParam<Real>("al_penetration_tolerance");
1015  if (isParamValid("penalty_multiplier"))
1016  uo_params.set<Real>("penalty_multiplier") = getParam<Real>("penalty_multiplier");
1017  if (isParamValid("penalty_multiplier_friction"))
1018  uo_params.set<Real>("penalty_multiplier_friction") =
1019  getParam<Real>("penalty_multiplier_friction");
1020 
1021  if (isParamValid("al_incremental_slip_tolerance"))
1022  uo_params.set<Real>("slip_tolerance") = getParam<Real>("al_incremental_slip_tolerance");
1023  // In the contact action, we force the physical value of the normal gap, which also normalizes
1024  // the penalty factor with the "area" around the node
1025  uo_params.set<bool>("use_physical_gap") = true;
1026 
1027  if (_use_dual)
1028  uo_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
1029 
1030  uo_params.applySpecificParameters(parameters(),
1031  {"friction_coefficient", "penalty", "penalty_friction"});
1032 
1033  _problem->addUserObject(
1034  "PenaltyFrictionUserObject",
1035  register_mortar_uo_name(_boundary_pairs[0], "penalty_friction_object_"),
1036  uo_params);
1037  _problem->haveADObjects(true);
1038  }
1039  }
1040 
1041  if (_current_task == "add_constraint")
1042  {
1043  // Prepare problem for enforcement with Lagrange multipliers
1045  {
1046  std::string mortar_constraint_name;
1047 
1048  if (!_mortar_dynamics)
1049  mortar_constraint_name = "ComputeWeightedGapLMMechanicalContact";
1050  else
1051  mortar_constraint_name = "ComputeDynamicWeightedGapLMMechanicalContact";
1052 
1053  InputParameters params = _factory.getValidParams(mortar_constraint_name);
1054  if (_mortar_dynamics)
1055  params.applySpecificParameters(
1056  parameters(), {"newmark_beta", "newmark_gamma", "capture_tolerance", "wear_depth"});
1057 
1058  else // We need user objects for quasistatic constraints
1059  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedgap_object_" + name();
1060 
1061  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
1062  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
1063  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
1064  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
1065  params.set<NonlinearVariableName>("variable") = normal_lagrange_multiplier_name;
1066  params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
1067  params.set<Real>("c") = getParam<Real>("c_normal");
1068 
1069  if (ndisp > 1)
1070  params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
1071  if (ndisp > 2)
1072  params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
1073 
1074  params.set<bool>("use_displaced_mesh") = true;
1075 
1077  {"correct_edge_dropping",
1078  "normalize_c",
1079  "extra_vector_tags",
1080  "absolute_value_vector_tags",
1081  "debug_mesh"});
1082 
1083  _problem->addConstraint(
1084  mortar_constraint_name, action_name + "_normal_lm_weighted_gap", params);
1085  _problem->haveADObjects(true);
1086  }
1087  // Add the tangential and normal Lagrange's multiplier constraints on the secondary boundary.
1089  {
1090  std::string mortar_constraint_name;
1091 
1092  if (!_mortar_dynamics)
1093  mortar_constraint_name = "ComputeFrictionalForceLMMechanicalContact";
1094  else
1095  mortar_constraint_name = "ComputeDynamicFrictionalForceLMMechanicalContact";
1096 
1097  InputParameters params = _factory.getValidParams(mortar_constraint_name);
1098  if (_mortar_dynamics)
1099  params.applySpecificParameters(
1100  parameters(), {"newmark_beta", "newmark_gamma", "capture_tolerance", "wear_depth"});
1101  else
1102  { // We need user objects for quasistatic constraints
1103  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedvelocities_object_" + name();
1104  params.set<UserObjectName>("weighted_velocities_uo") =
1105  "lm_weightedvelocities_object_" + name();
1106  }
1107 
1108  params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
1109  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
1110  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
1111  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
1112  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
1113  params.set<bool>("use_displaced_mesh") = true;
1114  params.set<Real>("c_t") = getParam<Real>("c_tangential");
1115  params.set<Real>("c") = getParam<Real>("c_normal");
1116  params.set<bool>("normalize_c") = getParam<bool>("normalize_c");
1117  params.set<bool>("compute_primal_residuals") = false;
1118 
1119  params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
1120 
1121  if (ndisp > 1)
1122  params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
1123  if (ndisp > 2)
1124  params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
1125 
1126  params.set<NonlinearVariableName>("variable") = normal_lagrange_multiplier_name;
1127  params.set<std::vector<VariableName>>("friction_lm") = {tangential_lagrange_multiplier_name};
1128 
1129  if (ndisp > 2)
1130  params.set<std::vector<VariableName>>("friction_lm_dir") = {
1131  tangential_lagrange_multiplier_3d_name};
1132 
1133  params.set<Real>("mu") = getParam<Real>("friction_coefficient");
1134  params.applySpecificParameters(
1135  parameters(), {"extra_vector_tags", "absolute_value_vector_tags", "debug_mesh"});
1136 
1137  _problem->addConstraint(mortar_constraint_name, action_name + "_tangential_lm", params);
1138  _problem->haveADObjects(true);
1139  }
1140 
1141  const auto addMechanicalContactConstraints =
1142  [this, &primary_subdomain_name, &secondary_subdomain_name, &displacements](
1143  const std::string & variable_name,
1144  const std::string & constraint_prefix,
1145  const std::string & constraint_type,
1146  const bool is_additional_frictional_constraint,
1147  const bool is_normal_constraint)
1148  {
1149  InputParameters params = _factory.getValidParams(constraint_type);
1150 
1151  params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
1152  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
1153  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
1154  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
1155  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
1156 
1158  params.set<NonlinearVariableName>("variable") = variable_name;
1159 
1160  params.set<bool>("use_displaced_mesh") = true;
1161  params.set<bool>("compute_lm_residuals") = false;
1162 
1163  // Additional displacement residual for frictional problem
1164  // The second frictional LM acts on a perpendicular direction.
1165  if (is_additional_frictional_constraint)
1166  params.set<MooseEnum>("direction") = "direction_2";
1167  params.applySpecificParameters(
1168  parameters(), {"extra_vector_tags", "absolute_value_vector_tags", "debug_mesh"});
1169 
1170  for (unsigned int i = 0; i < displacements.size(); ++i)
1171  {
1172  std::string constraint_name = constraint_prefix + Moose::stringify(i);
1173 
1174  params.set<VariableName>("secondary_variable") = displacements[i];
1175  params.set<MooseEnum>("component") = i;
1176 
1177  if (is_normal_constraint && _model != ContactModel::COULOMB &&
1179  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedgap_object_" + name();
1180  else if (is_normal_constraint && _model == ContactModel::COULOMB &&
1182  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedvelocities_object_" + name();
1184  params.set<UserObjectName>("weighted_velocities_uo") =
1185  "lm_weightedvelocities_object_" + name();
1186  else if (is_normal_constraint && _model != ContactModel::COULOMB &&
1188  params.set<UserObjectName>("weighted_gap_uo") = "penalty_weightedgap_object_" + name();
1189  else if (is_normal_constraint && _model == ContactModel::COULOMB &&
1191  params.set<UserObjectName>("weighted_gap_uo") = "penalty_friction_object_" + name();
1193  params.set<UserObjectName>("weighted_velocities_uo") =
1194  "penalty_friction_object_" + name();
1195 
1196  _problem->addConstraint(constraint_type, constraint_name, params);
1197  }
1198  _problem->haveADObjects(true);
1199  };
1200 
1201  // Add mortar mechanical contact constraint objects for primal variables
1202  addMechanicalContactConstraints(normal_lagrange_multiplier_name,
1203  action_name + "_normal_constraint_",
1204  "NormalMortarMechanicalContact",
1205  /* is_additional_frictional_constraint = */ false,
1206  /* is_normal_constraint = */ true);
1207 
1209  {
1210  addMechanicalContactConstraints(tangential_lagrange_multiplier_name,
1211  action_name + "_tangential_constraint_",
1212  "TangentialMortarMechanicalContact",
1213  /* is_additional_frictional_constraint = */ false,
1214  /* is_normal_constraint = */ false);
1215  if (ndisp > 2)
1216  addMechanicalContactConstraints(tangential_lagrange_multiplier_3d_name,
1217  action_name + "_tangential_constraint_3d_",
1218  "TangentialMortarMechanicalContact",
1219  /* is_additional_frictional_constraint = */ true,
1220  /* is_normal_constraint = */ false);
1221  }
1222  }
1223 }
1224 
1225 void
1227 {
1228  if (_current_task == "post_mesh_prepared" && _automatic_pairing_boundaries.size() > 0)
1229  {
1230  if (getParam<MooseEnum>("automatic_pairing_method").getEnum<ProximityMethod>() ==
1233  else if (getParam<MooseEnum>("automatic_pairing_method").getEnum<ProximityMethod>() ==
1236  }
1237 
1238  if (_current_task != "add_constraint")
1239  return;
1240 
1241  std::string action_name = MooseUtils::shortName(name());
1242  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
1243  const unsigned int ndisp = displacements.size();
1244 
1245  std::string constraint_type;
1246 
1248  constraint_type = "RANFSNormalMechanicalContact";
1249  else
1250  constraint_type = "MechanicalContactConstraint";
1251 
1252  InputParameters params = _factory.getValidParams(constraint_type);
1253 
1254  params.applyParameters(parameters(),
1255  {"displacements",
1256  "secondary_gap_offset",
1257  "mapped_primary_gap_offset",
1258  "primary",
1259  "secondary"});
1260 
1261  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
1262  .system()
1263  .variable_type(displacements[0])
1264  .order.get_order();
1265 
1266  params.set<std::vector<VariableName>>("displacements") = displacements;
1267  params.set<bool>("use_displaced_mesh") = true;
1268  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
1269 
1270  for (const auto & contact_pair : _boundary_pairs)
1271  {
1273  {
1274  params.set<std::vector<VariableName>>("nodal_area") = {"nodal_area"};
1275  params.set<BoundaryName>("boundary") = contact_pair.first;
1276  if (isParamValid("secondary_gap_offset"))
1277  params.set<std::vector<VariableName>>("secondary_gap_offset") = {
1278  getParam<VariableName>("secondary_gap_offset")};
1279  if (isParamValid("mapped_primary_gap_offset"))
1280  params.set<std::vector<VariableName>>("mapped_primary_gap_offset") = {
1281  getParam<VariableName>("mapped_primary_gap_offset")};
1282  }
1283 
1284  for (unsigned int i = 0; i < ndisp; ++i)
1285  {
1286  std::string name = action_name + "_constraint_" + Moose::stringify(contact_pair, "_") + "_" +
1287  Moose::stringify(i);
1288 
1290  params.set<MooseEnum>("component") = i;
1291  else
1292  params.set<unsigned int>("component") = i;
1293 
1294  params.set<BoundaryName>("primary") = contact_pair.first;
1295  params.set<BoundaryName>("secondary") = contact_pair.second;
1296  params.set<NonlinearVariableName>("variable") = displacements[i];
1297  params.set<std::vector<VariableName>>("primary_variable") = {displacements[i]};
1299  {"extra_vector_tags", "absolute_value_vector_tags"});
1300  _problem->addConstraint(constraint_type, name, params);
1301  }
1302  }
1303 }
1304 
1305 // Specialization for PointListAdaptor<MooseMesh::PeriodicNodeInfo>
1306 // Return node location from NodeBoundaryIDInfo pairs
1307 template <>
1308 inline const Point &
1310 {
1311  return *(item.first);
1312 }
1313 
1314 void
1316 {
1317  mooseInfo("The contact action is reading the list of boundaries and automatically pairs them "
1318  "if the distance between nodes is less than a specified distance.");
1319 
1320  if (!_mesh)
1321  mooseError("Failed to obtain mesh for automatically generating contact pairs.");
1322 
1323  if (!_mesh->getMesh().is_serial())
1324  paramError(
1325  "automatic_pairing_boundaries",
1326  "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1327 
1328  // Create automatic_pairing_boundaries_id
1329  std::vector<BoundaryID> _automatic_pairing_boundaries_id;
1330  for (const auto & sideset_name : _automatic_pairing_boundaries)
1331  _automatic_pairing_boundaries_id.emplace_back(_mesh->getBoundaryID(sideset_name));
1332 
1333  // Vector of pairs node-boundary id
1334  std::vector<NodeBoundaryIDInfo> node_boundary_id_vector;
1335 
1336  // Data structures to hold the boundary nodes
1337  const ConstBndNodeRange & bnd_nodes = *_mesh->getBoundaryNodeRange();
1338 
1339  for (const auto & bnode : bnd_nodes)
1340  {
1341  const BoundaryID boundary_id = bnode->_bnd_id;
1342  const Node * node_ptr = bnode->_node;
1343 
1344  // Make sure node is on a boundary chosen for contact mechanics
1345  auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1346  _automatic_pairing_boundaries_id.end(),
1347  boundary_id);
1348 
1349  if (it != _automatic_pairing_boundaries_id.end())
1350  node_boundary_id_vector.emplace_back(node_ptr, boundary_id);
1351  }
1352 
1353  // sort by increasing boundary id
1354  std::sort(node_boundary_id_vector.begin(),
1355  node_boundary_id_vector.end(),
1356  [](const NodeBoundaryIDInfo & first_pair, const NodeBoundaryIDInfo & second_pair)
1357  { return first_pair.second < second_pair.second; });
1358 
1359  // build kd-tree
1360  using KDTreeType = nanoflann::KDTreeSingleIndexAdaptor<
1361  nanoflann::L2_Simple_Adaptor<Real, PointListAdaptor<NodeBoundaryIDInfo>, Real, std::size_t>,
1363  LIBMESH_DIM,
1364  std::size_t>;
1365 
1366  // This parameter can be tuned. Others use '10'
1367  const unsigned int max_leaf_size = 20;
1368 
1369  // Build point list adaptor with all nodes-sidesets pairs for possible mechanical contact
1370  auto point_list = PointListAdaptor<NodeBoundaryIDInfo>(node_boundary_id_vector.begin(),
1371  node_boundary_id_vector.end());
1372  auto kd_tree = std::make_unique<KDTreeType>(
1373  LIBMESH_DIM, point_list, nanoflann::KDTreeSingleIndexAdaptorParams(max_leaf_size));
1374 
1375  if (!kd_tree)
1376  mooseError("Internal error. KDTree was not properly initialized in the contact action.");
1377 
1378  kd_tree->buildIndex();
1379 
1380  // data structures for kd-tree search
1381  nanoflann::SearchParameters search_params;
1382  std::vector<nanoflann::ResultItem<std::size_t, Real>> ret_matches;
1383 
1384  const auto radius_for_search = getParam<Real>("automatic_pairing_distance");
1385 
1386  // For all nodes
1387  for (const auto & pair : node_boundary_id_vector)
1388  {
1389  // clear result buffer
1390  ret_matches.clear();
1391 
1392  // position where we expect a periodic partner for the current node and boundary
1393  const Point search_point = *pair.first;
1394 
1395  // search at the expected point
1396  kd_tree->radiusSearch(
1397  &(search_point)(0), radius_for_search * radius_for_search, ret_matches, search_params);
1398 
1399  for (auto & match_pair : ret_matches)
1400  {
1401  const auto & match = node_boundary_id_vector[match_pair.first];
1402 
1403  //
1404  // If the proximity node identified belongs to a boundary in the input, add boundary pair
1405  //
1406 
1407  // Make sure node is on a boundary chosen for contact mechanics
1408  auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1409  _automatic_pairing_boundaries_id.end(),
1410  match.second);
1411 
1412  // If nodes are on the same boundary, pass.
1413  if (match.second == pair.second)
1414  continue;
1415 
1416  // At this point we will likely create many repeated pairs because many nodal pairs may
1417  // fulfill the distance condition imposed by the automatic_pairing_distance user input
1418  // parameter.
1419  if (it != _automatic_pairing_boundaries_id.end())
1420  {
1421  const auto index_one = cast_int<int>(it - _automatic_pairing_boundaries_id.begin());
1422  auto it_other = std::find(_automatic_pairing_boundaries_id.begin(),
1423  _automatic_pairing_boundaries_id.end(),
1424  pair.second);
1425 
1426  mooseAssert(it_other != _automatic_pairing_boundaries_id.end(),
1427  "Error in contact action. Unable to find boundary ID for node proximity "
1428  "automatic pairing.");
1429 
1430  const auto index_two = cast_int<int>(it_other - _automatic_pairing_boundaries_id.begin());
1431 
1432  if (pair.second > match.second)
1433  _boundary_pairs.push_back(
1435  else
1436  _boundary_pairs.push_back(
1438  }
1439  }
1440  }
1441 
1442  // Let's remove likely repeated pairs
1444 
1445  mooseInfo(
1446  "The following boundary pairs were created by the contact action using nodal proximity: ");
1447  for (const auto & [primary, secondary] : _boundary_pairs)
1449  "Primary boundary ID: ", primary, " and secondary boundary ID: ", secondary, ".");
1450 }
1451 
1452 void
1454 {
1455  mooseInfo("The contact action is reading the list of boundaries and automatically pairs them "
1456  "if their centroids fall within a specified distance of each other.");
1457 
1458  if (!_mesh)
1459  mooseError("Failed to obtain mesh for automatically generating contact pairs.");
1460 
1461  if (!_mesh->getMesh().is_serial())
1462  paramError(
1463  "automatic_pairing_boundaries",
1464  "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1465 
1466  // Compute centers of gravity for each sideset
1467  std::vector<std::pair<BoundaryName, Point>> automatic_pairing_boundaries_cog;
1468  const auto & sideset_ids = _mesh->meshSidesetIds();
1469 
1470  const auto & bnd_to_elem_map = _mesh->getBoundariesToActiveSemiLocalElemIds();
1471 
1472  for (const auto & sideset_name : _automatic_pairing_boundaries)
1473  {
1474  // If the sideset provided in the input file isn't in the mesh, error out.
1475  const auto find_set = sideset_ids.find(_mesh->getBoundaryID(sideset_name));
1476  if (find_set == sideset_ids.end())
1477  paramError("automatic_pairing_boundaries",
1478  sideset_name,
1479  " is not defined as a sideset in the mesh.");
1480 
1481  auto dofs_set = bnd_to_elem_map.find(_mesh->getBoundaryID(sideset_name));
1482 
1483  // Initialize data for sideset
1484  Point center_of_gravity(0, 0, 0);
1485  Real accumulated_sideset_area(0);
1486 
1487  // Pointer to lower-dimensional element on the sideset
1488  std::unique_ptr<const Elem> side_ptr;
1489  const std::unordered_set<dof_id_type> & bnd_elems = dofs_set->second;
1490 
1491  for (auto elem_id : bnd_elems)
1492  {
1493  const Elem * elem = _mesh->elemPtr(elem_id);
1494  unsigned int side = _mesh->sideWithBoundaryID(elem, _mesh->getBoundaryID(sideset_name));
1495 
1496  // update side_ptr
1497  elem->side_ptr(side_ptr, side);
1498 
1499  // area of the (linearized) side
1500  const auto side_area = side_ptr->volume();
1501 
1502  // position of the side
1503  const auto side_position = side_ptr->true_centroid();
1504 
1505  center_of_gravity += side_position * side_area;
1506  accumulated_sideset_area += side_area;
1507  }
1508 
1509  // Average each element's center of gravity (centroid) with its area
1510  center_of_gravity /= accumulated_sideset_area;
1511 
1512  // Add sideset-cog pair to vector
1513  automatic_pairing_boundaries_cog.emplace_back(sideset_name, center_of_gravity);
1514  }
1515 
1516  // Vectors of distances for each pair
1517  std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> pairs_distances;
1518 
1519  // Assign distances to identify nearby pairs.
1520  for (std::size_t i = 0; i < automatic_pairing_boundaries_cog.size() - 1; i++)
1521  for (std::size_t j = i + 1; j < automatic_pairing_boundaries_cog.size(); j++)
1522  {
1523  const Point & distance_vector =
1524  automatic_pairing_boundaries_cog[i].second - automatic_pairing_boundaries_cog[j].second;
1525 
1526  if (automatic_pairing_boundaries_cog[i].first != automatic_pairing_boundaries_cog[j].first)
1527  {
1528  const Real distance = distance_vector.norm();
1529  const std::pair pair = std::make_pair(automatic_pairing_boundaries_cog[i].first,
1530  automatic_pairing_boundaries_cog[j].first);
1531  pairs_distances.emplace_back(std::make_pair(pair, distance));
1532  }
1533  }
1534 
1535  const auto automatic_pairing_distance = getParam<Real>("automatic_pairing_distance");
1536 
1537  // Loop over all pairs
1538  std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> lean_pairs_distances;
1539  for (const auto & pair_distance : pairs_distances)
1540  if (pair_distance.second <= automatic_pairing_distance)
1541  {
1542  lean_pairs_distances.emplace_back(pair_distance);
1543  mooseInfoRepeated("Generating contact pair primary--secondary ",
1544  pair_distance.first.first,
1545  "--",
1546  pair_distance.first.second,
1547  ", with a relative distance of ",
1548  pair_distance.second);
1549  }
1550 
1551  // Create the boundary pairs (possibly with repeated pairs depending on user input)
1552  for (const auto & lean_pairs_distance : lean_pairs_distances)
1553  {
1554  // Make sure secondary surface's boundary ID is less than primary surface's boundary ID.
1555  // This is done to ensure some consistency in the boundary matching, which helps in defining
1556  // auxiliary kernels in the input file.
1557  if (_mesh->getBoundaryID(lean_pairs_distance.first.first) >
1558  _mesh->getBoundaryID(lean_pairs_distance.first.second))
1559  _boundary_pairs.push_back(
1560  {lean_pairs_distance.first.first, lean_pairs_distance.first.second});
1561  else
1562  _boundary_pairs.push_back(
1563  {lean_pairs_distance.first.second, lean_pairs_distance.first.first});
1564  }
1565 
1566  // Let's remove possibly repeated pairs
1568 }
1569 
1570 MooseEnum
1572 {
1573  return MooseEnum("frictionless glued coulomb", "frictionless");
1574 }
1575 
1576 MooseEnum
1578 {
1579  return MooseEnum("node centroid");
1580 }
1581 
1582 MooseEnum
1584 {
1585  auto formulations = MooseEnum(
1586  "ranfs kinematic penalty augmented_lagrange tangential_penalty mortar mortar_penalty",
1587  "kinematic");
1588 
1589  formulations.addDocumentation(
1590  "ranfs",
1591  "Reduced Active Nonlinear Function Set scheme for node-on-face contact. Provides exact "
1592  "enforcement without Lagrange multipliers or penalty terms.");
1593  formulations.addDocumentation(
1594  "kinematic",
1595  "Kinematic contact constraint enforcement transfers the internal forces at secondary nodes "
1596  "to the corresponding primary face for node-on-face contact. Provides exact "
1597  "enforcement without Lagrange multipliers or penalty terms.");
1598  formulations.addDocumentation(
1599  "penalty",
1600  "Node-on-face penalty based contact constraint enforcement. Interpenetration is penalized. "
1601  "Enforcement depends on the penalty magnitude. High penalties can introduce ill conditioning "
1602  "of the system.");
1603  formulations.addDocumentation("augmented_lagrange",
1604  "Node-on-face augmented Lagrange penalty based contact constraint "
1605  "enforcement. Interpenetration is enforced up to a user specified "
1606  "tolerance, ill-conditioning is generally avoided. Requires an "
1607  "Augmented Lagrange Problem class to be used in the simulation.");
1608  formulations.addDocumentation(
1609  "tangential_penalty",
1610  "Node-on-face penalty based frictional contact constraint enforcement. Interpenetration and "
1611  "slip distance for sticking nodes are penalized. Enforcement depends on the penalty "
1612  "magnitudes. High penalties can introduce ill conditioning of the system.");
1613  formulations.addDocumentation(
1614  "mortar",
1615  "Mortar based contact constraint enforcement using Lagrange multipliers. Provides exact "
1616  "enforcement and a variationally consistent formulation. Lagrange multipliers introduce a "
1617  "saddle point character in the system matrix which can have a negative impact on scalability "
1618  "with iterative solvers");
1619  formulations.addDocumentation(
1620  "mortar_penalty",
1621  "Mortar and penalty based contact constraint enforcement. When using an Augmented Lagrange "
1622  "Problem class this provides normal (and tangential) contact constratint enforced up to a "
1623  "user specified tolerances. Without AL the enforcement depends on the penalty magnitudes. "
1624  "High penalties can introduce ill conditioning of the system.");
1625 
1626  return formulations;
1627 }
1628 
1629 MooseEnum
1631 {
1632  return MooseEnum("Constraint", "Constraint");
1633 }
1634 
1635 MooseEnum
1637 {
1638  return MooseEnum("edge_based nodal_normal_based", "");
1639 }
1640 
1643 {
1645 
1646  params.addParam<MooseEnum>("normal_smoothing_method",
1648  "Method to use to smooth normals");
1649  params.addParam<Real>(
1650  "normal_smoothing_distance",
1651  "Distance from edge in parametric coordinates over which to smooth contact normal");
1652 
1653  params.addParam<MooseEnum>(
1654  "formulation", ContactAction::getFormulationEnum(), "The contact formulation");
1655 
1656  params.addParam<MooseEnum>("model", ContactAction::getModelEnum(), "The contact model to use");
1657 
1658  return params;
1659 }
Action class for creating constraints, kernels, and user objects necessary for mechanical contact...
Definition: ContactAction.h:44
LAGRANGE
void mooseInfo(Args &&... args) const
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:94
std::vector< BoundaryName > _automatic_pairing_boundaries
List of all possible boundaries for contact for automatic pairing (optional)
Definition: ContactAction.h:97
bool isUltimateMaster() const
void addDeprecatedParam(const std::string &name, const T &value, const std::string &doc_string, const std::string &deprecation_message)
static MooseEnum getFormulationEnum()
Get contact formulation.
RelationshipManagerType
const std::string & _name
virtual void addRelationshipManagers(Moose::RelationshipManagerType input_rm_type) override
ActionWarehouse & _awh
void paramError(const std::string &param, Args... args) const
const T & getParam(const std::string &name) const
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)
Factory & _factory
static InputParameters commonParameters()
Define parameters used by multiple contact objects.
const InputParameters & parameters() const
MooseApp & _app
ProximityMethod
Definition: ContactAction.h:34
static unsigned int contact_action_counter
Definition: ContactAction.C:55
T & set(const std::string &name, bool quiet_mode=false)
void removeRepeatedPairs()
Remove repeated contact pairs from _boundary_pairs.
const Point & getPoint(const PointObject &item) const
if(subdm)
InputParameters getValidParams(const std::string &name) const
static unsigned int contact_userobject_counter
Definition: ContactAction.C:52
void mooseInfoRepeated(Args &&... args)
void applyParameters(const InputParameters &common, const std::vector< std::string > &exclude={}, const bool allow_private=false)
static MooseEnum getSmoothingEnum()
Get smoothing type.
static MooseEnum getProximityMethod()
Get proximity method for automatic pairing.
const ContactFormulation _formulation
Contact formulation.
virtual void act() override
const ExecFlagType EXEC_TIMESTEP_END
void createSidesetPairsFromGeometry()
Create contact pairs between all boundaries whose centroids are within a user-specified distance of e...
Real distance(const Point &p)
InputParameters emptyInputParameters()
std::string shortName(const std::string &name)
static InputParameters validParams()
std::map< std::pair< BoundaryName, BoundaryName >, const MortarInfo > _bnd_pair_to_mortar_info
Map from boundary pair to mortar user object name.
static unsigned int contact_mortar_auxkernel_counter
Definition: ContactAction.C:46
const std::string & name() const
static InputParameters validParams()
void addMortarContact()
Generate mesh and other Moose objects for Mortar contact.
const bool _generate_mortar_mesh
Whether to generate the mortar mesh (useful in a restart simulation e.g.).
void addNodeFaceContact()
Generate constraints for node to face contact.
const ExecFlagType EXEC_TIMESTEP_BEGIN
boundary_id_type BoundaryID
static MooseEnum getSystemEnum()
Get contact system.
const std::string & type() const
const std::string & _current_task
static std::string variableType(const libMesh::FEType &fe_type, const bool is_fv=false, const bool is_array=false)
std::pair< const Node *, BoundaryID > NodeBoundaryIDInfo
Definition: ContactAction.C:43
const ExecFlagType EXEC_LINEAR
std::string stringify(const T &t)
const MeshGenerator & appendMeshGenerator(const std::string &type, const std::string &name, InputParameters params)
void addCoupledVar(const std::string &name, const std::string &doc_string)
std::pair< T, U > ResultItem
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
ContactFormulation
Definition: ContactAction.h:23
const bool _mortar_dynamics
Whether mortar dynamic contact constraints are to be used.
ContactAction(const InputParameters &params)
const ExecFlagType EXEC_NONLINEAR
bool isParamSetByUser(const std::string &name) const
void addContactPressureAuxKernel()
Add single contact pressure auxiliary kernel for various contact action objects.
registerMooseAction("ContactApp", ContactAction, "append_mesh_generator")
std::shared_ptr< MooseMesh > & _mesh
static libMesh::FEType feType(const InputParameters &params)
static MooseEnum getModelEnum()
Get contact model.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void createSidesetsFromNodeProximity()
Create contact pairs between all boundaries by determining that nodes on both boundaries are close en...
static unsigned int contact_auxkernel_counter
Definition: ContactAction.C:49
bool useMasterMesh() const
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
std::shared_ptr< FEProblemBase > & _problem
bool _use_dual
Whether to use the dual Mortar approach.
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
bool isParamValid(const std::string &name) const
ContactModel
Definition: ContactAction.h:16
std::vector< const T *> getActions()
const ContactModel _model
Contact model type enum.
bool isRecovering() const
SearchParams SearchParameters
bool isParamSetByUser(const std::string &name) const
auto index_range(const T &sizable)
static InputParameters validParams()
Definition: ContactAction.C:72
const ExecFlagType EXEC_INITIAL