https://mooseframework.inl.gov
Classes | Public Types | Public Member Functions | Static Public Member Functions | Public Attributes | Static Public Attributes | Protected Member Functions | Static Protected Member Functions | Protected Attributes | Private Member Functions | List of all members
ContactAction Class Reference

Action class for creating constraints, kernels, and user objects necessary for mechanical contact. More...

#include <ContactAction.h>

Inheritance diagram for ContactAction:
[legend]

Classes

struct  MortarInfo
 

Public Types

typedef DataFileName DataFileParameterType
 

Public Member Functions

 ContactAction (const InputParameters &params)
 
virtual void act () override
 
virtual void addRelationshipManagers (Moose::RelationshipManagerType input_rm_type) override
 
virtual void addRelationshipManagers (Moose::RelationshipManagerType when_type)
 
bool addRelationshipManagers (Moose::RelationshipManagerType when_type, const InputParameters &moose_object_pars)
 
void timedAct ()
 
MooseObjectName uniqueActionName () const
 
const std::string & specificTaskName () const
 
const std::set< std::string > & getAllTasks () const
 
void appendTask (const std::string &task)
 
MooseAppgetMooseApp () const
 
const std::string & type () const
 
const std::string & name () const
 
std::string typeAndName () const
 
MooseObjectParameterName uniqueParameterName (const std::string &parameter_name) const
 
MooseObjectName uniqueName () const
 
const InputParametersparameters () const
 
const hit::Node * getHitNode () const
 
bool hasBase () const
 
const std::string & getBase () const
 
const TgetParam (const std::string &name) const
 
std::vector< std::pair< T1, T2 > > getParam (const std::string &param1, const std::string &param2) const
 
const TqueryParam (const std::string &name) const
 
const TgetRenamedParam (const std::string &old_name, const std::string &new_name) const
 
T getCheckedPointerParam (const std::string &name, const std::string &error_string="") const
 
bool haveParameter (const std::string &name) const
 
bool isParamValid (const std::string &name) const
 
bool isParamSetByUser (const std::string &name) const
 
void connectControllableParams (const std::string &parameter, const std::string &object_type, const std::string &object_name, const std::string &object_parameter) const
 
void paramError (const std::string &param, Args... args) const
 
void paramWarning (const std::string &param, Args... args) const
 
void paramWarning (const std::string &param, Args... args) const
 
void paramInfo (const std::string &param, Args... args) const
 
std::string messagePrefix (const bool hit_prefix=true) const
 
std::string errorPrefix (const std::string &) const
 
void mooseError (Args &&... args) const
 
void mooseDocumentedError (const std::string &repo_name, const unsigned int issue_num, Args &&... args) const
 
void mooseErrorNonPrefixed (Args &&... args) const
 
void mooseWarning (Args &&... args) const
 
void mooseWarning (Args &&... args) const
 
void mooseWarningNonPrefixed (Args &&... args) const
 
void mooseWarningNonPrefixed (Args &&... args) const
 
void mooseDeprecated (Args &&... args) const
 
void mooseDeprecated (Args &&... args) const
 
void mooseDeprecatedNoTrace (Args &&... args) const
 
void mooseInfo (Args &&... args) const
 
void callMooseError (std::string msg, const bool with_prefix, const hit::Node *node=nullptr, const bool show_trace=true) const
 
std::string getDataFileName (const std::string &param) const
 
std::string getDataFileNameByName (const std::string &relative_path) const
 
std::string getDataFilePath (const std::string &relative_path) const
 
PerfGraphperfGraph ()
 
const Parallel::Communicator & comm () const
 
processor_id_type n_processors () const
 
processor_id_type processor_id () const
 

Static Public Member Functions

static InputParameters validParams ()
 
static MooseEnum getModelEnum ()
 Get contact model. More...
 
static MooseEnum getFormulationEnum ()
 Get contact formulation. More...
 
static MooseEnum getSystemEnum ()
 Get contact system. More...
 
static MooseEnum getSmoothingEnum ()
 Get smoothing type. More...
 
static MooseEnum getProximityMethod ()
 Get proximity method for automatic pairing. More...
 
static InputParameters commonParameters ()
 Define parameters used by multiple contact objects. More...
 
static void callMooseError (MooseApp *const app, const InputParameters &params, std::string msg, const bool with_prefix, const hit::Node *node, const bool show_trace=true)
 

Public Attributes

 usingCombinedWarningSolutionWarnings
 
const ConsoleStream _console
 

Static Public Attributes

static const std::string unique_action_name_param
 
static const std::string type_param
 
static const std::string name_param
 
static const std::string unique_name_param
 
static const std::string app_param
 
static const std::string moose_base_param
 
static const std::string kokkos_object_param
 
static constexpr auto SYSTEM
 
static constexpr auto NAME
 

Protected Member Functions

bool addRelationshipManagers (Moose::RelationshipManagerType when_type, const InputParameters &moose_object_pars)
 
void associateWithParameter (const std::string &param_name, InputParameters &params) const
 
void associateWithParameter (const InputParameters &from_params, const std::string &param_name, InputParameters &params) const
 
const TgetMeshProperty (const std::string &data_name, const std::string &prefix)
 
const TgetMeshProperty (const std::string &data_name)
 
bool hasMeshProperty (const std::string &data_name, const std::string &prefix) const
 
bool hasMeshProperty (const std::string &data_name, const std::string &prefix) const
 
bool hasMeshProperty (const std::string &data_name) const
 
bool hasMeshProperty (const std::string &data_name) const
 
std::string meshPropertyName (const std::string &data_name) const
 
PerfID registerTimedSection (const std::string &section_name, const unsigned int level) const
 
PerfID registerTimedSection (const std::string &section_name, const unsigned int level, const std::string &live_message, const bool print_dots=true) const
 
std::string timedSectionName (const std::string &section_name) const
 
void flagInvalidSolutionInternal (const InvalidSolutionID invalid_solution_id) const
 
InvalidSolutionID registerInvalidSolutionInternal (const std::string &message, const bool warning) const
 

Static Protected Member Functions

static std::string meshPropertyName (const std::string &data_name, const std::string &prefix)
 

Protected Attributes

std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
 Primary/Secondary boundary name pairs for mechanical contact. More...
 
std::vector< BoundaryName > _automatic_pairing_boundaries
 List of all possible boundaries for contact for automatic pairing (optional) More...
 
const ContactModel _model
 Contact model type enum. More...
 
const ContactFormulation _formulation
 Contact formulation. More...
 
bool _use_dual
 Whether to use the dual Mortar approach. More...
 
const bool _generate_mortar_mesh
 Whether to generate the mortar mesh (useful in a restart simulation e.g.). More...
 
const bool _mortar_dynamics
 Whether mortar dynamic contact constraints are to be used. More...
 
std::map< std::pair< BoundaryName, BoundaryName >, const MortarInfo_bnd_pair_to_mortar_info
 Map from boundary pair to mortar user object name. More...
 
std::string _registered_identifier
 
std::string _specific_task_name
 
std::set< std::string > _all_tasks
 
ActionWarehouse_awh
 
const std::string & _current_task
 
std::shared_ptr< MooseMesh > & _mesh
 
std::shared_ptr< MooseMesh > & _displaced_mesh
 
std::shared_ptr< FEProblemBase > & _problem
 
PerfID _act_timer
 
MooseApp_app
 
Factory_factory
 
ActionFactory_action_factory
 
const std::string & _type
 
const std::string & _name
 
const InputParameters_pars
 
MooseApp_pg_moose_app
 
const std::string _prefix
 
const Parallel::Communicator & _communicator
 

Private Member Functions

void addMortarContact ()
 Generate mesh and other Moose objects for Mortar contact. More...
 
void addNodeFaceContact ()
 Generate constraints for node to face contact. More...
 
void addContactPressureAuxKernel ()
 Add single contact pressure auxiliary kernel for various contact action objects. More...
 
void removeRepeatedPairs ()
 Remove repeated contact pairs from _boundary_pairs. More...
 
void createSidesetPairsFromGeometry ()
 Create contact pairs between all boundaries whose centroids are within a user-specified distance of each other. More...
 
void createSidesetsFromNodeProximity ()
 Create contact pairs between all boundaries by determining that nodes on both boundaries are close enough. More...
 

Detailed Description

Action class for creating constraints, kernels, and user objects necessary for mechanical contact.

Definition at line 31 of file ContactAction.h.

Constructor & Destructor Documentation

◆ ContactAction()

ContactAction::ContactAction ( const InputParameters params)

Definition at line 285 of file ContactAction.C.

286  : Action(params),
287  _boundary_pairs(getParam<BoundaryName, BoundaryName>("primary", "secondary")),
288  _model(getParam<MooseEnum>("model").getEnum<ContactModel>()),
289  _formulation(getParam<MooseEnum>("formulation").getEnum<ContactFormulation>()),
290  _generate_mortar_mesh(getParam<bool>("generate_mortar_mesh")),
291  _mortar_dynamics(getParam<bool>("mortar_dynamics"))
292 {
293  // Check for automatic selection of contact pairs.
294  if (getParam<std::vector<BoundaryName>>("automatic_pairing_boundaries").size() > 1)
296  getParam<std::vector<BoundaryName>>("automatic_pairing_boundaries");
297 
298  if (_automatic_pairing_boundaries.size() > 0 && !isParamValid("automatic_pairing_distance"))
299  paramError("automatic_pairing_distance",
300  "For automatic selection of contact pairs (for particular geometries) in contact "
301  "action, 'automatic_pairing_distance' needs to be provided.");
302 
303  if (_automatic_pairing_boundaries.size() > 0 && !isParamValid("automatic_pairing_method"))
304  paramError("automatic_pairing_distance",
305  "For automatic selection of contact pairs (for particular geometries) in contact "
306  "action, 'automatic_pairing_method' needs to be provided.");
307 
308  if (_automatic_pairing_boundaries.size() > 0 && _boundary_pairs.size() != 0)
309  paramError("automatic_pairing_boundaries",
310  "If a boundary list is provided, primary and secondary surfaces will be identified "
311  "automatically. Therefore, one cannot provide an automatic pairing boundary list "
312  "and primary/secondary lists.");
313  else if (_automatic_pairing_boundaries.size() == 0 && _boundary_pairs.size() == 0)
314  paramError("primary",
315  "'primary' and 'secondary' surfaces or a list of boundaries for automatic pair "
316  "generation need to be provided.");
317 
318  // End of checks for automatic selection of contact pairs.
319 
320  if (_boundary_pairs.size() != 1 && _formulation == ContactFormulation::MORTAR)
321  paramError("formulation", "When using mortar, a vector of contact pairs cannot be used");
322 
323  if (_formulation == ContactFormulation::TANGENTIAL_PENALTY && _model != ContactModel::COULOMB)
324  paramError("formulation",
325  "The 'tangential_penalty' formulation can only be used with the 'coulomb' model");
326 
327  if (_formulation == ContactFormulation::MORTAR_PENALTY)
328  {
329  // Use dual basis functions for contact traction interpolation
330  if (isParamValid("use_dual"))
331  _use_dual = getParam<bool>("use_dual");
332  else
333  _use_dual = true;
334 
335  if (_model == ContactModel::GLUED)
336  paramError("model", "The 'mortar_penalty' formulation does not support glued contact");
337 
338  if (getParam<bool>("mortar_dynamics"))
339  paramError("mortar_dynamics",
340  "The 'mortar_penalty' formulation does not support implicit dynamic simulations");
341 
342  if (getParam<bool>("use_petrov_galerkin"))
343  paramError("use_petrov_galerkin",
344  "The 'mortar_penalty' formulation does not support usage of the Petrov-Galerkin "
345  "flag. The default (use_dual = true) behavior is such that contact tractions are "
346  "interpolated with dual bases whereas mortar or weighted contact quantities are "
347  "interpolated with Lagrange shape functions.");
348  }
349 
350  if (_formulation == ContactFormulation::MORTAR)
351  {
352  if (_model == ContactModel::GLUED)
353  paramError("model", "The 'mortar' formulation does not support glued contact (yet)");
354 
355  // use dual basis function for Lagrange multipliers?
356  if (isParamValid("use_dual"))
357  _use_dual = getParam<bool>("use_dual");
358  else
359  _use_dual = true;
360 
361  if (!getParam<bool>("mortar_dynamics"))
362  {
363  if (params.isParamSetByUser("newmark_beta"))
364  paramError("newmark_beta", "newmark_beta can only be used with the mortar_dynamics option");
365 
366  if (params.isParamSetByUser("newmark_gamma"))
367  paramError("newmark_gamma",
368  "newmark_gamma can only be used with the mortar_dynamics option");
369  }
370 
371  if (isParamSetByUser("penalty"))
372  paramError("penalty",
373  "The 'penalty' parameter is not used for the 'mortar' formulation which instead "
374  "uses Lagrange multipliers");
375  }
376  else
377  {
378  if (params.isParamSetByUser("correct_edge_dropping"))
379  paramError(
380  "correct_edge_dropping",
381  "The 'correct_edge_dropping' option can only be used with the 'mortar' formulation "
382  "(weighted)");
383  else if (params.isParamSetByUser("use_dual") &&
384  _formulation != ContactFormulation::MORTAR_PENALTY)
385  paramError("use_dual",
386  "The 'use_dual' option can only be used with the 'mortar' formulation");
387  else if (params.isParamSetByUser("c_normal"))
388  paramError("c_normal",
389  "The 'c_normal' option can only be used with the 'mortar' formulation");
390  else if (params.isParamSetByUser("c_tangential"))
391  paramError("c_tangential",
392  "The 'c_tangential' option can only be used with the 'mortar' formulation");
393  else if (params.isParamSetByUser("mortar_dynamics"))
394  paramError("mortar_dynamics",
395  "The 'mortar_dynamics' constraint option can only be used with the 'mortar' "
396  "formulation and in dynamic simulations using Newmark-beta");
397  else if (params.isParamSetByUser("segment_quadrature"))
398  paramError("segment_quadrature",
399  "The 'segment_quadrature' option can only be used with the "
400  "'mortar' formulation.");
401  }
402 
403  if (_formulation == ContactFormulation::RANFS)
404  {
405  if (isParamValid("secondary_gap_offset"))
406  paramError("secondary_gap_offset",
407  "The 'secondary_gap_offset' option can only be used with the "
408  "'MechanicalContactConstraint'");
409  if (isParamValid("mapped_primary_gap_offset"))
410  paramError("mapped_primary_gap_offset",
411  "The 'mapped_primary_gap_offset' option can only be used with the "
412  "'MechanicalContactConstraint'");
413  }
414  else if (getParam<bool>("ping_pong_protection"))
415  paramError("ping_pong_protection",
416  "The 'ping_pong_protection' option can only be used with the 'ranfs' formulation");
417 
418  // Remove repeated pairs from input file.
420 }
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:81
std::vector< BoundaryName > _automatic_pairing_boundaries
List of all possible boundaries for contact for automatic pairing (optional)
Definition: ContactAction.h:84
void paramError(const std::string &param, Args... args) const
const T & getParam(const std::string &name) const
Action(const InputParameters &parameters)
void removeRepeatedPairs()
Remove repeated contact pairs from _boundary_pairs.
const ContactFormulation _formulation
Contact formulation.
Definition: ContactAction.h:90
const bool _generate_mortar_mesh
Whether to generate the mortar mesh (useful in a restart simulation e.g.).
Definition: ContactAction.h:96
const bool _mortar_dynamics
Whether mortar dynamic contact constraints are to be used.
Definition: ContactAction.h:99
bool isParamSetByUser(const std::string &name) const
bool _use_dual
Whether to use the dual Mortar approach.
Definition: ContactAction.h:93
bool isParamValid(const std::string &name) const
const ContactModel _model
Contact model type enum.
Definition: ContactAction.h:87
bool isParamSetByUser(const std::string &name) const

Member Function Documentation

◆ act()

void ContactAction::act ( )
overridevirtual

Implements Action.

Definition at line 464 of file ContactAction.C.

465 {
466  // proform problem checks/corrections once during the first feasible task
467  if (_current_task == "add_contact_aux_variable")
468  {
469  if (!_problem->getDisplacedProblem())
470  mooseError(
471  "Contact requires updated coordinates. Use the 'displacements = ...' parameter in the "
472  "Mesh block.");
473 
474  // It is risky to apply this optimization to contact problems
475  // since the problem configuration may be changed during Jacobian
476  // evaluation. We therefore turn it off for all contact problems so that
477  // PETSc-3.8.4 or higher will have the same behavior as PETSc-3.8.3.
478  if (!_problem->isSNESMFReuseBaseSetbyUser())
479  _problem->setSNESMFReuseBase(false, false);
480  }
481 
482  if (_formulation == ContactFormulation::MORTAR ||
483  _formulation == ContactFormulation::MORTAR_PENALTY)
485  else
487 
488  if (_current_task == "add_aux_kernel")
489  {
490  if (!_problem->getDisplacedProblem())
491  mooseError("Contact requires updated coordinates. Use the 'displacements = ...' line in the "
492  "Mesh block.");
493 
494  // Create auxiliary kernels for each contact pairs
495  for (const auto & contact_pair : _boundary_pairs)
496  {
497  const auto & [primary_name, secondary_name] = contact_pair;
498  if ((_formulation != ContactFormulation::MORTAR) &&
499  (_formulation != ContactFormulation::MORTAR_PENALTY))
500  {
501  InputParameters params = _factory.getValidParams("PenetrationAux");
502  params.applyParameters(parameters(),
503  {"secondary_gap_offset", "mapped_primary_gap_offset", "order"});
504 
505  std::vector<VariableName> displacements =
506  getParam<std::vector<VariableName>>("displacements");
507  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
508  .system()
509  .variable_type(displacements[0])
510  .order.get_order();
511 
512  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
513  params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL, EXEC_LINEAR};
514  params.set<std::vector<BoundaryName>>("boundary") = {secondary_name};
515  params.set<BoundaryName>("paired_boundary") = primary_name;
516  params.set<AuxVariableName>("variable") = "penetration";
517  if (isParamValid("secondary_gap_offset"))
518  params.set<std::vector<VariableName>>("secondary_gap_offset") = {
519  getParam<VariableName>("secondary_gap_offset")};
520  if (isParamValid("mapped_primary_gap_offset"))
521  params.set<std::vector<VariableName>>("mapped_primary_gap_offset") = {
522  getParam<VariableName>("mapped_primary_gap_offset")};
523  params.set<bool>("use_displaced_mesh") = true;
524  std::string name = _name + "_contact_" + Moose::stringify(contact_auxkernel_counter++);
525 
526  _problem->addAuxKernel("PenetrationAux", name, params);
527  }
528  else
529  {
530  const auto type = "MortarUserObjectAux";
532  params.set<std::vector<BoundaryName>>("boundary") = {secondary_name};
533  params.set<AuxVariableName>("variable") = "gap";
534  params.set<bool>("use_displaced_mesh") = true; // Unnecessary as this object only operates
535  // on nodes, but we'll do it for consistency
536  params.set<MooseEnum>("contact_quantity") = "normal_gap";
537  const auto & [primary_id, secondary_id, uo_name] =
538  libmesh_map_find(_bnd_pair_to_mortar_info, contact_pair);
539  params.set<UserObjectName>("user_object") = uo_name;
540  std::string name = _name + "_contact_gap_" + std::to_string(primary_id) + "_" +
541  std::to_string(secondary_id);
542 
543  _problem->addAuxKernel(type, name, params);
544  }
545  }
546 
548 
549  const unsigned int ndisp = getParam<std::vector<VariableName>>("displacements").size();
550 
551  // Add MortarFrictionalPressureVectorAux
552  if (_formulation == ContactFormulation::MORTAR && _model == ContactModel::COULOMB && ndisp > 2)
553  {
554  {
555  InputParameters params = _factory.getValidParams("MortarFrictionalPressureVectorAux");
556 
557  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
558  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
559  params.set<std::vector<BoundaryName>>("boundary") = {_boundary_pairs[0].second};
560  params.set<ExecFlagEnum>("execute_on", true) = {EXEC_NONLINEAR};
561 
562  std::string action_name = MooseUtils::shortName(name());
563  const std::string tangential_lagrange_multiplier_name = action_name + "_tangential_lm";
564  const std::string tangential_lagrange_multiplier_3d_name =
565  action_name + "_tangential_3d_lm";
566 
567  params.set<std::vector<VariableName>>("tangent_one") = {
568  tangential_lagrange_multiplier_name};
569  params.set<std::vector<VariableName>>("tangent_two") = {
570  tangential_lagrange_multiplier_3d_name};
571 
572  std::vector<std::string> disp_components({"x", "y", "z"});
573  unsigned component_index = 0;
574 
575  // Loop over three displacements
576  for (const auto & disp_component : disp_components)
577  {
578  params.set<AuxVariableName>("variable") = _name + "_tangent_" + disp_component;
579  params.set<unsigned int>("component") = component_index;
580 
581  std::string name = _name + "_mortar_frictional_pressure_" + disp_component + "_" +
583 
584  _problem->addAuxKernel("MortarFrictionalPressureVectorAux", name, params);
585  component_index++;
586  }
587  }
588  }
589  }
590 
591  if (_current_task == "add_contact_aux_variable")
592  {
593  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
594  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
595  .system()
596  .variable_type(displacements[0])
597  .order.get_order();
598  std::unique_ptr<InputParameters> current_params;
599  const auto create_aux_var_params = [this, order, &current_params]() -> InputParameters &
600  {
601  current_params = std::make_unique<InputParameters>(_factory.getValidParams("MooseVariable"));
602  current_params->set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
603  current_params->set<MooseEnum>("family") = "LAGRANGE";
604  return *current_params;
605  };
606 
607  if ((_formulation != ContactFormulation::MORTAR) &&
608  (_formulation != ContactFormulation::MORTAR_PENALTY))
609  {
610  // Add penetration aux variable
611  _problem->addAuxVariable("MooseVariable", "penetration", create_aux_var_params());
612  // Add nodal area aux variable
613  _problem->addAuxVariable("MooseVariable", "nodal_area", create_aux_var_params());
614  }
615  else
616  _problem->addAuxVariable("MooseVariable", "gap", create_aux_var_params());
617 
618  // Add contact pressure aux variable
619  _problem->addAuxVariable("MooseVariable", "contact_pressure", create_aux_var_params());
620 
621  const unsigned int ndisp = getParam<std::vector<VariableName>>("displacements").size();
622 
623  // Add MortarFrictionalPressureVectorAux variables
624  if (_formulation == ContactFormulation::MORTAR && _model == ContactModel::COULOMB && ndisp > 2)
625  {
626  {
627  std::vector<std::string> disp_components({"x", "y", "z"});
628  // Loop over three displacements
629  for (const auto & disp_component : disp_components)
630  {
631  auto var_params = _factory.getValidParams("MooseVariable");
632  var_params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
633  var_params.set<MooseEnum>("family") = "LAGRANGE";
634 
635  _problem->addAuxVariable(
636  "MooseVariable", _name + "_tangent_" + disp_component, var_params);
637  }
638  }
639  }
640  }
641 
642  if (_current_task == "add_user_object" && (_formulation != ContactFormulation::MORTAR) &&
643  (_formulation != ContactFormulation::MORTAR_PENALTY))
644  {
645  auto var_params = _factory.getValidParams("NodalArea");
646 
647  // Get secondary_boundary_vector from possibly updated set from the
648  // ContactAction constructor cleanup
649  const auto actions = _awh.getActions<ContactAction>();
650 
651  std::vector<BoundaryName> secondary_boundary_vector;
652  for (const auto * const action : actions)
653  for (const auto j : index_range(action->_boundary_pairs))
654  secondary_boundary_vector.push_back(action->_boundary_pairs[j].second);
655 
656  var_params.set<std::vector<BoundaryName>>("boundary") = secondary_boundary_vector;
657  var_params.set<std::vector<VariableName>>("variable") = {"nodal_area"};
658 
659  mooseAssert(_problem, "Problem pointer is NULL");
660  var_params.set<ExecFlagEnum>("execute_on", true) = {EXEC_INITIAL, EXEC_TIMESTEP_BEGIN};
661  var_params.set<bool>("use_displaced_mesh") = true;
662 
663  _problem->addUserObject("NodalArea",
664  "nodal_area_object_" + Moose::stringify(contact_userobject_counter++),
665  var_params);
666  }
667 }
Action class for creating constraints, kernels, and user objects necessary for mechanical contact...
Definition: ContactAction.h:31
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:81
const std::string & _name
ActionWarehouse & _awh
Factory & _factory
const InputParameters & parameters() const
T & set(const std::string &name, bool quiet_mode=false)
InputParameters getValidParams(const std::string &name) const
static unsigned int contact_userobject_counter
Definition: ContactAction.C:52
void applyParameters(const InputParameters &common, const std::vector< std::string > &exclude={}, const bool allow_private=false)
const ContactFormulation _formulation
Contact formulation.
Definition: ContactAction.h:90
std::string shortName(const std::string &name)
static unsigned int contact_mortar_auxkernel_counter
Definition: ContactAction.C:46
std::map< std::pair< BoundaryName, BoundaryName >, const MortarInfo > _bnd_pair_to_mortar_info
Map from boundary pair to mortar user object name.
const std::string & name() const
void addMortarContact()
Generate mesh and other Moose objects for Mortar contact.
void addNodeFaceContact()
Generate constraints for node to face contact.
const ExecFlagType EXEC_TIMESTEP_BEGIN
const std::string & type() const
const std::string & _current_task
const ExecFlagType EXEC_LINEAR
std::string stringify(const T &t)
const ExecFlagType EXEC_NONLINEAR
void addContactPressureAuxKernel()
Add single contact pressure auxiliary kernel for various contact action objects.
static unsigned int contact_auxkernel_counter
Definition: ContactAction.C:49
void mooseError(Args &&... args) const
std::shared_ptr< FEProblemBase > & _problem
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
bool isParamValid(const std::string &name) const
std::vector< const T *> getActions()
const ContactModel _model
Contact model type enum.
Definition: ContactAction.h:87
auto index_range(const T &sizable)
const ExecFlagType EXEC_INITIAL

◆ addContactPressureAuxKernel()

void ContactAction::addContactPressureAuxKernel ( )
private

Add single contact pressure auxiliary kernel for various contact action objects.

Definition at line 670 of file ContactAction.C.

Referenced by act().

671 {
672  // Increment counter for contact action objects
674 
675  if ((_formulation != ContactFormulation::MORTAR) &&
676  (_formulation != ContactFormulation::MORTAR_PENALTY))
677  {
678  // Add ContactPressureAux: Only one object for all contact pairs
679  const auto actions = _awh.getActions<ContactAction>();
680 
681  // Add auxiliary kernel if we are the last contact action object.
682  if (contact_action_counter == actions.size())
683  {
684  std::vector<BoundaryName> boundary_vector;
685  std::vector<BoundaryName> pair_boundary_vector;
686 
687  for (const auto * const action : actions)
688  for (const auto j : index_range(action->_boundary_pairs))
689  {
690  boundary_vector.push_back(action->_boundary_pairs[j].second);
691  pair_boundary_vector.push_back(action->_boundary_pairs[j].first);
692  }
693 
694  InputParameters params = _factory.getValidParams("ContactPressureAux");
695  params.applyParameters(parameters(), {"order"});
696 
697  std::vector<VariableName> displacements =
698  getParam<std::vector<VariableName>>("displacements");
699  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
700  .system()
701  .variable_type(displacements[0])
702  .order.get_order();
703 
704  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
705  params.set<std::vector<BoundaryName>>("boundary") = boundary_vector;
706  params.set<std::vector<BoundaryName>>("paired_boundary") = pair_boundary_vector;
707  params.set<AuxVariableName>("variable") = "contact_pressure";
708  params.addRequiredCoupledVar("nodal_area", "The nodal area");
709  params.set<std::vector<VariableName>>("nodal_area") = {"nodal_area"};
710  params.set<bool>("use_displaced_mesh") = true;
711 
712  std::string name = _name + "_contact_pressure";
713  params.set<ExecFlagEnum>("execute_on",
715  _problem->addAuxKernel("ContactPressureAux", name, params);
716  }
717  }
718  else
719  for (const auto & contact_pair : _boundary_pairs)
720  {
721  const auto & [_, secondary_name] = contact_pair;
722  const auto type = "MortarUserObjectAux";
724  params.set<std::vector<BoundaryName>>("boundary") = {secondary_name};
725  params.set<AuxVariableName>("variable") = "contact_pressure";
726  params.set<bool>("use_displaced_mesh") = true; // Unecessary as this object only operates on
727  // nodes, but we'll do it for consistency
728  params.set<MooseEnum>("contact_quantity") = "normal_pressure";
729  const auto & [primary_id, secondary_id, uo_name] =
730  libmesh_map_find(_bnd_pair_to_mortar_info, contact_pair);
731  params.set<UserObjectName>("user_object") = uo_name;
732  const std::string name = _name + "_contact_pressure" + std::to_string(primary_id) + "_" +
733  std::to_string(secondary_id);
734 
735  _problem->addAuxKernel(type, name, params);
736  }
737 }
Action class for creating constraints, kernels, and user objects necessary for mechanical contact...
Definition: ContactAction.h:31
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:81
const std::string & _name
ActionWarehouse & _awh
Factory & _factory
const InputParameters & parameters() const
static unsigned int contact_action_counter
Definition: ContactAction.C:55
T & set(const std::string &name, bool quiet_mode=false)
InputParameters getValidParams(const std::string &name) const
void applyParameters(const InputParameters &common, const std::vector< std::string > &exclude={}, const bool allow_private=false)
const ContactFormulation _formulation
Contact formulation.
Definition: ContactAction.h:90
const ExecFlagType EXEC_TIMESTEP_END
std::map< std::pair< BoundaryName, BoundaryName >, const MortarInfo > _bnd_pair_to_mortar_info
Map from boundary pair to mortar user object name.
const std::string & name() const
const ExecFlagType EXEC_TIMESTEP_BEGIN
const std::string & type() const
void addRequiredCoupledVar(const std::string &name, const std::string &doc_string)
const ExecFlagType EXEC_NONLINEAR
std::shared_ptr< FEProblemBase > & _problem
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
std::vector< const T *> getActions()
auto index_range(const T &sizable)

◆ addMortarContact()

void ContactAction::addMortarContact ( )
private

Generate mesh and other Moose objects for Mortar contact.

Definition at line 760 of file ContactAction.C.

Referenced by act().

761 {
762  std::string action_name = MooseUtils::shortName(name());
763 
764  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
765  const unsigned int ndisp = displacements.size();
766 
767  // Definitions for mortar contact.
768  const std::string primary_subdomain_name = action_name + "_primary_subdomain";
769  const std::string secondary_subdomain_name = action_name + "_secondary_subdomain";
770  const std::string normal_lagrange_multiplier_name = action_name + "_normal_lm";
771  const std::string tangential_lagrange_multiplier_name = action_name + "_tangential_lm";
772  const std::string tangential_lagrange_multiplier_3d_name = action_name + "_tangential_3d_lm";
773  const std::string auxiliary_lagrange_multiplier_name = action_name + "_aux_lm";
774 
775  if (_current_task == "append_mesh_generator")
776  {
777  // Don't do mesh generators when recovering or when the user has requested for us not to
778  // (presumably because the lower-dimensional blocks are already in the mesh due to manual
779  // addition or because we are restarting)
782  {
783  const MeshGeneratorName primary_name = primary_subdomain_name + "_generator";
784  const MeshGeneratorName secondary_name = secondary_subdomain_name + "_generator";
785 
786  auto primary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator");
787  auto secondary_params = _factory.getValidParams("LowerDBlockFromSidesetGenerator");
788 
789  primary_params.set<SubdomainName>("new_block_name") = primary_subdomain_name;
790  secondary_params.set<SubdomainName>("new_block_name") = secondary_subdomain_name;
791 
792  primary_params.set<std::vector<BoundaryName>>("sidesets") = {_boundary_pairs[0].first};
793  secondary_params.set<std::vector<BoundaryName>>("sidesets") = {_boundary_pairs[0].second};
794 
795  _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", primary_name, primary_params);
796  _app.appendMeshGenerator("LowerDBlockFromSidesetGenerator", secondary_name, secondary_params);
797  }
798  }
799 
800  // Add the lagrange multiplier on the secondary subdomain.
801  const auto addLagrangeMultiplier =
802  [this, &secondary_subdomain_name, &displacements](const std::string & variable_name,
803  const Real scaling_factor,
804  const bool add_aux_lm,
805  const bool penalty_traction) //
806  {
807  InputParameters params = _factory.getValidParams("MooseVariableBase");
808 
809  // Allow the user to select "weighted" constraints and standard bases (use_dual = false) or
810  // "legacy" constraints and dual bases (use_dual = true). Unless it's for testing purposes,
811  // this combination isn't recommended
812  if (!add_aux_lm || penalty_traction)
813  params.set<bool>("use_dual") = _use_dual;
814 
815  mooseAssert(_problem->systemBaseNonlinear(/*nl_sys_num=*/0).hasVariable(displacements[0]),
816  "Displacement variable is missing");
817  const auto primal_type =
818  _problem->systemBaseNonlinear(/*nl_sys_num=*/0).system().variable_type(displacements[0]);
819 
820  const int lm_order = primal_type.order.get_order();
821 
822  if (primal_type.family == LAGRANGE)
823  {
824  params.set<MooseEnum>("family") = Utility::enum_to_string<FEFamily>(primal_type.family);
825  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{lm_order});
826  }
827  else
828  mooseError("Invalid bases for mortar contact.");
829 
830  params.set<std::vector<SubdomainName>>("block") = {secondary_subdomain_name};
831  if (!(add_aux_lm || penalty_traction))
832  params.set<std::vector<Real>>("scaling") = {scaling_factor};
833 
834  auto fe_type = AddVariableAction::feType(params);
835  auto var_type = AddVariableAction::variableType(fe_type);
836  if (add_aux_lm || penalty_traction)
837  _problem->addAuxVariable(var_type, variable_name, params);
838  else
839  _problem->addVariable(var_type, variable_name, params);
840  };
841 
842  if (_current_task == "add_mortar_variable" && _formulation == ContactFormulation::MORTAR)
843  {
844  addLagrangeMultiplier(
845  normal_lagrange_multiplier_name, getParam<Real>("normal_lm_scaling"), false, false);
846 
847  if (_model == ContactModel::COULOMB)
848  {
849  addLagrangeMultiplier(tangential_lagrange_multiplier_name,
850  getParam<Real>("tangential_lm_scaling"),
851  false,
852  false);
853  if (ndisp > 2)
854  addLagrangeMultiplier(tangential_lagrange_multiplier_3d_name,
855  getParam<Real>("tangential_lm_scaling"),
856  false,
857  false);
858  }
859 
860  if (getParam<bool>("use_petrov_galerkin"))
861  addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0, true, false);
862  }
863  else if (_current_task == "add_mortar_variable" &&
864  _formulation == ContactFormulation::MORTAR_PENALTY)
865  {
866  if (_use_dual)
867  addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0, false, true);
868  }
869 
870  if (_current_task == "add_user_object")
871  {
872  const auto register_mortar_uo_name = [this](const auto & bnd_pair, const auto & uo_prefix)
873  {
874  const auto & [primary_name, secondary_name] = bnd_pair;
875  const auto primary_id = _mesh->getBoundaryID(primary_name);
876  const auto secondary_id = _mesh->getBoundaryID(secondary_name);
877  const auto uo_name = uo_prefix + name();
878  _bnd_pair_to_mortar_info.emplace(bnd_pair, MortarInfo{primary_id, secondary_id, uo_name});
879  return uo_name;
880  };
881 
882  // check if the correct problem class is selected if AL parameters are provided
883  if (_formulation == ContactFormulation::MORTAR_PENALTY &&
884  !dynamic_cast<AugmentedLagrangianContactProblemInterface *>(_problem.get()))
885  {
886  const std::vector<std::string> params = {"penalty_multiplier",
887  "penalty_multiplier_friction",
888  "al_penetration_tolerance",
889  "al_incremental_slip_tolerance",
890  "al_frictional_force_tolerance"};
891  for (const auto & param : params)
892  if (parameters().isParamSetByUser(param))
893  paramError(param,
894  "Augmented Lagrange parameter was specified, but the selected problem type "
895  "does not support Augmented Lagrange iterations.");
896  }
897 
898  if (_model != ContactModel::COULOMB && _formulation == ContactFormulation::MORTAR)
899  {
900  auto uo_params = _factory.getValidParams("LMWeightedGapUserObject");
901 
902  uo_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
903  uo_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
904  uo_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
905  uo_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
906  uo_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
907  uo_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
908  if (ndisp > 2)
909  uo_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
910  uo_params.set<bool>("use_displaced_mesh") = true;
911  uo_params.set<std::vector<VariableName>>("lm_variable") = {normal_lagrange_multiplier_name};
912  uo_params.applySpecificParameters(
913  parameters(), {"correct_edge_dropping", "use_petrov_galerkin", "debug_mesh"});
914  if (getParam<bool>("use_petrov_galerkin"))
915  uo_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
916 
917  _problem->addUserObject("LMWeightedGapUserObject",
918  register_mortar_uo_name(_boundary_pairs[0], "lm_weightedgap_object_"),
919  uo_params);
920  }
921  else if (_model == ContactModel::COULOMB && _formulation == ContactFormulation::MORTAR)
922  {
923  auto uo_params = _factory.getValidParams("LMWeightedVelocitiesUserObject");
924  uo_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
925  uo_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
926  uo_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
927  uo_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
928  uo_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
929  uo_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
930  if (ndisp > 2)
931  uo_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
932 
933  uo_params.set<VariableName>("secondary_variable") = displacements[0];
934  uo_params.set<bool>("use_displaced_mesh") = true;
935  uo_params.set<std::vector<VariableName>>("lm_variable_normal") = {
936  normal_lagrange_multiplier_name};
937  uo_params.set<std::vector<VariableName>>("lm_variable_tangential_one") = {
938  tangential_lagrange_multiplier_name};
939  if (ndisp > 2)
940  uo_params.set<std::vector<VariableName>>("lm_variable_tangential_two") = {
941  tangential_lagrange_multiplier_3d_name};
942  uo_params.applySpecificParameters(
943  parameters(), {"correct_edge_dropping", "use_petrov_galerkin", "debug_mesh"});
944  if (getParam<bool>("use_petrov_galerkin"))
945  uo_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
946 
947  const auto uo_name = _problem->addUserObject(
948  "LMWeightedVelocitiesUserObject",
949  register_mortar_uo_name(_boundary_pairs[0], "lm_weightedvelocities_object_"),
950  uo_params);
951  }
952 
953  if (_model != ContactModel::COULOMB && _formulation == ContactFormulation::MORTAR_PENALTY)
954  {
955  auto uo_params = _factory.getValidParams("PenaltyWeightedGapUserObject");
956 
957  uo_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
958  uo_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
959  uo_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
960  uo_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
961  uo_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
962  uo_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
963 
964  // AL parameters
965  uo_params.applySpecificParameters(parameters(),
966  {"correct_edge_dropping",
967  "penalty",
968  "debug_mesh",
969  "max_penalty_multiplier",
970  "adaptivity_penalty_normal"});
971 
972  if (isParamValid("al_penetration_tolerance"))
973  uo_params.set<Real>("penetration_tolerance") = getParam<Real>("al_penetration_tolerance");
974  if (isParamValid("penalty_multiplier"))
975  uo_params.set<Real>("penalty_multiplier") = getParam<Real>("penalty_multiplier");
976  // In the contact action, we force the physical value of the normal gap, which also normalizes
977  // the penalty factor with the "area" around the node
978  uo_params.set<bool>("use_physical_gap") = true;
979 
980  if (_use_dual)
981  uo_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
982 
983  if (ndisp > 2)
984  uo_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
985  uo_params.set<bool>("use_displaced_mesh") = true;
986 
987  _problem->addUserObject(
988  "PenaltyWeightedGapUserObject",
989  register_mortar_uo_name(_boundary_pairs[0], "penalty_weightedgap_object_"),
990  uo_params);
991  _problem->haveADObjects(true);
992  }
993  else if (_model == ContactModel::COULOMB && _formulation == ContactFormulation::MORTAR_PENALTY)
994  {
995  auto uo_params = _factory.getValidParams("PenaltyFrictionUserObject");
996  uo_params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
997  uo_params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
998  uo_params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
999  uo_params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
1000  uo_params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
1001  uo_params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
1002  uo_params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
1003  if (ndisp > 2)
1004  uo_params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
1005 
1006  uo_params.set<VariableName>("secondary_variable") = displacements[0];
1007  uo_params.set<bool>("use_displaced_mesh") = true;
1008  uo_params.set<Real>("friction_coefficient") = getParam<Real>("friction_coefficient");
1009  uo_params.set<Real>("penalty") = getParam<Real>("penalty");
1010  uo_params.set<Real>("penalty_friction") = getParam<Real>("penalty_friction");
1011 
1012  // AL parameters
1013  uo_params.set<Real>("max_penalty_multiplier") = getParam<Real>("max_penalty_multiplier");
1014  uo_params.set<MooseEnum>("adaptivity_penalty_normal") =
1015  getParam<MooseEnum>("adaptivity_penalty_normal");
1016  uo_params.set<MooseEnum>("adaptivity_penalty_friction") =
1017  getParam<MooseEnum>("adaptivity_penalty_friction");
1018  if (isParamValid("al_penetration_tolerance"))
1019  uo_params.set<Real>("penetration_tolerance") = getParam<Real>("al_penetration_tolerance");
1020  if (isParamValid("penalty_multiplier"))
1021  uo_params.set<Real>("penalty_multiplier") = getParam<Real>("penalty_multiplier");
1022  if (isParamValid("penalty_multiplier_friction"))
1023  uo_params.set<Real>("penalty_multiplier_friction") =
1024  getParam<Real>("penalty_multiplier_friction");
1025 
1026  if (isParamValid("al_incremental_slip_tolerance"))
1027  uo_params.set<Real>("slip_tolerance") = getParam<Real>("al_incremental_slip_tolerance");
1028  // In the contact action, we force the physical value of the normal gap, which also normalizes
1029  // the penalty factor with the "area" around the node
1030  uo_params.set<bool>("use_physical_gap") = true;
1031 
1032  if (_use_dual)
1033  uo_params.set<std::vector<VariableName>>("aux_lm") = {auxiliary_lagrange_multiplier_name};
1034 
1035  uo_params.applySpecificParameters(parameters(),
1036  {"friction_coefficient", "penalty", "penalty_friction"});
1037 
1038  _problem->addUserObject(
1039  "PenaltyFrictionUserObject",
1040  register_mortar_uo_name(_boundary_pairs[0], "penalty_friction_object_"),
1041  uo_params);
1042  _problem->haveADObjects(true);
1043  }
1044  }
1045 
1046  if (_current_task == "add_constraint")
1047  {
1048  // Prepare problem for enforcement with Lagrange multipliers
1049  if (_model != ContactModel::COULOMB && _formulation == ContactFormulation::MORTAR)
1050  {
1051  std::string mortar_constraint_name;
1052 
1053  if (!_mortar_dynamics)
1054  mortar_constraint_name = "ComputeWeightedGapLMMechanicalContact";
1055  else
1056  mortar_constraint_name = "ComputeDynamicWeightedGapLMMechanicalContact";
1057 
1058  InputParameters params = _factory.getValidParams(mortar_constraint_name);
1059  if (_mortar_dynamics)
1060  params.applySpecificParameters(
1061  parameters(), {"newmark_beta", "newmark_gamma", "capture_tolerance", "wear_depth"});
1062 
1063  else // We need user objects for quasistatic constraints
1064  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedgap_object_" + name();
1065 
1066  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
1067  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
1068  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
1069  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
1070  params.set<NonlinearVariableName>("variable") = normal_lagrange_multiplier_name;
1071  params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
1072  params.set<Real>("c") = getParam<Real>("c_normal");
1073 
1074  if (ndisp > 1)
1075  params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
1076  if (ndisp > 2)
1077  params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
1078 
1079  params.set<bool>("use_displaced_mesh") = true;
1080 
1082  {"correct_edge_dropping",
1083  "normalize_c",
1084  "extra_vector_tags",
1085  "absolute_value_vector_tags",
1086  "debug_mesh"});
1087 
1088  _problem->addConstraint(
1089  mortar_constraint_name, action_name + "_normal_lm_weighted_gap", params);
1090  _problem->haveADObjects(true);
1091  }
1092  // Add the tangential and normal Lagrange's multiplier constraints on the secondary boundary.
1093  else if (_model == ContactModel::COULOMB && _formulation == ContactFormulation::MORTAR)
1094  {
1095  std::string mortar_constraint_name;
1096 
1097  if (!_mortar_dynamics)
1098  mortar_constraint_name = "ComputeFrictionalForceLMMechanicalContact";
1099  else
1100  mortar_constraint_name = "ComputeDynamicFrictionalForceLMMechanicalContact";
1101 
1102  InputParameters params = _factory.getValidParams(mortar_constraint_name);
1103  if (_mortar_dynamics)
1104  params.applySpecificParameters(
1105  parameters(), {"newmark_beta", "newmark_gamma", "capture_tolerance", "wear_depth"});
1106  else
1107  { // We need user objects for quasistatic constraints
1108  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedvelocities_object_" + name();
1109  params.set<UserObjectName>("weighted_velocities_uo") =
1110  "lm_weightedvelocities_object_" + name();
1111  }
1112 
1113  params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
1114  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
1115  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
1116  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
1117  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
1118  params.set<bool>("use_displaced_mesh") = true;
1119  params.set<Real>("c_t") = getParam<Real>("c_tangential");
1120  params.set<Real>("c") = getParam<Real>("c_normal");
1121  params.set<bool>("normalize_c") = getParam<bool>("normalize_c");
1122  params.set<bool>("compute_primal_residuals") = false;
1123 
1124  params.set<MooseEnum>("segment_quadrature") = getParam<MooseEnum>("segment_quadrature");
1125 
1126  params.set<std::vector<VariableName>>("disp_x") = {displacements[0]};
1127 
1128  if (ndisp > 1)
1129  params.set<std::vector<VariableName>>("disp_y") = {displacements[1]};
1130  if (ndisp > 2)
1131  params.set<std::vector<VariableName>>("disp_z") = {displacements[2]};
1132 
1133  params.set<NonlinearVariableName>("variable") = normal_lagrange_multiplier_name;
1134  params.set<std::vector<VariableName>>("friction_lm") = {tangential_lagrange_multiplier_name};
1135 
1136  if (ndisp > 2)
1137  params.set<std::vector<VariableName>>("friction_lm_dir") = {
1138  tangential_lagrange_multiplier_3d_name};
1139 
1140  params.set<Real>("mu") = getParam<Real>("friction_coefficient");
1141  params.applySpecificParameters(
1142  parameters(), {"extra_vector_tags", "absolute_value_vector_tags", "debug_mesh"});
1143 
1144  _problem->addConstraint(mortar_constraint_name, action_name + "_tangential_lm", params);
1145  _problem->haveADObjects(true);
1146  }
1147 
1148  const auto addMechanicalContactConstraints =
1149  [this, &primary_subdomain_name, &secondary_subdomain_name, &displacements](
1150  const std::string & variable_name,
1151  const std::string & constraint_prefix,
1152  const std::string & constraint_type,
1153  const bool is_additional_frictional_constraint,
1154  const bool is_normal_constraint)
1155  {
1156  InputParameters params = _factory.getValidParams(constraint_type);
1157 
1158  params.set<bool>("correct_edge_dropping") = getParam<bool>("correct_edge_dropping");
1159  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
1160  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
1161  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
1162  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
1163 
1164  if (_formulation == ContactFormulation::MORTAR)
1165  params.set<NonlinearVariableName>("variable") = variable_name;
1166 
1167  params.set<MooseEnum>("segment_quadrature") = getParam<MooseEnum>("segment_quadrature");
1168  params.set<bool>("use_displaced_mesh") = true;
1169  params.set<bool>("compute_lm_residuals") = false;
1170 
1171  // Additional displacement residual for frictional problem
1172  // The second frictional LM acts on a perpendicular direction.
1173  if (is_additional_frictional_constraint)
1174  params.set<MooseEnum>("direction") = "direction_2";
1175  params.applySpecificParameters(
1176  parameters(), {"extra_vector_tags", "absolute_value_vector_tags", "debug_mesh"});
1177 
1178  for (unsigned int i = 0; i < displacements.size(); ++i)
1179  {
1180  std::string constraint_name = constraint_prefix + Moose::stringify(i);
1181 
1182  params.set<VariableName>("secondary_variable") = displacements[i];
1183  params.set<MooseEnum>("component") = i;
1184 
1185  if (is_normal_constraint && _model != ContactModel::COULOMB &&
1186  _formulation == ContactFormulation::MORTAR)
1187  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedgap_object_" + name();
1188  else if (is_normal_constraint && _model == ContactModel::COULOMB &&
1189  _formulation == ContactFormulation::MORTAR)
1190  params.set<UserObjectName>("weighted_gap_uo") = "lm_weightedvelocities_object_" + name();
1191  else if (_formulation == ContactFormulation::MORTAR)
1192  params.set<UserObjectName>("weighted_velocities_uo") =
1193  "lm_weightedvelocities_object_" + name();
1194  else if (is_normal_constraint && _model != ContactModel::COULOMB &&
1195  _formulation == ContactFormulation::MORTAR_PENALTY)
1196  params.set<UserObjectName>("weighted_gap_uo") = "penalty_weightedgap_object_" + name();
1197  else if (is_normal_constraint && _model == ContactModel::COULOMB &&
1198  _formulation == ContactFormulation::MORTAR_PENALTY)
1199  params.set<UserObjectName>("weighted_gap_uo") = "penalty_friction_object_" + name();
1200  else if (_formulation == ContactFormulation::MORTAR_PENALTY)
1201  params.set<UserObjectName>("weighted_velocities_uo") =
1202  "penalty_friction_object_" + name();
1203 
1204  _problem->addConstraint(constraint_type, constraint_name, params);
1205  }
1206  _problem->haveADObjects(true);
1207  };
1208 
1209  // Add mortar mechanical contact constraint objects for primal variables
1210  addMechanicalContactConstraints(normal_lagrange_multiplier_name,
1211  action_name + "_normal_constraint_",
1212  "NormalMortarMechanicalContact",
1213  /* is_additional_frictional_constraint = */ false,
1214  /* is_normal_constraint = */ true);
1215 
1216  if (_model == ContactModel::COULOMB)
1217  {
1218  addMechanicalContactConstraints(tangential_lagrange_multiplier_name,
1219  action_name + "_tangential_constraint_",
1220  "TangentialMortarMechanicalContact",
1221  /* is_additional_frictional_constraint = */ false,
1222  /* is_normal_constraint = */ false);
1223  if (ndisp > 2)
1224  addMechanicalContactConstraints(tangential_lagrange_multiplier_3d_name,
1225  action_name + "_tangential_constraint_3d_",
1226  "TangentialMortarMechanicalContact",
1227  /* is_additional_frictional_constraint = */ true,
1228  /* is_normal_constraint = */ false);
1229  }
1230  }
1231 }
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:81
bool isUltimateMaster() const
void paramError(const std::string &param, Args... args) const
void applySpecificParameters(const InputParameters &common, const std::vector< std::string > &include, bool allow_private=false)
Factory & _factory
const InputParameters & parameters() const
MooseApp & _app
T & set(const std::string &name, bool quiet_mode=false)
InputParameters getValidParams(const std::string &name) const
const ContactFormulation _formulation
Contact formulation.
Definition: ContactAction.h:90
std::string shortName(const std::string &name)
std::map< std::pair< BoundaryName, BoundaryName >, const MortarInfo > _bnd_pair_to_mortar_info
Map from boundary pair to mortar user object name.
const std::string & name() const
const bool _generate_mortar_mesh
Whether to generate the mortar mesh (useful in a restart simulation e.g.).
Definition: ContactAction.h:96
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::string stringify(const T &t)
const MeshGenerator & appendMeshGenerator(const std::string &type, const std::string &name, InputParameters params)
const bool _mortar_dynamics
Whether mortar dynamic contact constraints are to be used.
Definition: ContactAction.h:99
bool isParamSetByUser(const std::string &name) const
std::shared_ptr< MooseMesh > & _mesh
static libMesh::FEType feType(const InputParameters &params)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
bool useMasterMesh() const
void mooseError(Args &&... args) const
std::shared_ptr< FEProblemBase > & _problem
bool _use_dual
Whether to use the dual Mortar approach.
Definition: ContactAction.h:93
bool isParamValid(const std::string &name) const
const ContactModel _model
Contact model type enum.
Definition: ContactAction.h:87
bool isRecovering() const

◆ addNodeFaceContact()

void ContactAction::addNodeFaceContact ( )
private

Generate constraints for node to face contact.

Definition at line 1234 of file ContactAction.C.

Referenced by act().

1235 {
1236  if (_current_task == "post_mesh_prepared" && _automatic_pairing_boundaries.size() > 0)
1237  {
1238  if (getParam<MooseEnum>("automatic_pairing_method").getEnum<ProximityMethod>() ==
1239  ProximityMethod::NODE)
1241  else if (getParam<MooseEnum>("automatic_pairing_method").getEnum<ProximityMethod>() ==
1242  ProximityMethod::CENTROID)
1244  }
1245 
1246  if (_current_task != "add_constraint")
1247  return;
1248 
1249  std::string action_name = MooseUtils::shortName(name());
1250  std::vector<VariableName> displacements = getParam<std::vector<VariableName>>("displacements");
1251  const unsigned int ndisp = displacements.size();
1252 
1253  std::string constraint_type;
1254 
1255  if (_formulation == ContactFormulation::RANFS)
1256  constraint_type = "RANFSNormalMechanicalContact";
1257  else
1258  constraint_type = "MechanicalContactConstraint";
1259 
1260  InputParameters params = _factory.getValidParams(constraint_type);
1261 
1262  params.applyParameters(parameters(),
1263  {"displacements",
1264  "secondary_gap_offset",
1265  "mapped_primary_gap_offset",
1266  "primary",
1267  "secondary"});
1268 
1269  const auto order = _problem->systemBaseNonlinear(/*nl_sys_num=*/0)
1270  .system()
1271  .variable_type(displacements[0])
1272  .order.get_order();
1273 
1274  params.set<std::vector<VariableName>>("displacements") = displacements;
1275  params.set<bool>("use_displaced_mesh") = true;
1276  params.set<MooseEnum>("order") = Utility::enum_to_string<Order>(OrderWrapper{order});
1277 
1278  for (const auto & contact_pair : _boundary_pairs)
1279  {
1280  if (_formulation != ContactFormulation::RANFS)
1281  {
1282  params.set<std::vector<VariableName>>("nodal_area") = {"nodal_area"};
1283  params.set<BoundaryName>("boundary") = contact_pair.first;
1284  if (isParamValid("secondary_gap_offset"))
1285  params.set<std::vector<VariableName>>("secondary_gap_offset") = {
1286  getParam<VariableName>("secondary_gap_offset")};
1287  if (isParamValid("mapped_primary_gap_offset"))
1288  params.set<std::vector<VariableName>>("mapped_primary_gap_offset") = {
1289  getParam<VariableName>("mapped_primary_gap_offset")};
1290  }
1291 
1292  for (unsigned int i = 0; i < ndisp; ++i)
1293  {
1294  std::string name = action_name + "_constraint_" + Moose::stringify(contact_pair, "_") + "_" +
1295  Moose::stringify(i);
1296 
1297  if (_formulation == ContactFormulation::RANFS)
1298  params.set<MooseEnum>("component") = i;
1299  else
1300  params.set<unsigned int>("component") = i;
1301 
1302  params.set<BoundaryName>("primary") = contact_pair.first;
1303  params.set<BoundaryName>("secondary") = contact_pair.second;
1304  params.set<NonlinearVariableName>("variable") = displacements[i];
1305  params.set<std::vector<VariableName>>("primary_variable") = {displacements[i]};
1307  {"extra_vector_tags", "absolute_value_vector_tags"});
1308  _problem->addConstraint(constraint_type, name, params);
1309  }
1310  }
1311 }
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:81
std::vector< BoundaryName > _automatic_pairing_boundaries
List of all possible boundaries for contact for automatic pairing (optional)
Definition: ContactAction.h:84
void applySpecificParameters(const InputParameters &common, const std::vector< std::string > &include, bool allow_private=false)
Factory & _factory
const InputParameters & parameters() const
T & set(const std::string &name, bool quiet_mode=false)
if(subdm)
InputParameters getValidParams(const std::string &name) const
void applyParameters(const InputParameters &common, const std::vector< std::string > &exclude={}, const bool allow_private=false)
const ContactFormulation _formulation
Contact formulation.
Definition: ContactAction.h:90
void createSidesetPairsFromGeometry()
Create contact pairs between all boundaries whose centroids are within a user-specified distance of e...
std::string shortName(const std::string &name)
const std::string & name() const
const std::string & _current_task
std::string stringify(const T &t)
void createSidesetsFromNodeProximity()
Create contact pairs between all boundaries by determining that nodes on both boundaries are close en...
std::shared_ptr< FEProblemBase > & _problem
bool isParamValid(const std::string &name) const

◆ addRelationshipManagers() [1/3]

bool Action::addRelationshipManagers

◆ addRelationshipManagers() [2/3]

virtual void Action::addRelationshipManagers

◆ addRelationshipManagers() [3/3]

void ContactAction::addRelationshipManagers ( Moose::RelationshipManagerType  input_rm_type)
overridevirtual

Reimplemented from Action.

Definition at line 740 of file ContactAction.C.

741 {
742  if (_formulation == ContactFormulation::MORTAR ||
743  _formulation == ContactFormulation::MORTAR_PENALTY)
744  {
745  auto params = MortarConstraintBase::validParams();
746  params.set<bool>("use_displaced_mesh") = true;
747  std::string action_name = MooseUtils::shortName(name());
748  const std::string primary_subdomain_name = action_name + "_primary_subdomain";
749  const std::string secondary_subdomain_name = action_name + "_secondary_subdomain";
750  params.set<BoundaryName>("primary_boundary") = _boundary_pairs[0].first;
751  params.set<BoundaryName>("secondary_boundary") = _boundary_pairs[0].second;
752  params.set<SubdomainName>("primary_subdomain") = primary_subdomain_name;
753  params.set<SubdomainName>("secondary_subdomain") = secondary_subdomain_name;
754  params.set<bool>("use_petrov_galerkin") = getParam<bool>("use_petrov_galerkin");
755  addRelationshipManagers(input_rm_type, params);
756  }
757 }
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:81
virtual void addRelationshipManagers(Moose::RelationshipManagerType input_rm_type) override
const ContactFormulation _formulation
Contact formulation.
Definition: ContactAction.h:90
std::string shortName(const std::string &name)
static InputParameters validParams()
const std::string & name() const

◆ commonParameters()

InputParameters ContactAction::commonParameters ( )
static

Define parameters used by multiple contact objects.

Returns
InputParameters object populated with common parameters

Definition at line 1648 of file ContactAction.C.

Referenced by MechanicalContactConstraint::validParams(), and validParams().

1649 {
1651 
1652  params.addParam<MooseEnum>("normal_smoothing_method",
1654  "Method to use to smooth normals");
1655  params.addParam<Real>(
1656  "normal_smoothing_distance",
1657  "Distance from edge in parametric coordinates over which to smooth contact normal");
1658 
1659  params.addParam<MooseEnum>(
1660  "formulation", ContactAction::getFormulationEnum(), "The contact formulation");
1661 
1662  params.addParam<MooseEnum>("model", ContactAction::getModelEnum(), "The contact model to use");
1663 
1664  return params;
1665 }
static MooseEnum getFormulationEnum()
Get contact formulation.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
static MooseEnum getSmoothingEnum()
Get smoothing type.
InputParameters emptyInputParameters()
static MooseEnum getModelEnum()
Get contact model.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

◆ createSidesetPairsFromGeometry()

void ContactAction::createSidesetPairsFromGeometry ( )
private

Create contact pairs between all boundaries whose centroids are within a user-specified distance of each other.

Definition at line 1461 of file ContactAction.C.

Referenced by addNodeFaceContact().

1462 {
1463  mooseInfo("The contact action is reading the list of boundaries and automatically pairs them "
1464  "if their centroids fall within a specified distance of each other.");
1465 
1466  if (!_mesh)
1467  mooseError("Failed to obtain mesh for automatically generating contact pairs.");
1468 
1469  if (!_mesh->getMesh().is_serial())
1470  paramError(
1471  "automatic_pairing_boundaries",
1472  "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1473 
1474  // Compute centers of gravity for each sideset
1475  std::vector<std::pair<BoundaryName, Point>> automatic_pairing_boundaries_cog;
1476  const auto & sideset_ids = _mesh->meshSidesetIds();
1477 
1478  const auto & bnd_to_elem_map = _mesh->getBoundariesToActiveSemiLocalElemIds();
1479 
1480  for (const auto & sideset_name : _automatic_pairing_boundaries)
1481  {
1482  // If the sideset provided in the input file isn't in the mesh, error out.
1483  const auto find_set = sideset_ids.find(_mesh->getBoundaryID(sideset_name));
1484  if (find_set == sideset_ids.end())
1485  paramError("automatic_pairing_boundaries",
1486  sideset_name,
1487  " is not defined as a sideset in the mesh.");
1488 
1489  auto dofs_set = bnd_to_elem_map.find(_mesh->getBoundaryID(sideset_name));
1490 
1491  // Initialize data for sideset
1492  Point center_of_gravity(0, 0, 0);
1493  Real accumulated_sideset_area(0);
1494 
1495  // Pointer to lower-dimensional element on the sideset
1496  std::unique_ptr<const Elem> side_ptr;
1497  const std::unordered_set<dof_id_type> & bnd_elems = dofs_set->second;
1498 
1499  for (auto elem_id : bnd_elems)
1500  {
1501  const Elem * elem = _mesh->elemPtr(elem_id);
1502  unsigned int side = _mesh->sideWithBoundaryID(elem, _mesh->getBoundaryID(sideset_name));
1503 
1504  // update side_ptr
1505  elem->side_ptr(side_ptr, side);
1506 
1507  // area of the (linearized) side
1508  const auto side_area = side_ptr->volume();
1509 
1510  // position of the side
1511  const auto side_position = side_ptr->true_centroid();
1512 
1513  center_of_gravity += side_position * side_area;
1514  accumulated_sideset_area += side_area;
1515  }
1516 
1517  // Average each element's center of gravity (centroid) with its area
1518  center_of_gravity /= accumulated_sideset_area;
1519 
1520  // Add sideset-cog pair to vector
1521  automatic_pairing_boundaries_cog.emplace_back(sideset_name, center_of_gravity);
1522  }
1523 
1524  // Vectors of distances for each pair
1525  std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> pairs_distances;
1526 
1527  // Assign distances to identify nearby pairs.
1528  for (std::size_t i = 0; i < automatic_pairing_boundaries_cog.size() - 1; i++)
1529  for (std::size_t j = i + 1; j < automatic_pairing_boundaries_cog.size(); j++)
1530  {
1531  const Point & distance_vector =
1532  automatic_pairing_boundaries_cog[i].second - automatic_pairing_boundaries_cog[j].second;
1533 
1534  if (automatic_pairing_boundaries_cog[i].first != automatic_pairing_boundaries_cog[j].first)
1535  {
1536  const Real distance = distance_vector.norm();
1537  const std::pair pair = std::make_pair(automatic_pairing_boundaries_cog[i].first,
1538  automatic_pairing_boundaries_cog[j].first);
1539  pairs_distances.emplace_back(std::make_pair(pair, distance));
1540  }
1541  }
1542 
1543  const auto automatic_pairing_distance = getParam<Real>("automatic_pairing_distance");
1544 
1545  // Loop over all pairs
1546  std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> lean_pairs_distances;
1547  for (const auto & pair_distance : pairs_distances)
1548  if (pair_distance.second <= automatic_pairing_distance)
1549  {
1550  lean_pairs_distances.emplace_back(pair_distance);
1551  mooseInfoRepeated("Generating contact pair primary--secondary ",
1552  pair_distance.first.first,
1553  "--",
1554  pair_distance.first.second,
1555  ", with a relative distance of ",
1556  pair_distance.second);
1557  }
1558 
1559  // Create the boundary pairs (possibly with repeated pairs depending on user input)
1560  for (const auto & lean_pairs_distance : lean_pairs_distances)
1561  {
1562  // Make sure secondary surface's boundary ID is less than primary surface's boundary ID.
1563  // This is done to ensure some consistency in the boundary matching, which helps in defining
1564  // auxiliary kernels in the input file.
1565  if (_mesh->getBoundaryID(lean_pairs_distance.first.first) >
1566  _mesh->getBoundaryID(lean_pairs_distance.first.second))
1567  _boundary_pairs.push_back(
1568  {lean_pairs_distance.first.first, lean_pairs_distance.first.second});
1569  else
1570  _boundary_pairs.push_back(
1571  {lean_pairs_distance.first.second, lean_pairs_distance.first.first});
1572  }
1573 
1574  // Let's remove possibly repeated pairs
1576 }
void mooseInfo(Args &&... args) const
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:81
std::vector< BoundaryName > _automatic_pairing_boundaries
List of all possible boundaries for contact for automatic pairing (optional)
Definition: ContactAction.h:84
void paramError(const std::string &param, Args... args) const
void removeRepeatedPairs()
Remove repeated contact pairs from _boundary_pairs.
void mooseInfoRepeated(Args &&... args)
Real distance(const Point &p)
std::shared_ptr< MooseMesh > & _mesh
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void mooseError(Args &&... args) const
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")

◆ createSidesetsFromNodeProximity()

void ContactAction::createSidesetsFromNodeProximity ( )
private

Create contact pairs between all boundaries by determining that nodes on both boundaries are close enough.

Definition at line 1323 of file ContactAction.C.

Referenced by addNodeFaceContact().

1324 {
1325  mooseInfo("The contact action is reading the list of boundaries and automatically pairs them "
1326  "if the distance between nodes is less than a specified distance.");
1327 
1328  if (!_mesh)
1329  mooseError("Failed to obtain mesh for automatically generating contact pairs.");
1330 
1331  if (!_mesh->getMesh().is_serial())
1332  paramError(
1333  "automatic_pairing_boundaries",
1334  "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1335 
1336  // Create automatic_pairing_boundaries_id
1337  std::vector<BoundaryID> _automatic_pairing_boundaries_id;
1338  for (const auto & sideset_name : _automatic_pairing_boundaries)
1339  _automatic_pairing_boundaries_id.emplace_back(_mesh->getBoundaryID(sideset_name));
1340 
1341  // Vector of pairs node-boundary id
1342  std::vector<NodeBoundaryIDInfo> node_boundary_id_vector;
1343 
1344  // Data structures to hold the boundary nodes
1345  const ConstBndNodeRange & bnd_nodes = *_mesh->getBoundaryNodeRange();
1346 
1347  for (const auto & bnode : bnd_nodes)
1348  {
1349  const BoundaryID boundary_id = bnode->_bnd_id;
1350  const Node * node_ptr = bnode->_node;
1351 
1352  // Make sure node is on a boundary chosen for contact mechanics
1353  auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1354  _automatic_pairing_boundaries_id.end(),
1355  boundary_id);
1356 
1357  if (it != _automatic_pairing_boundaries_id.end())
1358  node_boundary_id_vector.emplace_back(node_ptr, boundary_id);
1359  }
1360 
1361  // sort by increasing boundary id
1362  std::sort(node_boundary_id_vector.begin(),
1363  node_boundary_id_vector.end(),
1364  [](const NodeBoundaryIDInfo & first_pair, const NodeBoundaryIDInfo & second_pair)
1365  { return first_pair.second < second_pair.second; });
1366 
1367  // build kd-tree
1368  using KDTreeType = nanoflann::KDTreeSingleIndexAdaptor<
1369  nanoflann::L2_Simple_Adaptor<Real, PointListAdaptor<NodeBoundaryIDInfo>, Real, std::size_t>,
1371  LIBMESH_DIM,
1372  std::size_t>;
1373 
1374  // This parameter can be tuned. Others use '10'
1375  const unsigned int max_leaf_size = 20;
1376 
1377  // Build point list adaptor with all nodes-sidesets pairs for possible mechanical contact
1378  auto point_list = PointListAdaptor<NodeBoundaryIDInfo>(node_boundary_id_vector.begin(),
1379  node_boundary_id_vector.end());
1380  auto kd_tree = std::make_unique<KDTreeType>(
1381  LIBMESH_DIM, point_list, nanoflann::KDTreeSingleIndexAdaptorParams(max_leaf_size));
1382 
1383  if (!kd_tree)
1384  mooseError("Internal error. KDTree was not properly initialized in the contact action.");
1385 
1386  kd_tree->buildIndex();
1387 
1388  // data structures for kd-tree search
1389  nanoflann::SearchParameters search_params;
1390  std::vector<nanoflann::ResultItem<std::size_t, Real>> ret_matches;
1391 
1392  const auto radius_for_search = getParam<Real>("automatic_pairing_distance");
1393 
1394  // For all nodes
1395  for (const auto & pair : node_boundary_id_vector)
1396  {
1397  // clear result buffer
1398  ret_matches.clear();
1399 
1400  // position where we expect a periodic partner for the current node and boundary
1401  const Point search_point = *pair.first;
1402 
1403  // search at the expected point
1404  kd_tree->radiusSearch(
1405  &(search_point)(0), radius_for_search * radius_for_search, ret_matches, search_params);
1406 
1407  for (auto & match_pair : ret_matches)
1408  {
1409  const auto & match = node_boundary_id_vector[match_pair.first];
1410 
1411  //
1412  // If the proximity node identified belongs to a boundary in the input, add boundary pair
1413  //
1414 
1415  // Make sure node is on a boundary chosen for contact mechanics
1416  auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1417  _automatic_pairing_boundaries_id.end(),
1418  match.second);
1419 
1420  // If nodes are on the same boundary, pass.
1421  if (match.second == pair.second)
1422  continue;
1423 
1424  // At this point we will likely create many repeated pairs because many nodal pairs may
1425  // fulfill the distance condition imposed by the automatic_pairing_distance user input
1426  // parameter.
1427  if (it != _automatic_pairing_boundaries_id.end())
1428  {
1429  const auto index_one = cast_int<int>(it - _automatic_pairing_boundaries_id.begin());
1430  auto it_other = std::find(_automatic_pairing_boundaries_id.begin(),
1431  _automatic_pairing_boundaries_id.end(),
1432  pair.second);
1433 
1434  mooseAssert(it_other != _automatic_pairing_boundaries_id.end(),
1435  "Error in contact action. Unable to find boundary ID for node proximity "
1436  "automatic pairing.");
1437 
1438  const auto index_two = cast_int<int>(it_other - _automatic_pairing_boundaries_id.begin());
1439 
1440  if (pair.second > match.second)
1441  _boundary_pairs.push_back(
1443  else
1444  _boundary_pairs.push_back(
1446  }
1447  }
1448  }
1449 
1450  // Let's remove likely repeated pairs
1452 
1453  mooseInfo(
1454  "The following boundary pairs were created by the contact action using nodal proximity: ");
1455  for (const auto & [primary, secondary] : _boundary_pairs)
1457  "Primary boundary ID: ", primary, " and secondary boundary ID: ", secondary, ".");
1458 }
void mooseInfo(Args &&... args) const
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:81
std::vector< BoundaryName > _automatic_pairing_boundaries
List of all possible boundaries for contact for automatic pairing (optional)
Definition: ContactAction.h:84
void paramError(const std::string &param, Args... args) const
void removeRepeatedPairs()
Remove repeated contact pairs from _boundary_pairs.
void mooseInfoRepeated(Args &&... args)
boundary_id_type BoundaryID
std::pair< const Node *, BoundaryID > NodeBoundaryIDInfo
Definition: ContactAction.C:43
std::shared_ptr< MooseMesh > & _mesh
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void mooseError(Args &&... args) const
SearchParams SearchParameters

◆ getFormulationEnum()

MooseEnum ContactAction::getFormulationEnum ( )
static

Get contact formulation.

Returns
enum

Definition at line 1591 of file ContactAction.C.

Referenced by commonParameters().

1592 {
1593  auto formulations = MooseEnum(getContactFormulationOptions(), "kinematic");
1594 
1595  formulations.addDocumentation(
1596  "ranfs",
1597  "Reduced Active Nonlinear Function Set scheme for node-on-face contact. Provides exact "
1598  "enforcement without Lagrange multipliers or penalty terms.");
1599  formulations.addDocumentation(
1600  "kinematic",
1601  "Kinematic contact constraint enforcement transfers the internal forces at secondary nodes "
1602  "to the corresponding primary face for node-on-face contact. Provides exact "
1603  "enforcement without Lagrange multipliers or penalty terms.");
1604  formulations.addDocumentation(
1605  "penalty",
1606  "Node-on-face penalty based contact constraint enforcement. Interpenetration is penalized. "
1607  "Enforcement depends on the penalty magnitude. High penalties can introduce ill conditioning "
1608  "of the system.");
1609  formulations.addDocumentation("augmented_lagrange",
1610  "Node-on-face augmented Lagrange penalty based contact constraint "
1611  "enforcement. Interpenetration is enforced up to a user specified "
1612  "tolerance, ill-conditioning is generally avoided. Requires an "
1613  "Augmented Lagrange Problem class to be used in the simulation.");
1614  formulations.addDocumentation(
1615  "tangential_penalty",
1616  "Node-on-face penalty based frictional contact constraint enforcement. Interpenetration and "
1617  "slip distance for sticking nodes are penalized. Enforcement depends on the penalty "
1618  "magnitudes. High penalties can introduce ill conditioning of the system.");
1619  formulations.addDocumentation(
1620  "mortar",
1621  "Mortar based contact constraint enforcement using Lagrange multipliers. Provides exact "
1622  "enforcement and a variationally consistent formulation. Lagrange multipliers introduce a "
1623  "saddle point character in the system matrix which can have a negative impact on scalability "
1624  "with iterative solvers");
1625  formulations.addDocumentation(
1626  "mortar_penalty",
1627  "Mortar and penalty based contact constraint enforcement. When using an Augmented Lagrange "
1628  "Problem class this provides normal (and tangential) contact constratint enforced up to a "
1629  "user specified tolerances. Without AL the enforcement depends on the penalty magnitudes. "
1630  "High penalties can introduce ill conditioning of the system.");
1631 
1632  return formulations;
1633 }

◆ getModelEnum()

MooseEnum ContactAction::getModelEnum ( )
static

Get contact model.

Returns
enum

Definition at line 1579 of file ContactAction.C.

Referenced by commonParameters(), and validParams().

1580 {
1581  return MooseEnum(getContactModelOptions(), "frictionless");
1582 }

◆ getProximityMethod()

MooseEnum ContactAction::getProximityMethod ( )
static

Get proximity method for automatic pairing.

Returns
enum

Definition at line 1585 of file ContactAction.C.

Referenced by validParams().

1586 {
1587  return MooseEnum(getProximityMethodOptions());
1588 }

◆ getSmoothingEnum()

MooseEnum ContactAction::getSmoothingEnum ( )
static

Get smoothing type.

Returns
enum

Definition at line 1642 of file ContactAction.C.

Referenced by commonParameters().

1643 {
1644  return MooseEnum("edge_based nodal_normal_based", "");
1645 }

◆ getSystemEnum()

MooseEnum ContactAction::getSystemEnum ( )
static

Get contact system.

Returns
enum

Definition at line 1636 of file ContactAction.C.

1637 {
1638  return MooseEnum("Constraint", "Constraint");
1639 }

◆ removeRepeatedPairs()

void ContactAction::removeRepeatedPairs ( )
private

Remove repeated contact pairs from _boundary_pairs.

Definition at line 423 of file ContactAction.C.

Referenced by ContactAction(), createSidesetPairsFromGeometry(), and createSidesetsFromNodeProximity().

424 {
425  if (_boundary_pairs.size() == 0 && _automatic_pairing_boundaries.size() == 0)
426  paramError(
427  "primary",
428  "Number of contact pairs in the contact action is zero. Please revise your input file.");
429 
430  // Remove repeated interactions
431  std::vector<std::pair<BoundaryName, BoundaryName>> lean_boundary_pairs;
432 
433  for (const auto & [primary, secondary] : _boundary_pairs)
434  {
435  // Structured bindings are not capturable (primary_copy, secondary_copy)
436  auto it = std::find_if(lean_boundary_pairs.begin(),
437  lean_boundary_pairs.end(),
438  [&, primary_copy = primary, secondary_copy = secondary](
439  const std::pair<BoundaryName, BoundaryName> & lean_pair)
440  {
441  const bool match_one = lean_pair.second == secondary_copy &&
442  lean_pair.first == primary_copy;
443  const bool match_two = lean_pair.second == primary_copy &&
444  lean_pair.first == secondary_copy;
445  const bool exist = match_one || match_two;
446  return exist;
447  });
448 
449  if (it == lean_boundary_pairs.end())
450  lean_boundary_pairs.emplace_back(primary, secondary);
451  else
452  mooseInfo("Contact pair ",
453  primary,
454  "--",
455  secondary,
456  " has been removed from the contact interaction list due to "
457  "duplicates in the input file.");
458  }
459 
460  _boundary_pairs = lean_boundary_pairs;
461 }
void mooseInfo(Args &&... args) const
std::vector< std::pair< BoundaryName, BoundaryName > > _boundary_pairs
Primary/Secondary boundary name pairs for mechanical contact.
Definition: ContactAction.h:81
std::vector< BoundaryName > _automatic_pairing_boundaries
List of all possible boundaries for contact for automatic pairing (optional)
Definition: ContactAction.h:84
void paramError(const std::string &param, Args... args) const

◆ validParams()

InputParameters ContactAction::validParams ( )
static

Definition at line 72 of file ContactAction.C.

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  params.transferParam<MooseEnum>(MortarConstraintBase::validParams(), "segment_quadrature");
282  return params;
283 }
void addDeprecatedParam(const std::string &name, const T &value, const std::string &doc_string, const std::string &deprecation_message)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
static InputParameters commonParameters()
Define parameters used by multiple contact objects.
static MooseEnum getProximityMethod()
Get proximity method for automatic pairing.
static InputParameters validParams()
static InputParameters validParams()
void transferParam(const InputParameters &source_param, const std::string &name, const std::string &new_name="", const std::string &new_description="")
void addCoupledVar(const std::string &name, const std::string &doc_string)
static MooseEnum getModelEnum()
Get contact model.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void addClassDescription(const std::string &doc_string)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)

Member Data Documentation

◆ _automatic_pairing_boundaries

std::vector<BoundaryName> ContactAction::_automatic_pairing_boundaries
protected

List of all possible boundaries for contact for automatic pairing (optional)

Definition at line 84 of file ContactAction.h.

Referenced by addNodeFaceContact(), ContactAction(), createSidesetPairsFromGeometry(), createSidesetsFromNodeProximity(), and removeRepeatedPairs().

◆ _bnd_pair_to_mortar_info

std::map<std::pair<BoundaryName, BoundaryName>, const MortarInfo> ContactAction::_bnd_pair_to_mortar_info
protected

Map from boundary pair to mortar user object name.

Definition at line 109 of file ContactAction.h.

Referenced by act(), addContactPressureAuxKernel(), and addMortarContact().

◆ _boundary_pairs

std::vector<std::pair<BoundaryName, BoundaryName> > ContactAction::_boundary_pairs
protected

◆ _formulation

const ContactFormulation ContactAction::_formulation
protected

◆ _generate_mortar_mesh

const bool ContactAction::_generate_mortar_mesh
protected

Whether to generate the mortar mesh (useful in a restart simulation e.g.).

Definition at line 96 of file ContactAction.h.

Referenced by addMortarContact().

◆ _model

const ContactModel ContactAction::_model
protected

Contact model type enum.

Definition at line 87 of file ContactAction.h.

Referenced by act(), addMortarContact(), and ContactAction().

◆ _mortar_dynamics

const bool ContactAction::_mortar_dynamics
protected

Whether mortar dynamic contact constraints are to be used.

Definition at line 99 of file ContactAction.h.

Referenced by addMortarContact().

◆ _use_dual

bool ContactAction::_use_dual
protected

Whether to use the dual Mortar approach.

Definition at line 93 of file ContactAction.h.

Referenced by addMortarContact(), and ContactAction().


The documentation for this class was generated from the following files: