572 if (!
_problem->getDisplacedProblem())
574 "Contact requires updated coordinates. Use the 'displacements = ...' parameter in the "
581 if (!
_problem->isSNESMFReuseBaseSetbyUser())
582 _problem->setSNESMFReuseBase(
false,
false);
593 if (!
_problem->getDisplacedProblem())
594 mooseError(
"Contact requires updated coordinates. Use the 'displacements = ...' line in the "
600 const auto & [primary_name, secondary_name] = contact_pair;
606 {
"secondary_gap_offset",
"mapped_primary_gap_offset",
"order"});
608 std::vector<VariableName> displacements =
609 getParam<std::vector<VariableName>>(
"displacements");
610 const auto order =
_problem->systemBaseNonlinear(0)
612 .variable_type(displacements[0])
615 params.
set<
MooseEnum>(
"order") = Utility::enum_to_string<Order>(OrderWrapper{order});
617 params.
set<std::vector<BoundaryName>>(
"boundary") = {secondary_name};
618 params.
set<BoundaryName>(
"paired_boundary") = primary_name;
619 params.
set<AuxVariableName>(
"variable") =
"penetration";
621 params.
set<std::vector<VariableName>>(
"secondary_gap_offset") = {
622 getParam<VariableName>(
"secondary_gap_offset")};
624 params.
set<std::vector<VariableName>>(
"mapped_primary_gap_offset") = {
625 getParam<VariableName>(
"mapped_primary_gap_offset")};
626 params.
set<
bool>(
"use_displaced_mesh") =
true;
633 const auto type =
"MortarUserObjectAux";
635 params.
set<std::vector<BoundaryName>>(
"boundary") = {secondary_name};
636 params.
set<AuxVariableName>(
"variable") =
"gap";
637 params.
set<
bool>(
"use_displaced_mesh") =
true;
639 params.
set<
MooseEnum>(
"contact_quantity") =
"normal_gap";
640 const auto & [primary_id, secondary_id, uo_name] =
642 params.
set<UserObjectName>(
"user_object") = uo_name;
643 std::string
name =
_name +
"_contact_gap_" + std::to_string(primary_id) +
"_" +
644 std::to_string(secondary_id);
652 const unsigned int ndisp = getParam<std::vector<VariableName>>(
"displacements").size();
655 if (
_formulation == ContactFormulation::MORTAR &&
_model == ContactModel::COULOMB && ndisp > 2)
666 const std::string tangential_lagrange_multiplier_name = action_name +
"_tangential_lm";
667 const std::string tangential_lagrange_multiplier_3d_name =
668 action_name +
"_tangential_3d_lm";
670 params.
set<std::vector<VariableName>>(
"tangent_one") = {
671 tangential_lagrange_multiplier_name};
672 params.
set<std::vector<VariableName>>(
"tangent_two") = {
673 tangential_lagrange_multiplier_3d_name};
675 std::vector<std::string> disp_components({
"x",
"y",
"z"});
676 unsigned component_index = 0;
679 for (
const auto & disp_component : disp_components)
681 params.
set<AuxVariableName>(
"variable") =
_name +
"_tangent_" + disp_component;
682 params.
set<
unsigned int>(
"component") = component_index;
684 std::string
name =
_name +
"_mortar_frictional_pressure_" + disp_component +
"_" +
687 _problem->addAuxKernel(
"MortarFrictionalPressureVectorAux",
name, params);
696 std::vector<VariableName> displacements = getParam<std::vector<VariableName>>(
"displacements");
697 const auto order =
_problem->systemBaseNonlinear(0)
699 .variable_type(displacements[0])
701 const auto mortar_lm_order =
702 _lm_space == ContactLMSpace::LINEAR ?
static_cast<int>(FIRST) : order;
703 std::unique_ptr<InputParameters> current_params;
704 const auto create_aux_var_params =
711 const auto aux_order =
_formulation == ContactFormulation::MORTAR ? mortar_lm_order : order;
712 current_params->set<
MooseEnum>(
"order") =
713 Utility::enum_to_string<Order>(OrderWrapper{aux_order});
714 current_params->set<
MooseEnum>(
"family") =
"LAGRANGE";
715 return *current_params;
722 _problem->addAuxVariable(
"MooseVariable",
"penetration", create_aux_var_params());
724 _problem->addAuxVariable(
"MooseVariable",
"nodal_area", create_aux_var_params());
727 _problem->addAuxVariable(
"MooseVariable",
"gap", create_aux_var_params());
730 _problem->addAuxVariable(
"MooseVariable",
"contact_pressure", create_aux_var_params());
732 const unsigned int ndisp = getParam<std::vector<VariableName>>(
"displacements").size();
735 if (
_formulation == ContactFormulation::MORTAR &&
_model == ContactModel::COULOMB && ndisp > 2)
738 std::vector<std::string> disp_components({
"x",
"y",
"z"});
740 for (
const auto & disp_component : disp_components)
744 Utility::enum_to_string<Order>(OrderWrapper{mortar_lm_order});
745 var_params.set<
MooseEnum>(
"family") =
"LAGRANGE";
748 "MooseVariable",
_name +
"_tangent_" + disp_component, var_params);
763 std::vector<BoundaryName> secondary_boundary_vector;
764 for (
const auto *
const action : actions)
765 for (
const auto j : index_range(action->_boundary_pairs))
766 secondary_boundary_vector.push_back(action->_boundary_pairs[j].second);
768 var_params.set<std::vector<BoundaryName>>(
"boundary") = secondary_boundary_vector;
769 var_params.set<std::vector<VariableName>>(
"variable") = {
"nodal_area"};
771 mooseAssert(
_problem,
"Problem pointer is NULL");
773 var_params.set<
bool>(
"use_displaced_mesh") =
true;
775 _problem->addUserObject(
"NodalArea",
895 std::vector<VariableName> displacements = getParam<std::vector<VariableName>>(
"displacements");
896 const unsigned int ndisp = displacements.size();
899 const std::string primary_subdomain_name = action_name +
"_primary_subdomain";
900 const std::string secondary_subdomain_name = action_name +
"_secondary_subdomain";
901 const std::string normal_lagrange_multiplier_name = action_name +
"_normal_lm";
902 const std::string tangential_lagrange_multiplier_name = action_name +
"_tangential_lm";
903 const std::string tangential_lagrange_multiplier_3d_name = action_name +
"_tangential_3d_lm";
904 const std::string auxiliary_lagrange_multiplier_name = action_name +
"_aux_lm";
914 const MeshGeneratorName primary_name = primary_subdomain_name +
"_generator";
915 const MeshGeneratorName secondary_name = secondary_subdomain_name +
"_generator";
920 primary_params.
set<SubdomainName>(
"new_block_name") = primary_subdomain_name;
921 secondary_params.set<SubdomainName>(
"new_block_name") = secondary_subdomain_name;
923 primary_params.set<std::vector<BoundaryName>>(
"sidesets") = {
_boundary_pairs[0].first};
924 secondary_params.set<std::vector<BoundaryName>>(
"sidesets") = {
_boundary_pairs[0].second};
932 const auto addLagrangeMultiplier =
933 [
this, &secondary_subdomain_name, &displacements](
const std::string & variable_name,
934 const Real scaling_factor,
935 const bool add_aux_lm,
936 const bool penalty_traction)
943 if (!add_aux_lm || penalty_traction)
946 mooseAssert(
_problem->systemBaseNonlinear(0).hasVariable(displacements[0]),
947 "Displacement variable is missing");
948 const auto primal_type =
949 _problem->systemBaseNonlinear(0).system().variable_type(displacements[0]);
955 ?
static_cast<int>(FIRST)
956 : primal_type.order.get_order();
958 if (primal_type.family == LAGRANGE)
960 params.
set<
MooseEnum>(
"family") = Utility::enum_to_string<FEFamily>(primal_type.family);
961 params.
set<
MooseEnum>(
"order") = Utility::enum_to_string<Order>(OrderWrapper{lm_order});
964 mooseError(
"Invalid bases for mortar contact.");
966 params.
set<std::vector<SubdomainName>>(
"block") = {secondary_subdomain_name};
967 if (!(add_aux_lm || penalty_traction))
968 params.
set<std::vector<Real>>(
"scaling") = {scaling_factor};
972 if (add_aux_lm || penalty_traction)
973 _problem->addAuxVariable(var_type, variable_name, params);
975 _problem->addVariable(var_type, variable_name, params);
980 addLagrangeMultiplier(
981 normal_lagrange_multiplier_name, getParam<Real>(
"normal_lm_scaling"),
false,
false);
983 if (
_model == ContactModel::COULOMB)
985 addLagrangeMultiplier(tangential_lagrange_multiplier_name,
986 getParam<Real>(
"tangential_lm_scaling"),
990 addLagrangeMultiplier(tangential_lagrange_multiplier_3d_name,
991 getParam<Real>(
"tangential_lm_scaling"),
996 if (getParam<bool>(
"use_petrov_galerkin"))
997 addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0,
true,
false);
1003 addLagrangeMultiplier(auxiliary_lagrange_multiplier_name, 1.0,
false,
true);
1008 const auto register_mortar_uo_name = [
this](
const auto & bnd_pair,
const auto & uo_prefix)
1010 const auto & [primary_name, secondary_name] = bnd_pair;
1011 const auto primary_id =
_mesh->getBoundaryID(primary_name);
1012 const auto secondary_id =
_mesh->getBoundaryID(secondary_name);
1013 const auto uo_name = uo_prefix +
name();
1019 if (
_formulation == ContactFormulation::MORTAR_PENALTY &&
1022 const std::vector<std::string> params = {
"penalty_multiplier",
1023 "penalty_multiplier_friction",
1024 "al_penetration_tolerance",
1025 "al_incremental_slip_tolerance",
1026 "al_frictional_force_tolerance"};
1027 for (
const auto & param : params)
1030 "Augmented Lagrange parameter was specified, but the selected problem type "
1031 "does not support Augmented Lagrange iterations.");
1039 uo_params.set<BoundaryName>(
"secondary_boundary") =
_boundary_pairs[0].second;
1040 uo_params.set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1041 uo_params.set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1042 uo_params.set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1043 uo_params.set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1045 uo_params.set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1046 uo_params.set<
bool>(
"use_displaced_mesh") =
true;
1047 uo_params.set<std::vector<VariableName>>(
"lm_variable") = {normal_lagrange_multiplier_name};
1048 uo_params.applySpecificParameters(
parameters(),
1049 {
"correct_edge_dropping",
1051 "triangulate_triangles",
1052 "minimum_projection_angle",
1053 "mortar_3d_subpatch_plane",
1054 "mortar_3d_qp_mapping",
1055 "use_petrov_galerkin",
1057 if (getParam<bool>(
"use_petrov_galerkin"))
1058 uo_params.set<std::vector<VariableName>>(
"aux_lm") = {auxiliary_lagrange_multiplier_name};
1060 _problem->addUserObject(
"LMWeightedGapUserObject",
1061 register_mortar_uo_name(
_boundary_pairs[0],
"lm_weightedgap_object_"),
1064 else if (
_model == ContactModel::COULOMB &&
_formulation == ContactFormulation::MORTAR)
1068 uo_params.set<BoundaryName>(
"secondary_boundary") =
_boundary_pairs[0].second;
1069 uo_params.set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1070 uo_params.set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1071 uo_params.set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1072 uo_params.set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1074 uo_params.set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1076 uo_params.set<VariableName>(
"secondary_variable") = displacements[0];
1077 uo_params.set<
bool>(
"use_displaced_mesh") =
true;
1078 uo_params.set<std::vector<VariableName>>(
"lm_variable_normal") = {
1079 normal_lagrange_multiplier_name};
1080 uo_params.set<std::vector<VariableName>>(
"lm_variable_tangential_one") = {
1081 tangential_lagrange_multiplier_name};
1083 uo_params.set<std::vector<VariableName>>(
"lm_variable_tangential_two") = {
1084 tangential_lagrange_multiplier_3d_name};
1085 uo_params.applySpecificParameters(
parameters(),
1086 {
"correct_edge_dropping",
1088 "triangulate_triangles",
1089 "minimum_projection_angle",
1090 "mortar_3d_subpatch_plane",
1091 "mortar_3d_qp_mapping",
1092 "use_petrov_galerkin",
1094 if (getParam<bool>(
"use_petrov_galerkin"))
1095 uo_params.set<std::vector<VariableName>>(
"aux_lm") = {auxiliary_lagrange_multiplier_name};
1097 const auto uo_name =
_problem->addUserObject(
1098 "LMWeightedVelocitiesUserObject",
1099 register_mortar_uo_name(
_boundary_pairs[0],
"lm_weightedvelocities_object_"),
1103 if (
_model != ContactModel::COULOMB &&
_formulation == ContactFormulation::MORTAR_PENALTY)
1108 uo_params.set<BoundaryName>(
"secondary_boundary") =
_boundary_pairs[0].second;
1109 uo_params.set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1110 uo_params.set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1111 uo_params.set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1112 uo_params.set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1115 uo_params.applySpecificParameters(
parameters(),
1116 {
"correct_edge_dropping",
1118 "triangulate_triangles",
1119 "minimum_projection_angle",
1120 "mortar_3d_subpatch_plane",
1121 "mortar_3d_qp_mapping",
1124 "max_penalty_multiplier",
1125 "adaptivity_penalty_normal"});
1128 uo_params.set<Real>(
"penetration_tolerance") = getParam<Real>(
"al_penetration_tolerance");
1130 uo_params.set<Real>(
"penalty_multiplier") = getParam<Real>(
"penalty_multiplier");
1133 uo_params.set<
bool>(
"use_physical_gap") =
true;
1136 uo_params.set<std::vector<VariableName>>(
"aux_lm") = {auxiliary_lagrange_multiplier_name};
1139 uo_params.set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1140 uo_params.set<
bool>(
"use_displaced_mesh") =
true;
1143 "PenaltyWeightedGapUserObject",
1144 register_mortar_uo_name(
_boundary_pairs[0],
"penalty_weightedgap_object_"),
1148 else if (
_model == ContactModel::COULOMB &&
_formulation == ContactFormulation::MORTAR_PENALTY)
1152 uo_params.set<BoundaryName>(
"secondary_boundary") =
_boundary_pairs[0].second;
1153 uo_params.set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1154 uo_params.set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1155 uo_params.set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1156 uo_params.set<
bool>(
"correct_edge_dropping") = getParam<bool>(
"correct_edge_dropping");
1157 uo_params.set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1159 uo_params.set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1161 uo_params.set<VariableName>(
"secondary_variable") = displacements[0];
1162 uo_params.set<
bool>(
"use_displaced_mesh") =
true;
1163 uo_params.set<Real>(
"friction_coefficient") = getParam<Real>(
"friction_coefficient");
1164 uo_params.set<Real>(
"penalty") = getParam<Real>(
"penalty");
1165 uo_params.set<Real>(
"penalty_friction") = getParam<Real>(
"penalty_friction");
1168 uo_params.set<Real>(
"max_penalty_multiplier") = getParam<Real>(
"max_penalty_multiplier");
1169 uo_params.set<
MooseEnum>(
"adaptivity_penalty_normal") =
1170 getParam<MooseEnum>(
"adaptivity_penalty_normal");
1171 uo_params.set<
MooseEnum>(
"adaptivity_penalty_friction") =
1172 getParam<MooseEnum>(
"adaptivity_penalty_friction");
1174 uo_params.set<Real>(
"penetration_tolerance") = getParam<Real>(
"al_penetration_tolerance");
1176 uo_params.set<Real>(
"penalty_multiplier") = getParam<Real>(
"penalty_multiplier");
1178 uo_params.set<Real>(
"penalty_multiplier_friction") =
1179 getParam<Real>(
"penalty_multiplier_friction");
1182 uo_params.set<Real>(
"slip_tolerance") = getParam<Real>(
"al_incremental_slip_tolerance");
1185 uo_params.set<
bool>(
"use_physical_gap") =
true;
1188 uo_params.set<std::vector<VariableName>>(
"aux_lm") = {auxiliary_lagrange_multiplier_name};
1190 uo_params.applySpecificParameters(
parameters(),
1192 "triangulate_triangles",
1193 "minimum_projection_angle",
1194 "mortar_3d_subpatch_plane",
1195 "mortar_3d_qp_mapping",
1196 "friction_coefficient",
1198 "penalty_friction"});
1201 "PenaltyFrictionUserObject",
1202 register_mortar_uo_name(
_boundary_pairs[0],
"penalty_friction_object_"),
1213 std::string mortar_constraint_name;
1216 mortar_constraint_name =
"ComputeWeightedGapLMMechanicalContact";
1218 mortar_constraint_name =
"ComputeDynamicWeightedGapLMMechanicalContact";
1223 parameters(), {
"newmark_beta",
"newmark_gamma",
"capture_tolerance",
"wear_depth"});
1226 params.
set<UserObjectName>(
"weighted_gap_uo") =
"lm_weightedgap_object_" +
name();
1230 params.
set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1231 params.
set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1232 params.
set<NonlinearVariableName>(
"variable") = normal_lagrange_multiplier_name;
1233 params.
set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1234 params.
set<Real>(
"c") = getParam<Real>(
"c_normal");
1237 params.
set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1239 params.
set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1241 params.
set<
bool>(
"use_displaced_mesh") =
true;
1244 {
"correct_edge_dropping",
1246 "triangulate_triangles",
1247 "minimum_projection_angle",
1248 "mortar_3d_subpatch_plane",
1249 "mortar_3d_qp_mapping",
1251 "extra_vector_tags",
1252 "absolute_value_vector_tags",
1256 mortar_constraint_name, action_name +
"_normal_lm_weighted_gap", params);
1260 else if (
_model == ContactModel::COULOMB &&
_formulation == ContactFormulation::MORTAR)
1262 std::string mortar_constraint_name;
1265 mortar_constraint_name =
"ComputeFrictionalForceLMMechanicalContact";
1267 mortar_constraint_name =
"ComputeDynamicFrictionalForceLMMechanicalContact";
1272 parameters(), {
"newmark_beta",
"newmark_gamma",
"capture_tolerance",
"wear_depth"});
1275 params.
set<UserObjectName>(
"weighted_gap_uo") =
"lm_weightedvelocities_object_" +
name();
1276 params.
set<UserObjectName>(
"weighted_velocities_uo") =
1277 "lm_weightedvelocities_object_" +
name();
1280 params.
set<
bool>(
"correct_edge_dropping") = getParam<bool>(
"correct_edge_dropping");
1283 params.
set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1284 params.
set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1285 params.
set<
bool>(
"use_displaced_mesh") =
true;
1286 params.
set<Real>(
"c_t") = getParam<Real>(
"c_tangential");
1287 params.
set<Real>(
"c") = getParam<Real>(
"c_normal");
1288 params.
set<
bool>(
"normalize_c") = getParam<bool>(
"normalize_c");
1289 params.
set<
bool>(
"compute_primal_residuals") =
false;
1291 params.
set<
MooseEnum>(
"segment_quadrature") = getParam<MooseEnum>(
"segment_quadrature");
1293 params.
set<std::vector<VariableName>>(
"disp_x") = {displacements[0]};
1296 params.
set<std::vector<VariableName>>(
"disp_y") = {displacements[1]};
1298 params.
set<std::vector<VariableName>>(
"disp_z") = {displacements[2]};
1300 params.
set<NonlinearVariableName>(
"variable") = normal_lagrange_multiplier_name;
1301 params.
set<std::vector<VariableName>>(
"friction_lm") = {tangential_lagrange_multiplier_name};
1304 params.
set<std::vector<VariableName>>(
"friction_lm_dir") = {
1305 tangential_lagrange_multiplier_3d_name};
1307 params.
set<Real>(
"mu") = getParam<Real>(
"friction_coefficient");
1310 "triangulate_triangles",
1311 "minimum_projection_angle",
1312 "mortar_3d_subpatch_plane",
1313 "mortar_3d_qp_mapping",
1314 "extra_vector_tags",
1315 "absolute_value_vector_tags",
1318 _problem->addConstraint(mortar_constraint_name, action_name +
"_tangential_lm", params);
1322 const auto addMechanicalContactConstraints =
1323 [
this, &primary_subdomain_name, &secondary_subdomain_name, &displacements](
1324 const std::string & variable_name,
1325 const std::string & constraint_prefix,
1326 const std::string & constraint_type,
1327 const bool is_additional_frictional_constraint,
1328 const bool is_normal_constraint)
1332 params.
set<
bool>(
"correct_edge_dropping") = getParam<bool>(
"correct_edge_dropping");
1335 params.
set<SubdomainName>(
"primary_subdomain") = primary_subdomain_name;
1336 params.
set<SubdomainName>(
"secondary_subdomain") = secondary_subdomain_name;
1339 params.
set<NonlinearVariableName>(
"variable") = variable_name;
1341 params.
set<
MooseEnum>(
"segment_quadrature") = getParam<MooseEnum>(
"segment_quadrature");
1342 params.
set<
bool>(
"use_displaced_mesh") =
true;
1343 params.
set<
bool>(
"compute_lm_residuals") =
false;
1347 if (is_additional_frictional_constraint)
1351 "triangulate_triangles",
1352 "minimum_projection_angle",
1353 "mortar_3d_subpatch_plane",
1354 "mortar_3d_qp_mapping",
1355 "extra_vector_tags",
1356 "absolute_value_vector_tags",
1359 for (
unsigned int i = 0; i < displacements.size(); ++i)
1363 params.
set<VariableName>(
"secondary_variable") = displacements[i];
1366 if (is_normal_constraint &&
_model != ContactModel::COULOMB &&
1368 params.
set<UserObjectName>(
"weighted_gap_uo") =
"lm_weightedgap_object_" +
name();
1369 else if (is_normal_constraint &&
_model == ContactModel::COULOMB &&
1371 params.
set<UserObjectName>(
"weighted_gap_uo") =
"lm_weightedvelocities_object_" +
name();
1373 params.
set<UserObjectName>(
"weighted_velocities_uo") =
1374 "lm_weightedvelocities_object_" +
name();
1375 else if (is_normal_constraint &&
_model != ContactModel::COULOMB &&
1377 params.
set<UserObjectName>(
"weighted_gap_uo") =
"penalty_weightedgap_object_" +
name();
1378 else if (is_normal_constraint &&
_model == ContactModel::COULOMB &&
1380 params.
set<UserObjectName>(
"weighted_gap_uo") =
"penalty_friction_object_" +
name();
1381 else if (
_formulation == ContactFormulation::MORTAR_PENALTY)
1382 params.
set<UserObjectName>(
"weighted_velocities_uo") =
1383 "penalty_friction_object_" +
name();
1385 _problem->addConstraint(constraint_type, constraint_name, params);
1391 addMechanicalContactConstraints(normal_lagrange_multiplier_name,
1392 action_name +
"_normal_constraint_",
1393 "NormalMortarMechanicalContact",
1397 if (
_model == ContactModel::COULOMB)
1399 addMechanicalContactConstraints(tangential_lagrange_multiplier_name,
1400 action_name +
"_tangential_constraint_",
1401 "TangentialMortarMechanicalContact",
1405 addMechanicalContactConstraints(tangential_lagrange_multiplier_3d_name,
1406 action_name +
"_tangential_constraint_3d_",
1407 "TangentialMortarMechanicalContact",
1506 mooseInfo(
"The contact action is reading the list of boundaries and automatically pairs them "
1507 "if the distance between nodes is less than a specified distance.");
1510 mooseError(
"Failed to obtain mesh for automatically generating contact pairs.");
1512 if (!
_mesh->getMesh().is_serial())
1514 "automatic_pairing_boundaries",
1515 "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1518 std::vector<BoundaryID> _automatic_pairing_boundaries_id;
1520 _automatic_pairing_boundaries_id.emplace_back(
_mesh->getBoundaryID(sideset_name));
1523 std::vector<NodeBoundaryIDInfo> node_boundary_id_vector;
1528 for (
const auto & bnode : bnd_nodes)
1530 const BoundaryID boundary_id = bnode->_bnd_id;
1531 const Node * node_ptr = bnode->_node;
1534 auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1535 _automatic_pairing_boundaries_id.end(),
1538 if (it != _automatic_pairing_boundaries_id.end())
1539 node_boundary_id_vector.emplace_back(node_ptr, boundary_id);
1543 std::sort(node_boundary_id_vector.begin(),
1544 node_boundary_id_vector.end(),
1546 { return first_pair.second < second_pair.second; });
1549 using KDTreeType = nanoflann::KDTreeSingleIndexAdaptor<
1550 nanoflann::L2_Simple_Adaptor<Real, PointListAdaptor<NodeBoundaryIDInfo>, Real, std::size_t>,
1556 const unsigned int max_leaf_size = 20;
1560 node_boundary_id_vector.end());
1561 auto kd_tree = std::make_unique<KDTreeType>(
1562 LIBMESH_DIM, point_list, nanoflann::KDTreeSingleIndexAdaptorParams(max_leaf_size));
1565 mooseError(
"Internal error. KDTree was not properly initialized in the contact action.");
1567 kd_tree->buildIndex();
1571 std::vector<nanoflann::ResultItem<std::size_t, Real>> ret_matches;
1573 const auto radius_for_search = getParam<Real>(
"automatic_pairing_distance");
1576 for (
const auto & pair : node_boundary_id_vector)
1579 ret_matches.clear();
1582 const Point search_point = *pair.first;
1585 kd_tree->radiusSearch(
1586 &(search_point)(0), radius_for_search * radius_for_search, ret_matches, search_params);
1588 for (
auto & match_pair : ret_matches)
1590 const auto & match = node_boundary_id_vector[match_pair.first];
1597 auto it = std::find(_automatic_pairing_boundaries_id.begin(),
1598 _automatic_pairing_boundaries_id.end(),
1602 if (match.second == pair.second)
1608 if (it != _automatic_pairing_boundaries_id.end())
1610 const auto index_one = cast_int<int>(it - _automatic_pairing_boundaries_id.begin());
1611 auto it_other = std::find(_automatic_pairing_boundaries_id.begin(),
1612 _automatic_pairing_boundaries_id.end(),
1615 mooseAssert(it_other != _automatic_pairing_boundaries_id.end(),
1616 "Error in contact action. Unable to find boundary ID for node proximity "
1617 "automatic pairing.");
1619 const auto index_two = cast_int<int>(it_other - _automatic_pairing_boundaries_id.begin());
1621 if (pair.second > match.second)
1635 "The following boundary pairs were created by the contact action using nodal proximity: ");
1638 "Primary boundary ID: ", primary,
" and secondary boundary ID: ", secondary,
".");
1644 mooseInfo(
"The contact action is reading the list of boundaries and automatically pairs them "
1645 "if their centroids fall within a specified distance of each other.");
1648 mooseError(
"Failed to obtain mesh for automatically generating contact pairs.");
1650 if (!
_mesh->getMesh().is_serial())
1652 "automatic_pairing_boundaries",
1653 "The generation of automatic contact pairs in the contact action requires a serial mesh.");
1656 std::vector<std::pair<BoundaryName, Point>> automatic_pairing_boundaries_cog;
1657 const auto & sideset_ids =
_mesh->meshSidesetIds();
1659 const auto & bnd_to_elem_map =
_mesh->getBoundariesToActiveSemiLocalElemIds();
1664 const auto find_set = sideset_ids.find(
_mesh->getBoundaryID(sideset_name));
1665 if (find_set == sideset_ids.end())
1668 " is not defined as a sideset in the mesh.");
1670 auto dofs_set = bnd_to_elem_map.find(
_mesh->getBoundaryID(sideset_name));
1673 Point center_of_gravity(0, 0, 0);
1674 Real accumulated_sideset_area(0);
1677 std::unique_ptr<const Elem> side_ptr;
1678 const std::unordered_set<dof_id_type> & bnd_elems = dofs_set->second;
1680 for (
auto elem_id : bnd_elems)
1682 const Elem * elem =
_mesh->elemPtr(elem_id);
1683 unsigned int side =
_mesh->sideWithBoundaryID(elem,
_mesh->getBoundaryID(sideset_name));
1686 elem->side_ptr(side_ptr, side);
1689 const auto side_area = side_ptr->volume();
1692 const auto side_position = side_ptr->true_centroid();
1694 center_of_gravity += side_position * side_area;
1695 accumulated_sideset_area += side_area;
1699 center_of_gravity /= accumulated_sideset_area;
1702 automatic_pairing_boundaries_cog.emplace_back(sideset_name, center_of_gravity);
1706 std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> pairs_distances;
1709 for (std::size_t i = 0; i < automatic_pairing_boundaries_cog.size() - 1; i++)
1710 for (std::size_t j = i + 1; j < automatic_pairing_boundaries_cog.size(); j++)
1712 const Point & distance_vector =
1713 automatic_pairing_boundaries_cog[i].second - automatic_pairing_boundaries_cog[j].second;
1715 if (automatic_pairing_boundaries_cog[i].first != automatic_pairing_boundaries_cog[j].first)
1717 const Real
distance = distance_vector.norm();
1718 const std::pair pair = std::make_pair(automatic_pairing_boundaries_cog[i].first,
1719 automatic_pairing_boundaries_cog[j].first);
1720 pairs_distances.emplace_back(std::make_pair(pair,
distance));
1724 const auto automatic_pairing_distance = getParam<Real>(
"automatic_pairing_distance");
1727 std::vector<std::pair<std::pair<BoundaryName, BoundaryName>, Real>> lean_pairs_distances;
1728 for (
const auto & pair_distance : pairs_distances)
1729 if (pair_distance.second <= automatic_pairing_distance)
1731 lean_pairs_distances.emplace_back(pair_distance);
1733 pair_distance.first.first,
1735 pair_distance.first.second,
1736 ", with a relative distance of ",
1737 pair_distance.second);
1741 for (
const auto & lean_pairs_distance : lean_pairs_distances)
1746 if (
_mesh->getBoundaryID(lean_pairs_distance.first.first) >
1747 _mesh->getBoundaryID(lean_pairs_distance.first.second))
1749 {lean_pairs_distance.first.first, lean_pairs_distance.first.second});
1752 {lean_pairs_distance.first.second, lean_pairs_distance.first.first});
1756 removeRepeatedPairs();