599 if (!
_problem->getDisplacedProblem())
601 "Contact requires updated coordinates. Use the 'displacements = ...' parameter in the "
608 if (!
_problem->isSNESMFReuseBaseSetbyUser())
609 _problem->setSNESMFReuseBase(
false,
false);
620 if (!
_problem->getDisplacedProblem())
621 mooseError(
"Contact requires updated coordinates. Use the 'displacements = ...' line in the "
627 const auto & [primary_name, secondary_name] = contact_pair;
633 {
"secondary_gap_offset",
"mapped_primary_gap_offset",
"order"});
635 std::vector<VariableName> displacements =
636 getParam<std::vector<VariableName>>(
"displacements");
637 const auto order =
_problem->systemBaseNonlinear(0)
639 .variable_type(displacements[0])
642 params.
set<
MooseEnum>(
"order") = Utility::enum_to_string<Order>(OrderWrapper{order});
644 params.
set<std::vector<BoundaryName>>(
"boundary") = {secondary_name};
645 params.
set<BoundaryName>(
"paired_boundary") = primary_name;
646 params.
set<AuxVariableName>(
"variable") =
"penetration";
648 params.
set<std::vector<VariableName>>(
"secondary_gap_offset") = {
649 getParam<VariableName>(
"secondary_gap_offset")};
651 params.
set<std::vector<VariableName>>(
"mapped_primary_gap_offset") = {
652 getParam<VariableName>(
"mapped_primary_gap_offset")};
653 params.
set<
bool>(
"use_displaced_mesh") =
true;
660 const auto type =
"MortarUserObjectAux";
662 params.
set<std::vector<BoundaryName>>(
"boundary") = {secondary_name};
663 params.
set<AuxVariableName>(
"variable") =
"gap";
664 params.
set<
bool>(
"use_displaced_mesh") =
true;
666 params.
set<
MooseEnum>(
"contact_quantity") =
"normal_gap";
667 const auto & [primary_id, secondary_id, uo_name] =
669 params.
set<UserObjectName>(
"user_object") = uo_name;
670 std::string
name =
_name +
"_contact_gap_" + std::to_string(primary_id) +
"_" +
671 std::to_string(secondary_id);
679 const unsigned int ndisp = getParam<std::vector<VariableName>>(
"displacements").size();
682 if (
_formulation == ContactFormulation::MORTAR &&
_model == ContactModel::COULOMB && ndisp > 2)
693 const std::string tangential_lagrange_multiplier_name = action_name +
"_tangential_lm";
694 const std::string tangential_lagrange_multiplier_3d_name =
695 action_name +
"_tangential_3d_lm";
697 params.
set<std::vector<VariableName>>(
"tangent_one") = {
698 tangential_lagrange_multiplier_name};
699 params.
set<std::vector<VariableName>>(
"tangent_two") = {
700 tangential_lagrange_multiplier_3d_name};
702 std::vector<std::string> disp_components({
"x",
"y",
"z"});
703 unsigned component_index = 0;
706 for (
const auto & disp_component : disp_components)
708 params.
set<AuxVariableName>(
"variable") =
_name +
"_tangent_" + disp_component;
709 params.
set<
unsigned int>(
"component") = component_index;
711 std::string
name =
_name +
"_mortar_frictional_pressure_" + disp_component +
"_" +
714 _problem->addAuxKernel(
"MortarFrictionalPressureVectorAux",
name, params);
723 std::vector<VariableName> displacements = getParam<std::vector<VariableName>>(
"displacements");
724 const auto order =
_problem->systemBaseNonlinear(0)
726 .variable_type(displacements[0])
728 const auto mortar_lm_order =
729 _lm_space == ContactLMSpace::LINEAR ?
static_cast<int>(FIRST) : order;
730 std::unique_ptr<InputParameters> current_params;
731 const auto create_aux_var_params =
738 const auto aux_order =
_formulation == ContactFormulation::MORTAR ? mortar_lm_order : order;
739 current_params->set<
MooseEnum>(
"order") =
740 Utility::enum_to_string<Order>(OrderWrapper{aux_order});
741 current_params->set<
MooseEnum>(
"family") =
"LAGRANGE";
742 return *current_params;
749 _problem->addAuxVariable(
"MooseVariable",
"penetration", create_aux_var_params());
751 _problem->addAuxVariable(
"MooseVariable",
"nodal_area", create_aux_var_params());
754 _problem->addAuxVariable(
"MooseVariable",
"gap", create_aux_var_params());
757 _problem->addAuxVariable(
"MooseVariable",
"contact_pressure", create_aux_var_params());
759 const unsigned int ndisp = getParam<std::vector<VariableName>>(
"displacements").size();
762 if (
_formulation == ContactFormulation::MORTAR &&
_model == ContactModel::COULOMB && ndisp > 2)
765 std::vector<std::string> disp_components({
"x",
"y",
"z"});
767 for (
const auto & disp_component : disp_components)
771 Utility::enum_to_string<Order>(OrderWrapper{mortar_lm_order});
772 var_params.set<
MooseEnum>(
"family") =
"LAGRANGE";
775 "MooseVariable",
_name +
"_tangent_" + disp_component, var_params);
790 std::vector<BoundaryName> secondary_boundary_vector;
791 for (
const auto *
const action : actions)
792 for (
const auto j : index_range(action->_boundary_pairs))
793 secondary_boundary_vector.push_back(action->_boundary_pairs[j].second);
795 var_params.set<std::vector<BoundaryName>>(
"boundary") = secondary_boundary_vector;
796 var_params.set<std::vector<VariableName>>(
"variable") = {
"nodal_area"};
798 mooseAssert(
_problem,
"Problem pointer is NULL");
800 var_params.set<
bool>(
"use_displaced_mesh") =
true;
802 _problem->addUserObject(
"NodalArea",
922 std::vector<VariableName> displacements = getParam<std::vector<VariableName>>(
"displacements");
923 const unsigned int ndisp = displacements.size();
926 const std::string primary_subdomain_name = action_name +
"_primary_subdomain";
927 const std::string secondary_subdomain_name = action_name +
"_secondary_subdomain";
928 const std::string normal_lagrange_multiplier_name = action_name +
"_normal_lm";
929 const std::string tangential_lagrange_multiplier_name = action_name +
"_tangential_lm";
930 const std::string tangential_lagrange_multiplier_3d_name = action_name +
"_tangential_3d_lm";
931 const std::string auxiliary_lagrange_multiplier_name = action_name +
"_aux_lm";
941 const MeshGeneratorName primary_name = primary_subdomain_name +
"_generator";
942 const MeshGeneratorName secondary_name = secondary_subdomain_name +
"_generator";
947 primary_params.
set<SubdomainName>(
"new_block_name") = primary_subdomain_name;
948 secondary_params.set<SubdomainName>(
"new_block_name") = secondary_subdomain_name;
950 primary_params.set<std::vector<BoundaryName>>(
"sidesets") = {
_boundary_pairs[0].first};
951 secondary_params.set<std::vector<BoundaryName>>(
"sidesets") = {
_boundary_pairs[0].second};
959 const auto addLagrangeMultiplier =
960 [
this, &secondary_subdomain_name, &displacements](
const std::string & variable_name,
961 const Real scaling_factor,
962 const bool add_aux_lm,
963 const bool penalty_traction)
970 if (!add_aux_lm || penalty_traction)
973 mooseAssert(
_problem->systemBaseNonlinear(0).hasVariable(displacements[0]),
974 "Displacement variable is missing");
975 const auto primal_type =
976 _problem->systemBaseNonlinear(0).system().variable_type(displacements[0]);
982 ?
static_cast<int>(FIRST)
983 : primal_type.order.get_order();
985 if (primal_type.family == LAGRANGE)
987 params.
set<
MooseEnum>(
"family") = Utility::enum_to_string<FEFamily>(primal_type.family);
988 params.
set<
MooseEnum>(
"order") = Utility::enum_to_string<Order>(OrderWrapper{lm_order});
991 mooseError(
"Invalid bases for mortar contact.");
993 params.
set<std::vector<SubdomainName>>(
"block") = {secondary_subdomain_name};
994 if (!(add_aux_lm || penalty_traction))
995 params.
set<std::vector<Real>>(
"scaling") = {scaling_factor};
999 if (add_aux_lm || penalty_traction)
1000 _problem->addAuxVariable(var_type, variable_name, params);
1002 _problem->addVariable(var_type, variable_name, params);
1007 addLagrangeMultiplier(
1008 normal_lagrange_multiplier_name, getParam<Real>(
"normal_lm_scaling"),
false,
false);
1010 if (
_model == ContactModel::COULOMB)
1012 addLagrangeMultiplier(tangential_lagrange_multiplier_name,
1013 getParam<Real>(
"tangential_lm_scaling"),
1017 addLagrangeMultiplier(tangential_lagrange_multiplier_3d_name,
1018 getParam<Real>(
"tangential_lm_scaling"),
1023 if (getParam<bool>(
"use_petrov_galerkin"))
1024 addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0,
true,
false);
1030 addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0,
false,
true);
1035 const auto register_mortar_uo_name = [
this](
const auto & bnd_pair,
const auto & uo_prefix)
1037 const auto & [primary_name, secondary_name] = bnd_pair;
1038 const auto primary_id =
_mesh->getBoundaryID(primary_name);
1039 const auto secondary_id =
_mesh->getBoundaryID(secondary_name);
1040 const auto uo_name = uo_prefix +
name();
1046 if (
_formulation == ContactFormulation::MORTAR_PENALTY &&
1049 const std::vector<std::string> params = {
"penalty_multiplier",
1050 "penalty_multiplier_friction",
1051 "al_penetration_tolerance",
1052 "al_incremental_slip_tolerance",
1053 "al_frictional_force_tolerance"};
1054 for (
const auto & param : params)
1057 "Augmented Lagrange parameter was specified, but the selected problem type "
1058 "does not support Augmented Lagrange iterations.");
1066 uo_params.set<BoundaryName>(
"secondary_boundary") =
_boundary_pairs[0].second;
1067 uo_params.set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1068 uo_params.set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1069 uo_params.set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1070 uo_params.set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1072 uo_params.set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1073 uo_params.set<
bool>(
"use_displaced_mesh") =
true;
1074 uo_params.set<std::vector<VariableName>>(
"lm_variable") = {normal_lagrange_multiplier_name};
1075 uo_params.applySpecificParameters(
parameters(),
1076 {
"correct_edge_dropping",
1077 "use_nodal_scaling",
1079 "triangulate_triangles",
1080 "minimum_projection_angle",
1081 "mortar_3d_subpatch_plane",
1082 "mortar_3d_qp_mapping",
1083 "use_petrov_galerkin",
1085 if (getParam<bool>(
"use_petrov_galerkin"))
1086 uo_params.set<std::vector<VariableName>>(
"aux_lm") = {auxiliary_lagrange_multiplier_name};
1088 _problem->addUserObject(
"LMWeightedGapUserObject",
1089 register_mortar_uo_name(
_boundary_pairs[0],
"lm_weightedgap_object_"),
1092 else if (
_model == ContactModel::COULOMB &&
_formulation == ContactFormulation::MORTAR)
1096 uo_params.set<BoundaryName>(
"secondary_boundary") =
_boundary_pairs[0].second;
1097 uo_params.set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1098 uo_params.set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1099 uo_params.set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1100 uo_params.set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1102 uo_params.set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1104 uo_params.set<VariableName>(
"secondary_variable") = displacements[0];
1105 uo_params.set<
bool>(
"use_displaced_mesh") =
true;
1106 uo_params.set<std::vector<VariableName>>(
"lm_variable_normal") = {
1107 normal_lagrange_multiplier_name};
1108 uo_params.set<std::vector<VariableName>>(
"lm_variable_tangential_one") = {
1109 tangential_lagrange_multiplier_name};
1111 uo_params.set<std::vector<VariableName>>(
"lm_variable_tangential_two") = {
1112 tangential_lagrange_multiplier_3d_name};
1113 uo_params.applySpecificParameters(
parameters(),
1114 {
"correct_edge_dropping",
1115 "use_nodal_scaling",
1117 "triangulate_triangles",
1118 "minimum_projection_angle",
1119 "mortar_3d_subpatch_plane",
1120 "mortar_3d_qp_mapping",
1121 "use_petrov_galerkin",
1123 if (getParam<bool>(
"use_petrov_galerkin"))
1124 uo_params.set<std::vector<VariableName>>(
"aux_lm") = {auxiliary_lagrange_multiplier_name};
1126 const auto uo_name =
_problem->addUserObject(
1127 "LMWeightedVelocitiesUserObject",
1128 register_mortar_uo_name(
_boundary_pairs[0],
"lm_weightedvelocities_object_"),
1132 if (
_model != ContactModel::COULOMB &&
_formulation == ContactFormulation::MORTAR_PENALTY)
1137 uo_params.set<BoundaryName>(
"secondary_boundary") =
_boundary_pairs[0].second;
1138 uo_params.set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1139 uo_params.set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1140 uo_params.set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1141 uo_params.set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1144 uo_params.applySpecificParameters(
parameters(),
1145 {
"correct_edge_dropping",
1147 "triangulate_triangles",
1148 "minimum_projection_angle",
1149 "mortar_3d_subpatch_plane",
1150 "mortar_3d_qp_mapping",
1153 "max_penalty_multiplier",
1154 "adaptivity_penalty_normal"});
1157 uo_params.set<Real>(
"penetration_tolerance") = getParam<Real>(
"al_penetration_tolerance");
1159 uo_params.set<Real>(
"penalty_multiplier") = getParam<Real>(
"penalty_multiplier");
1162 uo_params.set<
bool>(
"use_physical_gap") =
true;
1165 uo_params.set<std::vector<VariableName>>(
"aux_lm") = {auxiliary_lagrange_multiplier_name};
1168 uo_params.set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1169 uo_params.set<
bool>(
"use_displaced_mesh") =
true;
1172 "PenaltyWeightedGapUserObject",
1173 register_mortar_uo_name(
_boundary_pairs[0],
"penalty_weightedgap_object_"),
1177 else if (
_model == ContactModel::COULOMB &&
_formulation == ContactFormulation::MORTAR_PENALTY)
1181 uo_params.set<BoundaryName>(
"secondary_boundary") =
_boundary_pairs[0].second;
1182 uo_params.set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1183 uo_params.set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1184 uo_params.set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1185 uo_params.set<
bool>(
"correct_edge_dropping") = getParam<bool>(
"correct_edge_dropping");
1186 uo_params.set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1188 uo_params.set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1190 uo_params.set<VariableName>(
"secondary_variable") = displacements[0];
1191 uo_params.set<
bool>(
"use_displaced_mesh") =
true;
1192 uo_params.set<Real>(
"friction_coefficient") = getParam<Real>(
"friction_coefficient");
1193 uo_params.set<Real>(
"penalty") = getParam<Real>(
"penalty");
1194 uo_params.set<Real>(
"penalty_friction") = getParam<Real>(
"penalty_friction");
1197 uo_params.set<Real>(
"max_penalty_multiplier") = getParam<Real>(
"max_penalty_multiplier");
1198 uo_params.set<
MooseEnum>(
"adaptivity_penalty_normal") =
1199 getParam<MooseEnum>(
"adaptivity_penalty_normal");
1200 uo_params.set<
MooseEnum>(
"adaptivity_penalty_friction") =
1201 getParam<MooseEnum>(
"adaptivity_penalty_friction");
1203 uo_params.set<Real>(
"penetration_tolerance") = getParam<Real>(
"al_penetration_tolerance");
1205 uo_params.set<Real>(
"penalty_multiplier") = getParam<Real>(
"penalty_multiplier");
1207 uo_params.set<Real>(
"penalty_multiplier_friction") =
1208 getParam<Real>(
"penalty_multiplier_friction");
1211 uo_params.set<Real>(
"slip_tolerance") = getParam<Real>(
"al_incremental_slip_tolerance");
1214 uo_params.set<
bool>(
"use_physical_gap") =
true;
1217 uo_params.set<std::vector<VariableName>>(
"aux_lm") = {auxiliary_lagrange_multiplier_name};
1219 uo_params.applySpecificParameters(
parameters(),
1221 "triangulate_triangles",
1222 "minimum_projection_angle",
1223 "mortar_3d_subpatch_plane",
1224 "mortar_3d_qp_mapping",
1225 "friction_coefficient",
1227 "penalty_friction"});
1230 "PenaltyFrictionUserObject",
1231 register_mortar_uo_name(
_boundary_pairs[0],
"penalty_friction_object_"),
1242 std::string mortar_constraint_name;
1245 mortar_constraint_name =
"ComputeWeightedGapLMMechanicalContact";
1247 mortar_constraint_name =
"ComputeDynamicWeightedGapLMMechanicalContact";
1252 parameters(), {
"newmark_beta",
"newmark_gamma",
"capture_tolerance",
"wear_depth"});
1255 params.
set<UserObjectName>(
"weighted_gap_uo") =
"lm_weightedgap_object_" +
name();
1259 params.
set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1260 params.
set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1261 params.
set<NonlinearVariableName>(
"variable") = normal_lagrange_multiplier_name;
1262 params.
set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1263 params.
set<Real>(
"c") = getParam<Real>(
"c_normal");
1266 params.
set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1268 params.
set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1270 params.
set<
bool>(
"use_displaced_mesh") =
true;
1273 {
"correct_edge_dropping",
1275 "triangulate_triangles",
1276 "minimum_projection_angle",
1277 "mortar_3d_subpatch_plane",
1278 "mortar_3d_qp_mapping",
1280 "extra_vector_tags",
1281 "absolute_value_vector_tags",
1285 mortar_constraint_name, action_name +
"_normal_lm_weighted_gap", params);
1289 else if (
_model == ContactModel::COULOMB &&
_formulation == ContactFormulation::MORTAR)
1291 std::string mortar_constraint_name;
1294 mortar_constraint_name =
"ComputeFrictionalForceLMMechanicalContact";
1296 mortar_constraint_name =
"ComputeDynamicFrictionalForceLMMechanicalContact";
1301 parameters(), {
"newmark_beta",
"newmark_gamma",
"capture_tolerance",
"wear_depth"});
1304 params.
set<UserObjectName>(
"weighted_gap_uo") =
"lm_weightedvelocities_object_" +
name();
1305 params.
set<UserObjectName>(
"weighted_velocities_uo") =
1306 "lm_weightedvelocities_object_" +
name();
1309 params.
set<
bool>(
"correct_edge_dropping") = getParam<bool>(
"correct_edge_dropping");
1312 params.
set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1313 params.
set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1314 params.
set<
bool>(
"use_displaced_mesh") =
true;
1315 params.
set<Real>(
"c_t") = getParam<Real>(
"c_tangential");
1316 params.
set<Real>(
"c") = getParam<Real>(
"c_normal");
1317 params.
set<
bool>(
"normalize_c") = getParam<bool>(
"normalize_c");
1318 params.
set<
bool>(
"compute_primal_residuals") =
false;
1320 params.
set<
MooseEnum>(
"segment_quadrature") = getParam<MooseEnum>(
"segment_quadrature");
1322 params.
set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1325 params.
set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1327 params.
set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1329 params.
set<NonlinearVariableName>(
"variable") = normal_lagrange_multiplier_name;
1330 params.
set<std::vector<VariableName>>(
"friction_lm") = {tangential_lagrange_multiplier_name};
1333 params.
set<std::vector<VariableName>>(
"friction_lm_dir") = {
1334 tangential_lagrange_multiplier_3d_name};
1336 params.
set<Real>(
"mu") = getParam<Real>(
"friction_coefficient");
1338 getParam<MooseEnum>(
"friction_projection_degree");
1341 "triangulate_triangles",
1342 "minimum_projection_angle",
1343 "mortar_3d_subpatch_plane",
1344 "mortar_3d_qp_mapping",
1345 "extra_vector_tags",
1346 "absolute_value_vector_tags",
1349 _problem->addConstraint(mortar_constraint_name, action_name +
"_tangential_lm", params);
1353 const auto addMechanicalContactConstraints =
1354 [
this, &primary_subdomain_name, &secondary_subdomain_name, &displacements](
1355 const std::string & variable_name,
1356 const std::string & constraint_prefix,
1357 const std::string & constraint_type,
1358 const bool is_additional_frictional_constraint,
1359 const bool is_normal_constraint)
1363 params.
set<
bool>(
"correct_edge_dropping") = getParam<bool>(
"correct_edge_dropping");
1366 params.
set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1367 params.
set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1370 params.
set<NonlinearVariableName>(
"variable") = variable_name;
1372 params.
set<
MooseEnum>(
"segment_quadrature") = getParam<MooseEnum>(
"segment_quadrature");
1373 params.
set<
bool>(
"use_displaced_mesh") =
true;
1374 params.
set<
bool>(
"compute_lm_residuals") =
false;
1378 if (is_additional_frictional_constraint)
1382 "triangulate_triangles",
1383 "minimum_projection_angle",
1384 "mortar_3d_subpatch_plane",
1385 "mortar_3d_qp_mapping",
1386 "extra_vector_tags",
1387 "absolute_value_vector_tags",
1390 for (
unsigned int i = 0; i < displacements.size(); ++i)
1394 params.
set<VariableName>(
"secondary_variable") = displacements[i];
1397 if (is_normal_constraint &&
_model != ContactModel::COULOMB &&
1399 params.
set<UserObjectName>(
"weighted_gap_uo") =
"lm_weightedgap_object_" +
name();
1400 else if (is_normal_constraint &&
_model == ContactModel::COULOMB &&
1402 params.
set<UserObjectName>(
"weighted_gap_uo") =
"lm_weightedvelocities_object_" +
name();
1404 params.
set<UserObjectName>(
"weighted_velocities_uo") =
1405 "lm_weightedvelocities_object_" +
name();
1406 else if (is_normal_constraint &&
_model != ContactModel::COULOMB &&
1408 params.
set<UserObjectName>(
"weighted_gap_uo") =
"penalty_weightedgap_object_" +
name();
1409 else if (is_normal_constraint &&
_model == ContactModel::COULOMB &&
1411 params.
set<UserObjectName>(
"weighted_gap_uo") =
"penalty_friction_object_" +
name();
1412 else if (
_formulation == ContactFormulation::MORTAR_PENALTY)
1413 params.
set<UserObjectName>(
"weighted_velocities_uo") =
1414 "penalty_friction_object_" +
name();
1416 _problem->addConstraint(constraint_type, constraint_name, params);
1422 addMechanicalContactConstraints(normal_lagrange_multiplier_name,
1423 action_name +
"_normal_constraint_",
1424 "NormalMortarMechanicalContact",
1428 if (
_model == ContactModel::COULOMB)
1430 addMechanicalContactConstraints(tangential_lagrange_multiplier_name,
1431 action_name +
"_tangential_constraint_",
1432 "TangentialMortarMechanicalContact",
1436 addMechanicalContactConstraints(tangential_lagrange_multiplier_3d_name,
1437 action_name +
"_tangential_constraint_3d_",
1438 "TangentialMortarMechanicalContact",
1537 mooseInfo(
"The contact action is reading the list of boundaries and automatically pairs them "
1538 "if the distance between nodes is less than a specified distance.");
1541 mooseError(
"Failed to obtain mesh for automatically generating contact pairs.");
1543 if (!
_mesh->getMesh().is_serial())
1545 "automatic_pairing_boundaries",
1546 "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1549 std::vector<BoundaryID> _automatic_pairing_boundaries_id;
1551 _automatic_pairing_boundaries_id.emplace_back(
_mesh->getBoundaryID(sideset_name));
1554 std::vector<NodeBoundaryIDInfo> node_boundary_id_vector;
1559 for (
const auto & bnode : bnd_nodes)
1561 const BoundaryID boundary_id = bnode->_bnd_id;
1562 const Node * node_ptr = bnode->_node;
1565 auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1566 _automatic_pairing_boundaries_id.end(),
1569 if (it != _automatic_pairing_boundaries_id.end())
1570 node_boundary_id_vector.emplace_back(node_ptr, boundary_id);
1574 std::sort(node_boundary_id_vector.begin(),
1575 node_boundary_id_vector.end(),
1577 { return first_pair.second < second_pair.second; });
1580 using KDTreeType = nanoflann::KDTreeSingleIndexAdaptor<
1581 nanoflann::L2_Simple_Adaptor<Real, PointListAdaptor<NodeBoundaryIDInfo>, Real, std::size_t>,
1587 const unsigned int max_leaf_size = 20;
1591 node_boundary_id_vector.end());
1592 auto kd_tree = std::make_unique<KDTreeType>(
1593 LIBMESH_DIM, point_list, nanoflann::KDTreeSingleIndexAdaptorParams(max_leaf_size));
1596 mooseError(
"Internal error. KDTree was not properly initialized in the contact action.");
1598 kd_tree->buildIndex();
1602 std::vector<nanoflann::ResultItem<std::size_t, Real>> ret_matches;
1604 const auto radius_for_search = getParam<Real>(
"automatic_pairing_distance");
1607 for (
const auto & pair : node_boundary_id_vector)
1610 ret_matches.clear();
1613 const Point search_point = *pair.first;
1616 kd_tree->radiusSearch(
1617 &(search_point)(0), radius_for_search * radius_for_search, ret_matches, search_params);
1619 for (
auto & match_pair : ret_matches)
1621 const auto & match = node_boundary_id_vector[match_pair.first];
1628 auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1629 _automatic_pairing_boundaries_id.end(),
1633 if (match.second == pair.second)
1639 if (it != _automatic_pairing_boundaries_id.end())
1641 const auto index_one = cast_int<int>(it - _automatic_pairing_boundaries_id.begin());
1642 auto it_other = std::find(_automatic_pairing_boundaries_id.begin(),
1643 _automatic_pairing_boundaries_id.end(),
1646 mooseAssert(it_other != _automatic_pairing_boundaries_id.end(),
1647 "Error in contact action. Unable to find boundary ID for node proximity "
1648 "automatic pairing.");
1650 const auto index_two = cast_int<int>(it_other - _automatic_pairing_boundaries_id.begin());
1652 if (pair.second > match.second)
1666 "The following boundary pairs were created by the contact action using nodal proximity: ");
1669 "Primary boundary ID: ", primary,
" and secondary boundary ID: ", secondary,
".");
1675 mooseInfo(
"The contact action is reading the list of boundaries and automatically pairs them "
1676 "if their centroids fall within a specified distance of each other.");
1679 mooseError(
"Failed to obtain mesh for automatically generating contact pairs.");
1681 if (!
_mesh->getMesh().is_serial())
1683 "automatic_pairing_boundaries",
1684 "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1687 std::vector<std::pair<BoundaryName, Point>> automatic_pairing_boundaries_cog;
1688 const auto & sideset_ids =
_mesh->meshSidesetIds();
1690 const auto & bnd_to_elem_map =
_mesh->getBoundariesToActiveSemiLocalElemIds();
1695 const auto find_set = sideset_ids.find(
_mesh->getBoundaryID(sideset_name));
1696 if (find_set == sideset_ids.end())
1699 " is not defined as a sideset in the mesh.");
1701 auto dofs_set = bnd_to_elem_map.find(
_mesh->getBoundaryID(sideset_name));
1704 Point center_of_gravity(0, 0, 0);
1705 Real accumulated_sideset_area(0);
1708 std::unique_ptr<const Elem> side_ptr;
1709 const std::unordered_set<dof_id_type> & bnd_elems = dofs_set->second;
1711 for (
auto elem_id : bnd_elems)
1713 const Elem * elem =
_mesh->elemPtr(elem_id);
1714 unsigned int side =
_mesh->sideWithBoundaryID(elem,
_mesh->getBoundaryID(sideset_name));
1717 elem->side_ptr(side_ptr, side);
1720 const auto side_area = side_ptr->volume();
1723 const auto side_position = side_ptr->true_centroid();
1725 center_of_gravity += side_position * side_area;
1726 accumulated_sideset_area += side_area;
1730 center_of_gravity /= accumulated_sideset_area;
1733 automatic_pairing_boundaries_cog.emplace_back(sideset_name, center_of_gravity);
1737 std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> pairs_distances;
1740 for (std::size_t i = 0; i < automatic_pairing_boundaries_cog.size() - 1; i++)
1741 for (std::size_t j = i + 1; j < automatic_pairing_boundaries_cog.size(); j++)
1743 const Point & distance_vector =
1744 automatic_pairing_boundaries_cog[i].second - automatic_pairing_boundaries_cog[j].second;
1746 if (automatic_pairing_boundaries_cog[i].first != automatic_pairing_boundaries_cog[j].first)
1748 const Real
distance = distance_vector.norm();
1749 const std::pair pair = std::make_pair(automatic_pairing_boundaries_cog[i].first,
1750 automatic_pairing_boundaries_cog[j].first);
1751 pairs_distances.emplace_back(std::make_pair(pair,
distance));
1755 const auto automatic_pairing_distance = getParam<Real>(
"automatic_pairing_distance");
1758 std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> lean_pairs_distances;
1759 for (
const auto & pair_distance : pairs_distances)
1760 if (pair_distance.second <= automatic_pairing_distance)
1762 lean_pairs_distances.emplace_back(pair_distance);
1764 pair_distance.first.first,
1766 pair_distance.first.second,
1767 ", with a relative distance of ",
1768 pair_distance.second);
1772 for (
const auto & lean_pairs_distance : lean_pairs_distances)
1777 if (
_mesh->getBoundaryID(lean_pairs_distance.first.first) >
1778 _mesh->getBoundaryID(lean_pairs_distance.first.second))
1780 {lean_pairs_distance.first.first, lean_pairs_distance.first.second});
1783 {lean_pairs_distance.first.second, lean_pairs_distance.first.first});
1787 removeRepeatedPairs();