www.mooseframework.org
ContactAction.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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  return params;
277 }
278 
280  : Action(params),
281  _boundary_pairs(getParam<BoundaryName, BoundaryName>("primary", "secondary")),
282  _model(getParam<MooseEnum>("model").getEnum<ContactModel>()),
283  _formulation(getParam<MooseEnum>("formulation").getEnum<ContactFormulation>()),
284  _generate_mortar_mesh(getParam<bool>("generate_mortar_mesh")),
285  _mortar_dynamics(getParam<bool>("mortar_dynamics"))
286 {
287  // Check for automatic selection of contact pairs.
288  if (getParam<std::vector<BoundaryName>>("automatic_pairing_boundaries").size() > 1)
290  getParam<std::vector<BoundaryName>>("automatic_pairing_boundaries");
291 
292  if (_automatic_pairing_boundaries.size() > 0 && !isParamValid("automatic_pairing_distance"))
293  paramError("automatic_pairing_distance",
294  "For automatic selection of contact pairs (for particular geometries) in contact "
295  "action, 'automatic_pairing_distance' needs to be provided.");
296 
297  if (_automatic_pairing_boundaries.size() > 0 && !isParamValid("automatic_pairing_method"))
298  paramError("automatic_pairing_distance",
299  "For automatic selection of contact pairs (for particular geometries) in contact "
300  "action, 'automatic_pairing_method' needs to be provided.");
301 
302  if (_automatic_pairing_boundaries.size() > 0 && _boundary_pairs.size() != 0)
303  paramError("automatic_pairing_boundaries",
304  "If a boundary list is provided, primary and secondary surfaces will be identified "
305  "automatically. Therefore, one cannot provide an automatic pairing boundary list "
306  "and primary/secondary lists.");
307  else if (_automatic_pairing_boundaries.size() == 0 && _boundary_pairs.size() == 0)
308  paramError("primary",
309  "'primary' and 'secondary' surfaces or a list of boundaries for automatic pair "
310  "generation need to be provided.");
311 
312  // End of checks for automatic selection of contact pairs.
313 
315  paramError("formulation", "When using mortar, a vector of contact pairs cannot be used");
316 
318  paramError("formulation",
319  "The 'tangential_penalty' formulation can only be used with the 'coulomb' model");
320 
322  {
323  // Use dual basis functions for contact traction interpolation
324  if (isParamValid("use_dual"))
325  _use_dual = getParam<bool>("use_dual");
326  else
327  _use_dual = true;
328 
330  paramError("model", "The 'mortar_penalty' formulation does not support glued contact");
331 
332  if (getParam<bool>("mortar_dynamics"))
333  paramError("mortar_dynamics",
334  "The 'mortar_penalty' formulation does not support implicit dynamic simulations");
335 
336  if (getParam<bool>("use_petrov_galerkin"))
337  paramError("use_petrov_galerkin",
338  "The 'mortar_penalty' formulation does not support usage of the Petrov-Galerkin "
339  "flag. The default (use_dual = true) behavior is such that contact tractions are "
340  "interpolated with dual bases whereas mortar or weighted contact quantities are "
341  "interpolated with Lagrange shape functions.");
342  }
343 
345  {
347  paramError("model", "The 'mortar' formulation does not support glued contact (yet)");
348 
349  // use dual basis function for Lagrange multipliers?
350  if (isParamValid("use_dual"))
351  _use_dual = getParam<bool>("use_dual");
352  else
353  _use_dual = true;
354 
355  if (!getParam<bool>("mortar_dynamics"))
356  {
357  if (params.isParamSetByUser("newmark_beta"))
358  paramError("newmark_beta", "newmark_beta can only be used with the mortar_dynamics option");
359 
360  if (params.isParamSetByUser("newmark_gamma"))
361  paramError("newmark_gamma",
362  "newmark_gamma can only be used with the mortar_dynamics option");
363  }
364  }
365  else
366  {
367  if (params.isParamSetByUser("correct_edge_dropping"))
368  paramError(
369  "correct_edge_dropping",
370  "The 'correct_edge_dropping' option can only be used with the 'mortar' formulation "
371  "(weighted)");
372  else if (params.isParamSetByUser("use_dual") &&
374  paramError("use_dual",
375  "The 'use_dual' option can only be used with the 'mortar' formulation");
376  else if (params.isParamSetByUser("c_normal"))
377  paramError("c_normal",
378  "The 'c_normal' option can only be used with the 'mortar' formulation");
379  else if (params.isParamSetByUser("c_tangential"))
380  paramError("c_tangential",
381  "The 'c_tangential' option can only be used with the 'mortar' formulation");
382  else if (params.isParamSetByUser("mortar_dynamics"))
383  paramError("mortar_dynamics",
384  "The 'mortar_dynamics' constraint option can only be used with the 'mortar' "
385  "formulation and in dynamic simulations using Newmark-beta");
386  }
387 
389  {
390  if (isParamValid("secondary_gap_offset"))
391  paramError("secondary_gap_offset",
392  "The 'secondary_gap_offset' option can only be used with the "
393  "'MechanicalContactConstraint'");
394  if (isParamValid("mapped_primary_gap_offset"))
395  paramError("mapped_primary_gap_offset",
396  "The 'mapped_primary_gap_offset' option can only be used with the "
397  "'MechanicalContactConstraint'");
398  }
399  else if (getParam<bool>("ping_pong_protection"))
400  paramError("ping_pong_protection",
401  "The 'ping_pong_protection' option can only be used with the 'ranfs' formulation");
402 
403  // Remove repeated pairs from input file.
405 }
406 
407 void
409 {
410  if (_boundary_pairs.size() == 0 && _automatic_pairing_boundaries.size() == 0)
411  paramError(
412  "primary",
413  "Number of contact pairs in the contact action is zero. Please revise your input file.");
414 
415  // Remove repeated interactions
416  std::vector<std::pair<BoundaryName, BoundaryName>> lean_boundary_pairs;
417 
418  for (const auto & [primary, secondary] : _boundary_pairs)
419  {
420  // Structured bindings are not capturable (primary_copy, secondary_copy)
421  auto it = std::find_if(lean_boundary_pairs.begin(),
422  lean_boundary_pairs.end(),
423  [&, primary_copy = primary, secondary_copy = secondary](
424  const std::pair<BoundaryName, BoundaryName> & lean_pair)
425  {
426  const bool match_one = lean_pair.second == secondary_copy &&
427  lean_pair.first == primary_copy;
428  const bool match_two = lean_pair.second == primary_copy &&
429  lean_pair.first == secondary_copy;
430  const bool exist = match_one || match_two;
431  return exist;
432  });
433 
434  if (it == lean_boundary_pairs.end())
435  lean_boundary_pairs.emplace_back(primary, secondary);
436  else
437  mooseInfo("Contact pair ",
438  primary,
439  "--",
440  secondary,
441  " has been removed from the contact interaction list due to "
442  "duplicates in the input file.");
443  }
444 
445  _boundary_pairs = lean_boundary_pairs;
446 }
447 
448 void
450 {
451  // proform problem checks/corrections once during the first feasible task
452  if (_current_task == "add_contact_aux_variable")
453  {
454  if (!_problem->getDisplacedProblem())
455  mooseError(
456  "Contact requires updated coordinates. Use the 'displacements = ...' parameter in the "
457  "Mesh block.");
458 
459  // It is risky to apply this optimization to contact problems
460  // since the problem configuration may be changed during Jacobian
461  // evaluation. We therefore turn it off for all contact problems so that
462  // PETSc-3.8.4 or higher will have the same behavior as PETSc-3.8.3.
463  if (!_problem->isSNESMFReuseBaseSetbyUser())
464  _problem->setSNESMFReuseBase(false, false);
465  }
466 
470  else
472 
473  if (_current_task == "add_aux_kernel")
474  { // Add ContactPenetrationAuxAction.
475  if (!_problem->getDisplacedProblem())
476  mooseError("Contact requires updated coordinates. Use the 'displacements = ...' line in the "
477  "Mesh block.");
478  // Create auxiliary kernels for each contact pairs
479  for (const auto & contact_pair : _boundary_pairs)
480  {
481  {
482  InputParameters params = _factory.getValidParams("PenetrationAux");
483  params.applyParameters(parameters(),
484  {"secondary_gap_offset", "mapped_primary_gap_offset", "order"});
485 
486  std::vector<VariableName> displacements =
487  getParam<std::vector<VariableName>>("displacements");
488  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
489  .system()
490  .variable_type(displacements[0])
491  .order.get_order();
492 
493  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
494  params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL, EXEC_LINEAR};
495  params.set<std::vector<BoundaryName>>("boundary") = {contact_pair.second};
496  params.set<BoundaryName>("paired_boundary") = contact_pair.first;
497  params.set<AuxVariableName>("variable") = "penetration";
498  if (isParamValid("secondary_gap_offset"))
499  params.set<std::vector<VariableName>>("secondary_gap_offset") = {
500  getParam<VariableName>("secondary_gap_offset")};
501  if (isParamValid("mapped_primary_gap_offset"))
502  params.set<std::vector<VariableName>>("mapped_primary_gap_offset") = {
503  getParam<VariableName>("mapped_primary_gap_offset")};
504  params.set<bool>("use_displaced_mesh") = true;
505  std::string name = _name + "_contact_" + Moose::stringify(contact_auxkernel_counter++);
506 
507  _problem->addAuxKernel("PenetrationAux", name, params);
508  }
509  }
510 
512 
513  const unsigned int ndisp = getParam<std::vector<VariableName>>("displacements").size();
514 
515  // Add MortarFrictionalPressureVectorAux
517  {
518  {
519  InputParameters params = _factory.getValidParams("MortarFrictionalPressureVectorAux");
520 
521  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
522  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
523  params.set<std::vector<BoundaryName>>("boundary") = {_boundary_pairs[0].second};
524  params.set<ExecFlagEnum>("execute_on", true) = {EXEC_NONLINEAR};
525 
526  std::string action_name = MooseUtils::shortName(name());
527  const std::string tangential_lagrange_multiplier_name = action_name + "_tangential_lm";
528  const std::string tangential_lagrange_multiplier_3d_name =
529  action_name + "_tangential_3d_lm";
530 
531  params.set<std::vector<VariableName>>("tangent_one") = {
532  tangential_lagrange_multiplier_name};
533  params.set<std::vector<VariableName>>("tangent_two") = {
534  tangential_lagrange_multiplier_3d_name};
535 
536  std::vector<std::string> disp_components({"x", "y", "z"});
537  unsigned component_index = 0;
538 
539  // Loop over three displacements
540  for (const auto & disp_component : disp_components)
541  {
542  params.set<AuxVariableName>("variable") = _name + "_tangent_" + disp_component;
543  params.set<unsigned int>("component") = component_index;
544 
545  std::string name = _name + "_mortar_frictional_pressure_" + disp_component + "_" +
547 
548  _problem->addAuxKernel("MortarFrictionalPressureVectorAux", name, params);
549  component_index++;
550  }
551  }
552  }
553  }
554 
555  if (_current_task == "add_contact_aux_variable")
556  {
557  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
558  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
559  .system()
560  .variable_type(displacements[0])
561  .order.get_order();
562  // Add ContactPenetrationVarAction
563  {
564  auto var_params = _factory.getValidParams("MooseVariable");
565  var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
566  var_params.set<MooseEnum>("family") = "LAGRANGE";
567 
568  _problem->addAuxVariable("MooseVariable", "penetration", var_params);
569  }
570  // Add ContactPressureVarAction
571  {
572  auto var_params = _factory.getValidParams("MooseVariable");
573  var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
574  var_params.set<MooseEnum>("family") = "LAGRANGE";
575 
576  _problem->addAuxVariable("MooseVariable", "contact_pressure", var_params);
577  }
578  // Add nodal area contact variable
579  {
580  auto var_params = _factory.getValidParams("MooseVariable");
581  var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
582  var_params.set<MooseEnum>("family") = "LAGRANGE";
583 
584  _problem->addAuxVariable("MooseVariable", "nodal_area", var_params);
585  }
586 
587  const unsigned int ndisp = getParam<std::vector<VariableName>>("displacements").size();
588 
589  // Add MortarFrictionalPressureVectorAux variables
591  {
592  {
593  std::vector<std::string> disp_components({"x", "y", "z"});
594  // Loop over three displacements
595  for (const auto & disp_component : disp_components)
596  {
597  auto var_params = _factory.getValidParams("MooseVariable");
598  var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
599  var_params.set<MooseEnum>("family") = "LAGRANGE";
600 
601  _problem->addAuxVariable(
602  "MooseVariable", _name + "_tangent_" + disp_component, var_params);
603  }
604  }
605  }
606  }
607 
608  if (_current_task == "add_user_object")
609  {
610  auto var_params = _factory.getValidParams("NodalArea");
611 
612  // Get secondary_boundary_vector from possibly updated set from the
613  // ContactAction constructor cleanup
614  const auto actions = _awh.getActions<ContactAction>();
615 
616  std::vector<BoundaryName> secondary_boundary_vector;
617  for (const auto * const action : actions)
618  for (const auto j : index_range(action->_boundary_pairs))
619  secondary_boundary_vector.push_back(action->_boundary_pairs[j].second);
620 
621  var_params.set<std::vector<BoundaryName>>("boundary") = secondary_boundary_vector;
622  var_params.set<std::vector<VariableName>>("variable") = {"nodal_area"};
623 
624  mooseAssert(_problem, "Problem pointer is NULL");
625  var_params.set<ExecFlagEnum>("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN};
626  var_params.set<bool>("use_displaced_mesh") = true;
627 
628  _problem->addUserObject("NodalArea",
629  "nodal_area_object_" + Moose::stringify(contact_userobject_counter++),
630  var_params);
631  }
632 }
633 
634 void
636 {
637  // Add ContactPressureAux: Only one object for all contact pairs
638  // if (_formulation != ContactFormulation::MORTAR)
639  const auto actions = _awh.getActions<ContactAction>();
640 
641  // Increment counter for contact action objects
643  // Add auxiliary kernel if we are the last contact action object.
644  if (contact_action_counter == actions.size())
645  {
646  std::vector<BoundaryName> boundary_vector;
647  std::vector<BoundaryName> pair_boundary_vector;
648 
649  for (const auto * const action : actions)
650  for (const auto j : index_range(action->_boundary_pairs))
651  {
652  boundary_vector.push_back(action->_boundary_pairs[j].second);
653  pair_boundary_vector.push_back(action->_boundary_pairs[j].first);
654  }
655 
656  InputParameters params = _factory.getValidParams("ContactPressureAux");
657  params.applyParameters(parameters(), {"order"});
658 
659  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
660  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
661  .system()
662  .variable_type(displacements[0])
663  .order.get_order();
664 
665  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
666  params.set<std::vector<BoundaryName>>("boundary") = boundary_vector;
667  params.set<std::vector<BoundaryName>>("paired_boundary") = pair_boundary_vector;
668  params.set<AuxVariableName>("variable") = "contact_pressure";
669  params.addRequiredCoupledVar("nodal_area", "The nodal area");
670  params.set<std::vector<VariableName>>("nodal_area") = {"nodal_area"};
671  params.set<bool>("use_displaced_mesh") = true;
672 
673  std::string name = _name + "_contact_pressure";
674  params.set<ExecFlagEnum>("execute_on",
676  _problem->addAuxKernel("ContactPressureAux", name, params);
677  }
678 }
679 
680 void
682 {
685  {
686  auto params = MortarConstraintBase::validParams();
687  params.set<bool>("use_displaced_mesh") = true;
688  std::string action_name = MooseUtils::shortName(name());
689  const std::string primary_subdomain_name = action_name + "_primary_subdomain";
690  const std::string secondary_subdomain_name = action_name + "_secondary_subdomain";
691  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
692  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
693  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
694  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
695  params.set<bool>("use_petrov_galerkin") = getParam<bool>("use_petrov_galerkin");
696  addRelationshipManagers(input_rm_type, params);
697  }
698 }
699 
700 void
702 {
703  std::string action_name = MooseUtils::shortName(name());
704 
705  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
706  const unsigned int ndisp = displacements.size();
707 
708  // Definitions for mortar contact.
709  const std::string primary_subdomain_name = action_name + "_primary_subdomain";
710  const std::string secondary_subdomain_name = action_name + "_secondary_subdomain";
711  const std::string normal_lagrange_multiplier_name = action_name + "_normal_lm";
712  const std::string tangential_lagrange_multiplier_name = action_name + "_tangential_lm";
713  const std::string tangential_lagrange_multiplier_3d_name = action_name + "_tangential_3d_lm";
714  const std::string auxiliary_lagrange_multiplier_name = action_name + "_aux_lm";
715 
716  if (_current_task == "append_mesh_generator")
717  {
718  // Don't do mesh generators when recovering or when the user has requested for us not to
719  // (presumably because the lower-dimensional blocks are already in the mesh due to manual
720  // addition or because we are restarting)
721  if (!(_app.isRecovering() && _app.isUltimateMaster()) && !_app.masterMesh() &&
723  {
724  const MeshGeneratorName primary_name = primary_subdomain_name + "_generator";
725  const MeshGeneratorName secondary_name = secondary_subdomain_name + "_generator";
726 
727  auto primary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator");
728  auto secondary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator");
729 
730  primary_params.set<SubdomainName>("new_block_name") = primary_subdomain_name;
731  secondary_params.set<SubdomainName>("new_block_name") = secondary_subdomain_name;
732 
733  primary_params.set<std::vector<BoundaryName>>("sidesets") = {_boundary_pairs[0].first};
734  secondary_params.set<std::vector<BoundaryName>>("sidesets") = {_boundary_pairs[0].second};
735 
736  _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", primary_name, primary_params);
737  _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", secondary_name, secondary_params);
738  }
739  }
740 
741  // Add the lagrange multiplier on the secondary subdomain.
742  const auto addLagrangeMultiplier =
743  [this, &secondary_subdomain_name, &displacements](const std::string & variable_name,
744  const Real scaling_factor,
745  const bool add_aux_lm,
746  const bool penalty_traction) //
747  {
748  InputParameters params = _factory.getValidParams("MooseVariableBase");
749 
750  // Allow the user to select "weighted" constraints and standard bases (use_dual = false) or
751  // "legacy" constraints and dual bases (use_dual = true). Unless it's for testing purposes,
752  // this combination isn't recommended
753  if (!add_aux_lm || penalty_traction)
754  params.set<bool>("use_dual") = _use_dual;
755 
756  mooseAssert(_problem->systemBaseNonlinear(/*nl_sys_num=*/0).hasVariable(displacements[0]),
757  "Displacement variable is missing");
758  const auto primal_type =
759  _problem->systemBaseNonlinear(/*nl_sys_num=*/0).system().variable_type(displacements[0]);
760 
761  const int lm_order = primal_type.order.get_order();
762 
763  if (primal_type.family == LAGRANGE)
764  {
765  params.set<MooseEnum>("family") = Utility::enum_to_string<FEFamily>(primal_type.family);
766  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{lm_order});
767  }
768  else
769  mooseError("Invalid bases for mortar contact.");
770 
771  params.set<std::vector<SubdomainName>>("block") = {secondary_subdomain_name};
772  if (!(add_aux_lm || penalty_traction))
773  params.set<std::vector<Real>>("scaling") = {scaling_factor};
774 
775  auto fe_type = AddVariableAction::feType(params);
776  auto var_type = AddVariableAction::variableType(fe_type);
777  if (add_aux_lm || penalty_traction)
778  _problem->addAuxVariable(var_type, variable_name, params);
779  else
780  _problem->addVariable(var_type, variable_name, params);
781  };
782 
783  if (_current_task == "add_mortar_variable" && _formulation == ContactFormulation::MORTAR)
784  {
785  addLagrangeMultiplier(
786  normal_lagrange_multiplier_name, getParam<Real>("normal_lm_scaling"), false, false);
787 
789  {
790  addLagrangeMultiplier(tangential_lagrange_multiplier_name,
791  getParam<Real>("tangential_lm_scaling"),
792  false,
793  false);
794  if (ndisp > 2)
795  addLagrangeMultiplier(tangential_lagrange_multiplier_3d_name,
796  getParam<Real>("tangential_lm_scaling"),
797  false,
798  false);
799  }
800 
801  if (getParam<bool>("use_petrov_galerkin"))
802  addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0, true, false);
803  }
804  else if (_current_task == "add_mortar_variable" &&
806  {
807  if (_use_dual)
808  addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0, false, true);
809  }
810 
811  if (_current_task == "add_user_object")
812  {
813  // check if the correct problem class is selected if AL parameters are provided
815  !dynamic_cast<AugmentedLagrangianContactProblemInterface *>(_problem.get()))
816  {
817  const std::vector<std::string> params = {"penalty_multiplier",
818  "penalty_multiplier_friction",
819  "al_penetration_tolerance",
820  "al_incremental_slip_tolerance",
821  "al_frictional_force_tolerance"};
822  for (const auto & param : params)
823  if (parameters().isParamSetByUser(param))
824  paramError(param,
825  "Augmented Lagrange parameter was specified, but the selected problem type "
826  "does not support Augmented Lagrange iterations.");
827  }
828 
830  {
831  auto var_params = _factory.getValidParams("LMWeightedGapUserObject");
832 
833  var_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
834  var_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
835  var_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
836  var_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
837  var_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
838  var_params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
839  var_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
840  if (ndisp > 2)
841  var_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
842  var_params.set<bool>("use_displaced_mesh") = true;
843  var_params.set<std::vector<VariableName>>("lm_variable") = {normal_lagrange_multiplier_name};
844  var_params.set<bool>("use_petrov_galerkin") = getParam<bool>("use_petrov_galerkin");
845  if (getParam<bool>("use_petrov_galerkin"))
846  var_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
847 
848  _problem->addUserObject(
849  "LMWeightedGapUserObject", "lm_weightedgap_object_" + name(), var_params);
850  }
852  {
853  auto var_params = _factory.getValidParams("LMWeightedVelocitiesUserObject");
854  var_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
855  var_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
856  var_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
857  var_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
858  var_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
859  var_params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
860  var_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
861  if (ndisp > 2)
862  var_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
863 
864  var_params.set<VariableName>("secondary_variable") = displacements[0];
865  var_params.set<bool>("use_displaced_mesh") = true;
866  var_params.set<std::vector<VariableName>>("lm_variable_normal") = {
867  normal_lagrange_multiplier_name};
868  var_params.set<std::vector<VariableName>>("lm_variable_tangential_one") = {
869  tangential_lagrange_multiplier_name};
870  if (ndisp > 2)
871  var_params.set<std::vector<VariableName>>("lm_variable_tangential_two") = {
872  tangential_lagrange_multiplier_3d_name};
873  var_params.set<bool>("use_petrov_galerkin") = getParam<bool>("use_petrov_galerkin");
874  if (getParam<bool>("use_petrov_galerkin"))
875  var_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
876 
877  _problem->addUserObject(
878  "LMWeightedVelocitiesUserObject", "lm_weightedvelocities_object_" + name(), var_params);
879  }
880 
882  {
883  auto var_params = _factory.getValidParams("PenaltyWeightedGapUserObject");
884 
885  var_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
886  var_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
887  var_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
888  var_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
889  var_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
890  var_params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
891  var_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
892  var_params.set<Real>("penalty") = getParam<Real>("penalty");
893 
894  // AL parameters
895  var_params.set<Real>("max_penalty_multiplier") = getParam<Real>("max_penalty_multiplier");
896  var_params.set<MooseEnum>("adaptivity_penalty_normal") =
897  getParam<MooseEnum>("adaptivity_penalty_normal");
898  if (isParamValid("al_penetration_tolerance"))
899  var_params.set<Real>("penetration_tolerance") = getParam<Real>("al_penetration_tolerance");
900  if (isParamValid("penalty_multiplier"))
901  var_params.set<Real>("penalty_multiplier") = getParam<Real>("penalty_multiplier");
902  // In the contact action, we force the physical value of the normal gap, which also normalizes
903  // the penalty factor with the "area" around the node
904  var_params.set<bool>("use_physical_gap") = true;
905 
906  if (_use_dual)
907  var_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
908 
909  if (ndisp > 2)
910  var_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
911  var_params.set<bool>("use_displaced_mesh") = true;
912 
913  _problem->addUserObject(
914  "PenaltyWeightedGapUserObject", "penalty_weightedgap_object_" + name(), var_params);
915  _problem->haveADObjects(true);
916  }
918  {
919  auto var_params = _factory.getValidParams("PenaltyFrictionUserObject");
920  var_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
921  var_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
922  var_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
923  var_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
924  var_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
925  var_params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
926  var_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
927  if (ndisp > 2)
928  var_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
929 
930  var_params.set<VariableName>("secondary_variable") = displacements[0];
931  var_params.set<bool>("use_displaced_mesh") = true;
932  var_params.set<Real>("friction_coefficient") = getParam<Real>("friction_coefficient");
933  var_params.set<Real>("penalty") = getParam<Real>("penalty");
934  var_params.set<Real>("penalty_friction") = getParam<Real>("penalty_friction");
935 
936  // AL parameters
937  var_params.set<Real>("max_penalty_multiplier") = getParam<Real>("max_penalty_multiplier");
938  var_params.set<MooseEnum>("adaptivity_penalty_normal") =
939  getParam<MooseEnum>("adaptivity_penalty_normal");
940  var_params.set<MooseEnum>("adaptivity_penalty_friction") =
941  getParam<MooseEnum>("adaptivity_penalty_friction");
942  if (isParamValid("al_penetration_tolerance"))
943  var_params.set<Real>("penetration_tolerance") = getParam<Real>("al_penetration_tolerance");
944  if (isParamValid("penalty_multiplier"))
945  var_params.set<Real>("penalty_multiplier") = getParam<Real>("penalty_multiplier");
946  if (isParamValid("penalty_multiplier_friction"))
947  var_params.set<Real>("penalty_multiplier_friction") =
948  getParam<Real>("penalty_multiplier_friction");
949 
950  if (isParamValid("al_incremental_slip_tolerance"))
951  var_params.set<Real>("slip_tolerance") = getParam<Real>("al_incremental_slip_tolerance");
952  // In the contact action, we force the physical value of the normal gap, which also normalizes
953  // the penalty factor with the "area" around the node
954  var_params.set<bool>("use_physical_gap") = true;
955 
956  if (_use_dual)
957  var_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
958 
959  var_params.applySpecificParameters(parameters(),
960  {"friction_coefficient", "penalty", "penalty_friction"});
961 
962  _problem->addUserObject(
963  "PenaltyFrictionUserObject", "penalty_friction_object_" + name(), var_params);
964  _problem->haveADObjects(true);
965  }
966  }
967 
968  if (_current_task == "add_constraint")
969  {
970  // Prepare problem for enforcement with Lagrange multipliers
972  {
973  std::string mortar_constraint_name;
974 
975  if (!_mortar_dynamics)
976  mortar_constraint_name = "ComputeWeightedGapLMMechanicalContact";
977  else
978  mortar_constraint_name = "ComputeDynamicWeightedGapLMMechanicalContact";
979 
980  InputParameters params = _factory.getValidParams(mortar_constraint_name);
981  if (_mortar_dynamics)
983  parameters(), {"newmark_beta", "newmark_gamma", "capture_tolerance", "wear_depth"});
984 
985  else // We need user objects for quasistatic constraints
986  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedgap_object_" + name();
987 
988  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
989  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
990  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
991  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
992  params.set<NonlinearVariableName>("variable") = normal_lagrange_multiplier_name;
993  params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
994  params.set<Real>("c") = getParam<Real>("c_normal");
995 
996  if (ndisp > 1)
997  params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
998  if (ndisp > 2)
999  params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
1000 
1001  params.set<bool>("use_displaced_mesh") = true;
1002 
1004  {"correct_edge_dropping",
1005  "normalize_c",
1006  "extra_vector_tags",
1007  "absolute_value_vector_tags"});
1008 
1009  _problem->addConstraint(
1010  mortar_constraint_name, action_name + "_normal_lm_weighted_gap", params);
1011  _problem->haveADObjects(true);
1012  }
1013  // Add the tangential and normal Lagrange's multiplier constraints on the secondary boundary.
1015  {
1016  std::string mortar_constraint_name;
1017 
1018  if (!_mortar_dynamics)
1019  mortar_constraint_name = "ComputeFrictionalForceLMMechanicalContact";
1020  else
1021  mortar_constraint_name = "ComputeDynamicFrictionalForceLMMechanicalContact";
1022 
1023  InputParameters params = _factory.getValidParams(mortar_constraint_name);
1024  if (_mortar_dynamics)
1025  params.applySpecificParameters(
1026  parameters(), {"newmark_beta", "newmark_gamma", "capture_tolerance", "wear_depth"});
1027  else
1028  { // We need user objects for quasistatic constraints
1029  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedvelocities_object_" + name();
1030  params.set<UserObjectName>("weighted_velocities_uo") =
1031  "lm_weightedvelocities_object_" + name();
1032  }
1033 
1034  params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
1035  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
1036  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
1037  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
1038  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
1039  params.set<bool>("use_displaced_mesh") = true;
1040  params.set<Real>("c_t") = getParam<Real>("c_tangential");
1041  params.set<Real>("c") = getParam<Real>("c_normal");
1042  params.set<bool>("normalize_c") = getParam<bool>("normalize_c");
1043  params.set<bool>("compute_primal_residuals") = false;
1044 
1045  params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
1046 
1047  if (ndisp > 1)
1048  params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
1049  if (ndisp > 2)
1050  params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
1051 
1052  params.set<NonlinearVariableName>("variable") = normal_lagrange_multiplier_name;
1053  params.set<std::vector<VariableName>>("friction_lm") = {tangential_lagrange_multiplier_name};
1054 
1055  if (ndisp > 2)
1056  params.set<std::vector<VariableName>>("friction_lm_dir") = {
1057  tangential_lagrange_multiplier_3d_name};
1058 
1059  params.set<Real>("mu") = getParam<Real>("friction_coefficient");
1061  {"extra_vector_tags", "absolute_value_vector_tags"});
1062 
1063  _problem->addConstraint(mortar_constraint_name, action_name + "_tangential_lm", params);
1064  _problem->haveADObjects(true);
1065  }
1066 
1067  const auto addMechanicalContactConstraints =
1068  [this, &primary_subdomain_name, &secondary_subdomain_name, &displacements](
1069  const std::string & variable_name,
1070  const std::string & constraint_prefix,
1071  const std::string & constraint_type,
1072  const bool is_additional_frictional_constraint,
1073  const bool is_normal_constraint)
1074  {
1075  InputParameters params = _factory.getValidParams(constraint_type);
1076 
1077  params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
1078  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
1079  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
1080  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
1081  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
1082 
1084  params.set<NonlinearVariableName>("variable") = variable_name;
1085 
1086  params.set<bool>("use_displaced_mesh") = true;
1087  params.set<bool>("compute_lm_residuals") = false;
1088 
1089  // Additional displacement residual for frictional problem
1090  // The second frictional LM acts on a perpendicular direction.
1091  if (is_additional_frictional_constraint)
1092  params.set<MooseEnum>("direction") = "direction_2";
1094  {"extra_vector_tags", "absolute_value_vector_tags"});
1095 
1096  for (unsigned int i = 0; i < displacements.size(); ++i)
1097  {
1098  std::string constraint_name = constraint_prefix + Moose::stringify(i);
1099 
1100  params.set<VariableName>("secondary_variable") = displacements[i];
1101  params.set<MooseEnum>("component") = i;
1102 
1103  if (is_normal_constraint && _model != ContactModel::COULOMB &&
1105  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedgap_object_" + name();
1106  else if (is_normal_constraint && _model == ContactModel::COULOMB &&
1108  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedvelocities_object_" + name();
1110  params.set<UserObjectName>("weighted_velocities_uo") =
1111  "lm_weightedvelocities_object_" + name();
1112  else if (is_normal_constraint && _model != ContactModel::COULOMB &&
1114  params.set<UserObjectName>("weighted_gap_uo") = "penalty_weightedgap_object_" + name();
1115  else if (is_normal_constraint && _model == ContactModel::COULOMB &&
1117  params.set<UserObjectName>("weighted_gap_uo") = "penalty_friction_object_" + name();
1119  params.set<UserObjectName>("weighted_velocities_uo") =
1120  "penalty_friction_object_" + name();
1121 
1122  _problem->addConstraint(constraint_type, constraint_name, params);
1123  }
1124  _problem->haveADObjects(true);
1125  };
1126 
1127  // Add mortar mechanical contact constraint objects for primal variables
1128  addMechanicalContactConstraints(normal_lagrange_multiplier_name,
1129  action_name + "_normal_constraint_",
1130  "NormalMortarMechanicalContact",
1131  /* is_additional_frictional_constraint = */ false,
1132  /* is_normal_constraint = */ true);
1133 
1135  {
1136  addMechanicalContactConstraints(tangential_lagrange_multiplier_name,
1137  action_name + "_tangential_constraint_",
1138  "TangentialMortarMechanicalContact",
1139  /* is_additional_frictional_constraint = */ false,
1140  /* is_normal_constraint = */ false);
1141  if (ndisp > 2)
1142  addMechanicalContactConstraints(tangential_lagrange_multiplier_3d_name,
1143  action_name + "_tangential_constraint_3d_",
1144  "TangentialMortarMechanicalContact",
1145  /* is_additional_frictional_constraint = */ true,
1146  /* is_normal_constraint = */ false);
1147  }
1148  }
1149 }
1150 
1151 void
1153 {
1154  if (_current_task == "post_mesh_prepared" && _automatic_pairing_boundaries.size() > 0)
1155  {
1156  if (getParam<MooseEnum>("automatic_pairing_method").getEnum<ProximityMethod>() ==
1159  else if (getParam<MooseEnum>("automatic_pairing_method").getEnum<ProximityMethod>() ==
1162  }
1163 
1164  if (_current_task != "add_constraint")
1165  return;
1166 
1167  std::string action_name = MooseUtils::shortName(name());
1168  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
1169  const unsigned int ndisp = displacements.size();
1170 
1171  std::string constraint_type;
1172 
1174  constraint_type = "RANFSNormalMechanicalContact";
1175  else
1176  constraint_type = "MechanicalContactConstraint";
1177 
1178  InputParameters params = _factory.getValidParams(constraint_type);
1179 
1180  params.applyParameters(parameters(),
1181  {"displacements",
1182  "secondary_gap_offset",
1183  "mapped_primary_gap_offset",
1184  "primary",
1185  "secondary"});
1186 
1187  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
1188  .system()
1189  .variable_type(displacements[0])
1190  .order.get_order();
1191 
1192  params.set<std::vector<VariableName>>("displacements") = displacements;
1193  params.set<bool>("use_displaced_mesh") = true;
1194  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
1195 
1196  for (const auto & contact_pair : _boundary_pairs)
1197  {
1199  {
1200  params.set<std::vector<VariableName>>("nodal_area") = {"nodal_area"};
1201  params.set<BoundaryName>("boundary") = contact_pair.first;
1202  if (isParamValid("secondary_gap_offset"))
1203  params.set<std::vector<VariableName>>("secondary_gap_offset") = {
1204  getParam<VariableName>("secondary_gap_offset")};
1205  if (isParamValid("mapped_primary_gap_offset"))
1206  params.set<std::vector<VariableName>>("mapped_primary_gap_offset") = {
1207  getParam<VariableName>("mapped_primary_gap_offset")};
1208  }
1209 
1210  for (unsigned int i = 0; i < ndisp; ++i)
1211  {
1212  std::string name = action_name + "_constraint_" + Moose::stringify(contact_pair, "_") + "_" +
1213  Moose::stringify(i);
1214 
1216  params.set<MooseEnum>("component") = i;
1217  else
1218  params.set<unsigned int>("component") = i;
1219 
1220  params.set<BoundaryName>("primary") = contact_pair.first;
1221  params.set<BoundaryName>("secondary") = contact_pair.second;
1222  params.set<NonlinearVariableName>("variable") = displacements[i];
1223  params.set<std::vector<VariableName>>("primary_variable") = {displacements[i]};
1225  {"extra_vector_tags", "absolute_value_vector_tags"});
1226  _problem->addConstraint(constraint_type, name, params);
1227  }
1228  }
1229 }
1230 
1231 // Specialization for PointListAdaptor<MooseMesh::PeriodicNodeInfo>
1232 // Return node location from NodeBoundaryIDInfo pairs
1233 template <>
1234 inline const Point &
1236 {
1237  return *(item.first);
1238 }
1239 
1240 void
1242 {
1243  mooseInfo("The contact action is reading the list of boundaries and automatically pairs them "
1244  "if the distance between nodes is less than a specified distance.");
1245 
1246  if (!_mesh)
1247  mooseError("Failed to obtain mesh for automatically generating contact pairs.");
1248 
1249  if (!_mesh->getMesh().is_serial())
1250  paramError(
1251  "automatic_pairing_boundaries",
1252  "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1253 
1254  // Create automatic_pairing_boundaries_id
1255  std::vector<BoundaryID> _automatic_pairing_boundaries_id;
1256  for (const auto & sideset_name : _automatic_pairing_boundaries)
1257  _automatic_pairing_boundaries_id.emplace_back(_mesh->getBoundaryID(sideset_name));
1258 
1259  // Vector of pairs node-boundary id
1260  std::vector<NodeBoundaryIDInfo> node_boundary_id_vector;
1261 
1262  // Data structures to hold the boundary nodes
1263  const ConstBndNodeRange & bnd_nodes = *_mesh->getBoundaryNodeRange();
1264 
1265  for (const auto & bnode : bnd_nodes)
1266  {
1267  const BoundaryID boundary_id = bnode->_bnd_id;
1268  const Node * node_ptr = bnode->_node;
1269 
1270  // Make sure node is on a boundary chosen for contact mechanics
1271  auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1272  _automatic_pairing_boundaries_id.end(),
1273  boundary_id);
1274 
1275  if (it != _automatic_pairing_boundaries_id.end())
1276  node_boundary_id_vector.emplace_back(node_ptr, boundary_id);
1277  }
1278 
1279  // sort by increasing boundary id
1280  std::sort(node_boundary_id_vector.begin(),
1281  node_boundary_id_vector.end(),
1282  [](const NodeBoundaryIDInfo & first_pair, const NodeBoundaryIDInfo & second_pair)
1283  { return first_pair.second < second_pair.second; });
1284 
1285  // build kd-tree
1286  using KDTreeType = nanoflann::KDTreeSingleIndexAdaptor<
1287  nanoflann::L2_Simple_Adaptor<Real, PointListAdaptor<NodeBoundaryIDInfo>, Real, std::size_t>,
1289  LIBMESH_DIM,
1290  std::size_t>;
1291 
1292  // This parameter can be tuned. Others use '10'
1293  const unsigned int max_leaf_size = 20;
1294 
1295  // Build point list adaptor with all nodes-sidesets pairs for possible mechanical contact
1296  auto point_list = PointListAdaptor<NodeBoundaryIDInfo>(node_boundary_id_vector.begin(),
1297  node_boundary_id_vector.end());
1298  auto kd_tree = std::make_unique<KDTreeType>(
1299  LIBMESH_DIM, point_list, nanoflann::KDTreeSingleIndexAdaptorParams(max_leaf_size));
1300 
1301  if (!kd_tree)
1302  mooseError("Internal error. KDTree was not properly initialized in the contact action.");
1303 
1304  kd_tree->buildIndex();
1305 
1306  // data structures for kd-tree search
1307  nanoflann::SearchParameters search_params;
1308  std::vector<nanoflann::ResultItem<std::size_t, Real>> ret_matches;
1309 
1310  const auto radius_for_search = getParam<Real>("automatic_pairing_distance");
1311 
1312  // For all nodes
1313  for (const auto & pair : node_boundary_id_vector)
1314  {
1315  // clear result buffer
1316  ret_matches.clear();
1317 
1318  // position where we expect a periodic partner for the current node and boundary
1319  const Point search_point = *pair.first;
1320 
1321  // search at the expected point
1322  kd_tree->radiusSearch(
1323  &(search_point)(0), radius_for_search * radius_for_search, ret_matches, search_params);
1324 
1325  for (auto & match_pair : ret_matches)
1326  {
1327  const auto & match = node_boundary_id_vector[match_pair.first];
1328 
1329  //
1330  // If the proximity node identified belongs to a boundary in the input, add boundary pair
1331  //
1332 
1333  // Make sure node is on a boundary chosen for contact mechanics
1334  auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1335  _automatic_pairing_boundaries_id.end(),
1336  match.second);
1337 
1338  // If nodes are on the same boundary, pass.
1339  if (match.second == pair.second)
1340  continue;
1341 
1342  // At this point we will likely create many repeated pairs because many nodal pairs may
1343  // fulfill the distance condition imposed by the automatic_pairing_distance user input
1344  // parameter.
1345  if (it != _automatic_pairing_boundaries_id.end())
1346  {
1347  const auto index_one = cast_int<int>(it - _automatic_pairing_boundaries_id.begin());
1348  auto it_other = std::find(_automatic_pairing_boundaries_id.begin(),
1349  _automatic_pairing_boundaries_id.end(),
1350  pair.second);
1351 
1352  mooseAssert(it_other != _automatic_pairing_boundaries_id.end(),
1353  "Error in contact action. Unable to find boundary ID for node proximity "
1354  "automatic pairing.");
1355 
1356  const auto index_two = cast_int<int>(it_other - _automatic_pairing_boundaries_id.begin());
1357 
1358  if (pair.second > match.second)
1359  _boundary_pairs.push_back(
1361  else
1362  _boundary_pairs.push_back(
1364  }
1365  }
1366  }
1367 
1368  // Let's remove likely repeated pairs
1370 
1371  mooseInfo(
1372  "The following boundary pairs were created by the contact action using nodal proximity: ");
1373  for (const auto & [primary, secondary] : _boundary_pairs)
1375  "Primary boundary ID: ", primary, " and secondary boundary ID: ", secondary, ".");
1376 }
1377 
1378 void
1380 {
1381  mooseInfo("The contact action is reading the list of boundaries and automatically pairs them "
1382  "if their centroids fall within a specified distance of each other.");
1383 
1384  if (!_mesh)
1385  mooseError("Failed to obtain mesh for automatically generating contact pairs.");
1386 
1387  if (!_mesh->getMesh().is_serial())
1388  paramError(
1389  "automatic_pairing_boundaries",
1390  "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1391 
1392  // Compute centers of gravity for each sideset
1393  std::vector<std::pair<BoundaryName, Point>> automatic_pairing_boundaries_cog;
1394  const auto & sideset_ids = _mesh->meshSidesetIds();
1395 
1396  const auto & bnd_to_elem_map = _mesh->getBoundariesToActiveSemiLocalElemIds();
1397 
1398  for (const auto & sideset_name : _automatic_pairing_boundaries)
1399  {
1400  // If the sideset provided in the input file isn't in the mesh, error out.
1401  const auto find_set = sideset_ids.find(_mesh->getBoundaryID(sideset_name));
1402  if (find_set == sideset_ids.end())
1403  paramError("automatic_pairing_boundaries",
1404  sideset_name,
1405  " is not defined as a sideset in the mesh.");
1406 
1407  auto dofs_set = bnd_to_elem_map.find(_mesh->getBoundaryID(sideset_name));
1408 
1409  // Initialize data for sideset
1410  Point center_of_gravity(0, 0, 0);
1411  Real accumulated_sideset_area(0);
1412 
1413  // Pointer to lower-dimensional element on the sideset
1414  std::unique_ptr<const Elem> side_ptr;
1415  const std::unordered_set<dof_id_type> & bnd_elems = dofs_set->second;
1416 
1417  for (auto elem_id : bnd_elems)
1418  {
1419  const Elem * elem = _mesh->elemPtr(elem_id);
1420  unsigned int side = _mesh->sideWithBoundaryID(elem, _mesh->getBoundaryID(sideset_name));
1421 
1422  // update side_ptr
1423  elem->side_ptr(side_ptr, side);
1424 
1425  // area of the (linearized) side
1426  const auto side_area = side_ptr->volume();
1427 
1428  // position of the side
1429  const auto side_position = side_ptr->true_centroid();
1430 
1431  center_of_gravity += side_position * side_area;
1432  accumulated_sideset_area += side_area;
1433  }
1434 
1435  // Average each element's center of gravity (centroid) with its area
1436  center_of_gravity /= accumulated_sideset_area;
1437 
1438  // Add sideset-cog pair to vector
1439  automatic_pairing_boundaries_cog.emplace_back(sideset_name, center_of_gravity);
1440  }
1441 
1442  // Vectors of distances for each pair
1443  std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> pairs_distances;
1444 
1445  // Assign distances to identify nearby pairs.
1446  for (std::size_t i = 0; i < automatic_pairing_boundaries_cog.size() - 1; i++)
1447  for (std::size_t j = i + 1; j < automatic_pairing_boundaries_cog.size(); j++)
1448  {
1449  const Point & distance_vector =
1450  automatic_pairing_boundaries_cog[i].second - automatic_pairing_boundaries_cog[j].second;
1451 
1452  if (automatic_pairing_boundaries_cog[i].first != automatic_pairing_boundaries_cog[j].first)
1453  {
1454  const Real distance = distance_vector.norm();
1455  const std::pair pair = std::make_pair(automatic_pairing_boundaries_cog[i].first,
1456  automatic_pairing_boundaries_cog[j].first);
1457  pairs_distances.emplace_back(std::make_pair(pair, distance));
1458  }
1459  }
1460 
1461  const auto automatic_pairing_distance = getParam<Real>("automatic_pairing_distance");
1462 
1463  // Loop over all pairs
1464  std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> lean_pairs_distances;
1465  for (const auto & pair_distance : pairs_distances)
1466  if (pair_distance.second <= automatic_pairing_distance)
1467  {
1468  lean_pairs_distances.emplace_back(pair_distance);
1469  mooseInfoRepeated("Generating contact pair primary--secondary ",
1470  pair_distance.first.first,
1471  "--",
1472  pair_distance.first.second,
1473  ", with a relative distance of ",
1474  pair_distance.second);
1475  }
1476 
1477  // Create the boundary pairs (possibly with repeated pairs depending on user input)
1478  for (const auto & lean_pairs_distance : lean_pairs_distances)
1479  {
1480  // Make sure secondary surface's boundary ID is less than primary surface's boundary ID.
1481  // This is done to ensure some consistency in the boundary matching, which helps in defining
1482  // auxiliary kernels in the input file.
1483  if (_mesh->getBoundaryID(lean_pairs_distance.first.first) >
1484  _mesh->getBoundaryID(lean_pairs_distance.first.second))
1485  _boundary_pairs.push_back(
1486  {lean_pairs_distance.first.first, lean_pairs_distance.first.second});
1487  else
1488  _boundary_pairs.push_back(
1489  {lean_pairs_distance.first.second, lean_pairs_distance.first.first});
1490  }
1491 
1492  // Let's remove possibly repeated pairs
1494 }
1495 
1496 MooseEnum
1498 {
1499  return MooseEnum("frictionless glued coulomb", "frictionless");
1500 }
1501 
1502 MooseEnum
1504 {
1505  return MooseEnum("node centroid");
1506 }
1507 
1508 MooseEnum
1510 {
1511  auto formulations = MooseEnum(
1512  "ranfs kinematic penalty augmented_lagrange tangential_penalty mortar mortar_penalty",
1513  "kinematic");
1514 
1515  formulations.addDocumentation(
1516  "ranfs",
1517  "Reduced Active Nonlinear Function Set scheme for node-on-face contact. Provides exact "
1518  "enforcement without Lagrange multipliers or penalty terms.");
1519  formulations.addDocumentation(
1520  "kinematic",
1521  "Kinematic contact constraint enforcement transfers the internal forces at secondary nodes "
1522  "to the corresponding primary face for node-on-face contact. Provides exact "
1523  "enforcement without Lagrange multipliers or penalty terms.");
1524  formulations.addDocumentation(
1525  "penalty",
1526  "Node-on-face penalty based contact constraint enforcement. Interpenetration is penalized. "
1527  "Enforcement depends on the penalty magnitude. High penalties can introduce ill conditioning "
1528  "of the system.");
1529  formulations.addDocumentation("augmented_lagrange",
1530  "Node-on-face augmented Lagrange penalty based contact constraint "
1531  "enforcement. Interpenetration is enforced up to a user specified "
1532  "tolerance, ill-conditioning is generally avoided. Requires an "
1533  "Augmented Lagrange Problem class to be used in the simulation.");
1534  formulations.addDocumentation(
1535  "tangential_penalty",
1536  "Node-on-face penalty based frictional contact constraint enforcement. Interpenetration and "
1537  "slip distance for sticking nodes are penalized. Enforcement depends on the penalty "
1538  "magnitudes. High penalties can introduce ill conditioning of the system.");
1539  formulations.addDocumentation(
1540  "mortar",
1541  "Mortar based contact constraint enforcement using Lagrange multipliers. Provides exact "
1542  "enforcement and a variationally consistent formulation. Lagrange multipliers introduce a "
1543  "saddle point character in the system matrix which can have a negative impact on scalability "
1544  "with iterative solvers");
1545  formulations.addDocumentation(
1546  "mortar_penalty",
1547  "Mortar and penalty based contact constraint enforcement. When using an Augmented Lagrange "
1548  "Problem class this provides normal (and tangential) contact constratint enforced up to a "
1549  "user specified tolerances. Without AL the enforcement depends on the penalty magnitudes. "
1550  "High penalties can introduce ill conditioning of the system.");
1551 
1552  return formulations;
1553 }
1554 
1555 MooseEnum
1557 {
1558  return MooseEnum("Constraint", "Constraint");
1559 }
1560 
1561 MooseEnum
1563 {
1564  return MooseEnum("edge_based nodal_normal_based", "");
1565 }
1566 
1569 {
1571 
1572  params.addParam<MooseEnum>("normal_smoothing_method",
1574  "Method to use to smooth normals");
1575  params.addParam<Real>(
1576  "normal_smoothing_distance",
1577  "Distance from edge in parametric coordinates over which to smooth contact normal");
1578 
1579  params.addParam<MooseEnum>(
1580  "formulation", ContactAction::getFormulationEnum(), "The contact formulation");
1581 
1582  params.addParam<MooseEnum>("model", ContactAction::getModelEnum(), "The contact model to use");
1583 
1584  return params;
1585 }
Action class for creating constraints, kernels, and user objects necessary for mechanical contact...
Definition: ContactAction.h:44
LAGRANGE
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
virtual void addRelationshipManagers(Moose::RelationshipManagerType input_rm_type) override
static std::string variableType(const FEType &fe_type, const bool is_fv=false, const bool is_array=false)
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)
void mooseInfo(Args &&... args) const
static InputParameters commonParameters()
Define parameters used by multiple contact objects.
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)
virtual const std::string & name() const
InputParameters emptyInputParameters()
std::string shortName(const std::string &name)
static InputParameters validParams()
bool isParamValid(const std::string &name) const
Factory & _factory
static unsigned int contact_mortar_auxkernel_counter
Definition: ContactAction.C:46
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 T & getParam(const std::string &name) const
const std::string & _current_task
const MooseMesh * masterMesh() const
std::pair< const Node *, BoundaryID > NodeBoundaryIDInfo
Definition: ContactAction.C:43
void paramError(const std::string &param, Args... args) const
const ExecFlagType EXEC_LINEAR
std::string stringify(const T &t)
const std::string _name
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 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
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
std::shared_ptr< FEProblemBase > & _problem
const InputParameters & parameters() const
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)
ContactModel
Definition: ContactAction.h:16
StoredRange< MooseMesh::const_bnd_node_iterator, const BndNode *> ConstBndNodeRange
std::vector< const T *> getActions()
const ContactModel _model
Contact model type enum.
bool isRecovering() const
SearchParams SearchParameters
auto index_range(const T &sizable)
static InputParameters validParams()
Definition: ContactAction.C:72
const ExecFlagType EXEC_INITIAL