22 #include "libmesh/mesh_tools.h" 23 #include "libmesh/explicit_system.h" 24 #include "libmesh/numeric_vector.h" 25 #include "libmesh/elem.h" 26 #include "libmesh/node.h" 27 #include "libmesh/dof_map.h" 28 #include "libmesh/edge_edge2.h" 29 #include "libmesh/edge_edge3.h" 30 #include "libmesh/face_tri3.h" 31 #include "libmesh/face_quad4.h" 32 #include "libmesh/exodusII_io.h" 33 #include "libmesh/quadrature_gauss.h" 34 #include "libmesh/quadrature_nodal.h" 35 #include "libmesh/distributed_mesh.h" 36 #include "libmesh/replicated_mesh.h" 37 #include "libmesh/enum_to_string.h" 38 #include "libmesh/statistics.h" 39 #include "libmesh/equation_systems.h" 41 #include "metaphysicl/dualnumber.h" 54 #if NANOFLANN_VERSION < 0x150 68 params.addPrivateParam<
MooseApp *>(
"_moose_app",
nullptr);
69 params.set<std::string>(
"_type") =
"MortarNodalGeometryOutput";
81 if (_amg._secondary_node_to_nodal_normal.empty() ||
82 _amg._secondary_node_to_hh_nodal_tangents.empty())
83 mooseError(
"No entries found in the secondary node -> nodal geometry map.");
85 auto & problem = _app.feProblem();
86 auto & subproblem = _amg._on_displaced
87 ?
static_cast<SubProblem &
>(*problem.getDisplacedProblem())
88 : static_cast<SubProblem &>(problem);
89 auto & nodal_normals_es = subproblem.
es();
91 const std::string nodal_normals_sys_name =
"nodal_normals";
93 if (!_nodal_normals_system)
95 for (
const auto s :
make_range(nodal_normals_es.n_systems()))
96 if (!nodal_normals_es.get_system(s).is_initialized())
101 _nodal_normals_system =
102 &nodal_normals_es.template add_system<ExplicitSystem>(nodal_normals_sys_name);
103 _nnx_var_num = _nodal_normals_system->add_variable(
"nodal_normal_x",
FEType(
FIRST,
LAGRANGE)),
104 _nny_var_num = _nodal_normals_system->add_variable(
"nodal_normal_y",
FEType(
FIRST,
LAGRANGE));
105 _nnz_var_num = _nodal_normals_system->add_variable(
"nodal_normal_z",
FEType(
FIRST,
LAGRANGE));
120 nodal_normals_es.
reinit();
123 const DofMap & dof_map = _nodal_normals_system->get_dof_map();
124 std::vector<dof_id_type> dof_indices_nnx, dof_indices_nny, dof_indices_nnz;
125 std::vector<dof_id_type> dof_indices_t1x, dof_indices_t1y, dof_indices_t1z;
126 std::vector<dof_id_type> dof_indices_t2x, dof_indices_t2y, dof_indices_t2z;
128 for (MeshBase::const_element_iterator el = _amg._mesh.elements_begin(),
129 end_el = _amg._mesh.elements_end();
133 const Elem * elem = *el;
136 dof_map.
dof_indices(elem, dof_indices_nnx, _nnx_var_num);
137 dof_map.
dof_indices(elem, dof_indices_nny, _nny_var_num);
138 dof_map.
dof_indices(elem, dof_indices_nnz, _nnz_var_num);
140 dof_map.
dof_indices(elem, dof_indices_t1x, _t1x_var_num);
141 dof_map.
dof_indices(elem, dof_indices_t1y, _t1y_var_num);
142 dof_map.
dof_indices(elem, dof_indices_t1z, _t1z_var_num);
144 dof_map.
dof_indices(elem, dof_indices_t2x, _t2x_var_num);
145 dof_map.
dof_indices(elem, dof_indices_t2y, _t2y_var_num);
146 dof_map.
dof_indices(elem, dof_indices_t2z, _t2z_var_num);
154 auto it = _amg._secondary_node_to_nodal_normal.find(elem->
node_ptr(n));
155 if (it != _amg._secondary_node_to_nodal_normal.end())
157 _nodal_normals_system->solution->set(dof_indices_nnx[n], it->second(0));
158 _nodal_normals_system->solution->set(dof_indices_nny[n], it->second(1));
159 _nodal_normals_system->solution->set(dof_indices_nnz[n], it->second(2));
162 auto it_tangent = _amg._secondary_node_to_hh_nodal_tangents.find(elem->
node_ptr(n));
163 if (it_tangent != _amg._secondary_node_to_hh_nodal_tangents.end())
165 _nodal_normals_system->solution->set(dof_indices_t1x[n], it_tangent->second[0](0));
166 _nodal_normals_system->solution->set(dof_indices_t1y[n], it_tangent->second[0](1));
167 _nodal_normals_system->solution->set(dof_indices_t1z[n], it_tangent->second[0](2));
169 _nodal_normals_system->solution->set(dof_indices_t2x[n], it_tangent->second[1](0));
170 _nodal_normals_system->solution->set(dof_indices_t2y[n], it_tangent->second[1](1));
171 _nodal_normals_system->solution->set(dof_indices_t2z[n], it_tangent->second[1](2));
178 _nodal_normals_system->solution->close();
180 std::set<std::string> sys_names = {nodal_normals_sys_name};
189 "nodal_geometry_only.e", nodal_normals_es, &sys_names);
216 const std::pair<BoundaryID, BoundaryID> & boundary_key,
217 const std::pair<SubdomainID, SubdomainID> & subdomain_key,
221 const bool correct_edge_dropping,
222 const Real minimum_projection_angle)
227 _on_displaced(on_displaced),
229 _distributed(!_mesh.is_replicated()),
230 _correct_edge_dropping(correct_edge_dropping),
231 _minimum_projection_angle(minimum_projection_angle)
257 "mortar_nodal_geometry_" +
289 "Must specify secondary and primary boundary ids before building node-to-elem maps.");
292 for (
const auto & secondary_elem :
299 for (
const auto & nd : secondary_elem->node_ref_range())
302 vec.push_back(secondary_elem);
307 for (
const auto & primary_elem :
314 for (
const auto & nd : primary_elem->node_ref_range())
317 vec.push_back(primary_elem);
325 std::vector<Point> nodal_normals(secondary_elem.
n_nodes());
329 return nodal_normals;
337 "Map should locate secondary element");
342 std::map<unsigned int, unsigned int>
345 std::map<unsigned int, unsigned int> secondary_ip_i_to_lower_secondary_i;
347 mooseAssert(secondary_ip,
"This should be non-null");
351 const auto & nd = lower_secondary_elem.
node_ref(i);
352 secondary_ip_i_to_lower_secondary_i[secondary_ip->
get_node_index(&nd)] = i;
355 return secondary_ip_i_to_lower_secondary_i;
358 std::map<unsigned int, unsigned int>
360 const Elem & lower_primary_elem,
361 const Elem & primary_elem,
364 std::map<unsigned int, unsigned int> primary_ip_i_to_lower_primary_i;
368 const auto & nd = lower_primary_elem.
node_ref(i);
369 primary_ip_i_to_lower_primary_i[primary_elem.
get_node_index(&nd)] = i;
372 return primary_ip_i_to_lower_primary_i;
375 std::array<MooseUtils::SemidynamicVector<Point, 9>, 2>
384 const auto & tangent_vectors =
386 nodal_tangents_one.
push_back(tangent_vectors[0]);
387 nodal_tangents_two.
push_back(tangent_vectors[1]);
390 return {{nodal_tangents_one, nodal_tangents_two}};
395 const std::vector<Real> & oned_xi1_pts)
const 397 std::vector<Point> xi1_pts(oned_xi1_pts.size());
399 xi1_pts[qp] = oned_xi1_pts[qp];
406 const std::vector<Point> & xi1_pts)
const 409 const auto num_qps = xi1_pts.size();
411 std::vector<Point> normals(num_qps);
423 normals[qp] += phi * nodal_normals[n];
427 for (
auto & normal : normals)
437 std::size_t node_unique_id_offset = 0;
445 const auto primary_bnd_id = pr.first;
446 const auto secondary_bnd_id = pr.second;
447 const auto num_primary_nodes =
448 std::distance(
_mesh.bid_nodes_begin(primary_bnd_id),
_mesh.bid_nodes_end(primary_bnd_id));
449 const auto num_secondary_nodes = std::distance(
_mesh.bid_nodes_begin(secondary_bnd_id),
450 _mesh.bid_nodes_end(secondary_bnd_id));
451 mooseAssert(num_primary_nodes,
452 "There are no primary nodes on boundary ID " 453 << primary_bnd_id <<
". Does that bondary ID even exist on the mesh?");
454 mooseAssert(num_secondary_nodes,
455 "There are no secondary nodes on boundary ID " 456 << secondary_bnd_id <<
". Does that bondary ID even exist on the mesh?");
458 node_unique_id_offset += num_primary_nodes + 2 * num_secondary_nodes;
462 for (MeshBase::const_element_iterator el =
_mesh.active_elements_begin(),
463 end_el =
_mesh.active_elements_end();
467 const Elem * secondary_elem = *el;
473 std::vector<Node *> new_nodes;
474 for (MooseIndex(secondary_elem->
n_nodes()) n = 0; n < secondary_elem->
n_nodes(); ++n)
478 Node *
const new_node = new_nodes.back();
482 std::unique_ptr<Elem> new_elem;
484 new_elem = std::make_unique<Edge3>();
486 new_elem = std::make_unique<Edge2>();
488 new_elem->processor_id() = secondary_elem->
processor_id();
489 new_elem->subdomain_id() = secondary_elem->
subdomain_id();
490 new_elem->set_id(local_id_index++);
491 new_elem->set_unique_id(new_elem->id());
493 for (MooseIndex(new_elem->n_nodes()) n = 0; n < new_elem->n_nodes(); ++n)
494 new_elem->set_node(n) = new_nodes[n];
505 std::make_pair(secondary_elem->
node_ptr(0), secondary_elem)),
507 std::make_pair(secondary_elem->
node_ptr(1), secondary_elem));
509 bool new_container_node0_found =
511 new_container_node1_found =
514 const Elem * node0_primary_candidate =
nullptr;
515 const Elem * node1_primary_candidate =
nullptr;
517 if (new_container_node0_found)
519 const auto & xi2_primary_elem_pair = new_container_it0->second;
520 msinfo.
xi2_a = xi2_primary_elem_pair.first;
521 node0_primary_candidate = xi2_primary_elem_pair.second;
524 if (new_container_node1_found)
526 const auto & xi2_primary_elem_pair = new_container_it1->second;
527 msinfo.
xi2_b = xi2_primary_elem_pair.first;
528 node1_primary_candidate = xi2_primary_elem_pair.second;
535 if (node0_primary_candidate == node1_primary_candidate)
550 auto val = pr.second;
552 const Node * primary_node = std::get<1>(key);
553 Real xi1 = val.first;
554 const Elem * secondary_elem = val.second;
564 for (MooseIndex(secondary_elem->
n_nodes()) n = 0; n < secondary_elem->
n_nodes(); ++n)
569 Elem * current_mortar_segment =
nullptr;
572 for (
const auto & mortar_segment_candidate : mortar_segment_set)
578 catch (std::out_of_range &)
580 mooseError(
"MortarSegmentInfo not found for the mortar segment candidate");
582 if (
info->xi1_a <= xi1 && xi1 <= info->xi1_b)
584 current_mortar_segment = mortar_segment_candidate;
590 if (current_mortar_segment ==
nullptr)
591 mooseError(
"Unable to find appropriate mortar segment during linear search!");
598 if (
info->xi1_a == xi1 || xi1 ==
info->xi1_b)
603 "new_id must be the same on all processes");
604 Node *
const new_node =
606 new_node->set_unique_id(new_id + node_unique_id_offset);
611 const Point normal =
getNormals(*secondary_elem, std::vector<Real>({xi1}))[0];
616 mooseError(
"We should already have built this primary node to elem pair!");
617 const std::vector<const Elem *> & primary_node_neighbors =
621 if (primary_node_neighbors.size() == 0 || primary_node_neighbors.size() > 2)
622 mooseError(
"We must have either 1 or 2 primary side nodal neighbors, but we had ",
623 primary_node_neighbors.size());
630 const Elem * left_primary_elem = primary_node_neighbors[0];
631 const Elem * right_primary_elem =
632 (primary_node_neighbors.size() == 2) ? primary_node_neighbors[1] :
nullptr;
638 std::array<Real, 2> secondary_node_cps;
639 std::vector<Real> primary_node_cps(primary_node_neighbors.size());
642 for (
unsigned int nid = 0; nid < 2; ++nid)
643 secondary_node_cps[nid] = normal.
cross(secondary_elem->
point(nid) - new_pt)(2);
645 for (MooseIndex(primary_node_neighbors) mnn = 0; mnn < primary_node_neighbors.size(); ++mnn)
647 const Elem * primary_neigh = primary_node_neighbors[mnn];
648 Point opposite = (primary_neigh->
node_ptr(0) == primary_node) ? primary_neigh->
point(1)
649 : primary_neigh->
point(0);
651 primary_node_cps[mnn] = cp(2);
655 bool orientation1_valid =
false, orientation2_valid =
false;
657 if (primary_node_neighbors.size() == 2)
660 orientation1_valid = (secondary_node_cps[0] * primary_node_cps[0] > 0.) &&
661 (secondary_node_cps[1] * primary_node_cps[1] > 0.);
663 orientation2_valid = (secondary_node_cps[0] * primary_node_cps[1] > 0.) &&
664 (secondary_node_cps[1] * primary_node_cps[0] > 0.);
666 else if (primary_node_neighbors.size() == 1)
669 orientation1_valid = (secondary_node_cps[0] * primary_node_cps[0] > 0.);
670 orientation2_valid = (secondary_node_cps[1] * primary_node_cps[0] > 0.);
673 mooseError(
"Invalid primary node neighbors size ", primary_node_neighbors.size());
679 if (orientation1_valid && orientation2_valid)
681 "AutomaticMortarGeneration: Both orientations cannot simultaneously be valid.");
687 if (!orientation1_valid && !orientation2_valid)
690 "AutomaticMortarGeneration: Unable to determine valid secondary-primary orientation. " 691 "Consequently we will consider projection of the primary node invalid and not split the " 693 "This situation can indicate there are very oblique projections between primary (mortar) " 694 "and secondary (non-mortar) surfaces for a good problem set up. It can also mean your " 695 "time step is too large. This message is only printed once."));
700 std::unique_ptr<Elem> new_elem_left;
702 new_elem_left = std::make_unique<Edge3>();
704 new_elem_left = std::make_unique<Edge2>();
706 new_elem_left->processor_id() = current_mortar_segment->
processor_id();
707 new_elem_left->subdomain_id() = current_mortar_segment->
subdomain_id();
708 new_elem_left->set_id(local_id_index++);
709 new_elem_left->set_unique_id(new_elem_left->id());
710 new_elem_left->set_node(0) = current_mortar_segment->
node_ptr(0);
711 new_elem_left->set_node(1) = new_node;
714 std::unique_ptr<Elem> new_elem_right;
716 new_elem_right = std::make_unique<Edge3>();
718 new_elem_right = std::make_unique<Edge2>();
721 new_elem_right->subdomain_id() = current_mortar_segment->
subdomain_id();
722 new_elem_right->set_id(local_id_index++);
723 new_elem_right->set_unique_id(new_elem_right->id());
724 new_elem_right->set_node(0) = new_node;
725 new_elem_right->set_node(1) = current_mortar_segment->
node_ptr(1);
730 Point left_interior_point(0);
731 Real left_interior_xi = (xi1 +
info->xi1_a) / 2;
734 Real current_left_interior_eta =
735 (2. * left_interior_xi -
info->xi1_a -
info->xi1_b) / (
info->xi1_b -
info->xi1_a);
737 for (MooseIndex(current_mortar_segment->
n_nodes()) n = 0;
738 n < current_mortar_segment->
n_nodes();
741 current_mortar_segment->
point(n);
745 "new_id must be the same on all processes");
747 left_interior_point, new_interior_left_id, new_elem_left->processor_id());
748 new_elem_left->set_node(2) = new_interior_node_left;
749 new_interior_node_left->set_unique_id(new_interior_left_id + node_unique_id_offset);
752 Point right_interior_point(0);
753 Real right_interior_xi = (xi1 +
info->xi1_b) / 2;
755 Real current_right_interior_eta =
756 (2. * right_interior_xi -
info->xi1_a -
info->xi1_b) / (
info->xi1_b -
info->xi1_a);
758 for (MooseIndex(current_mortar_segment->
n_nodes()) n = 0;
759 n < current_mortar_segment->
n_nodes();
762 current_mortar_segment->
point(n);
766 "new_id must be the same on all processes");
768 right_interior_point, new_interior_id_right, new_elem_right->processor_id());
769 new_elem_right->set_node(2) = new_interior_node_right;
770 new_interior_node_right->set_unique_id(new_interior_id_right + node_unique_id_offset);
774 if (orientation2_valid)
775 std::swap(left_primary_elem, right_primary_elem);
779 if (left_primary_elem)
780 left_xi2 = (primary_node == left_primary_elem->
node_ptr(0)) ? -1 : +1;
781 if (right_primary_elem)
782 right_xi2 = (primary_node == right_primary_elem->
node_ptr(0)) ? -1 : +1;
791 mooseError(
"MortarSegmentInfo not found for current_mortar_segment.");
807 new_msinfo_left.
xi1_b = xi1;
808 new_msinfo_left.
xi2_b = left_xi2;
816 mortar_segment_set.insert(msm_new_elem);
826 new_msinfo_right.
xi1_b = current_msinfo.
xi1_b;
827 new_msinfo_right.
xi2_b = current_msinfo.
xi2_b;
829 new_msinfo_right.
xi1_a = xi1;
830 new_msinfo_right.
xi2_a = right_xi2;
835 mortar_segment_set.insert(msm_new_elem);
843 mortar_segment_set.erase(current_mortar_segment);
860 if (primary_elem ==
nullptr ||
std::abs(msinfo.
xi2_a) > 1.0 + TOLERANCE ||
866 "We should have found the element");
867 auto & msm_set = it->second;
868 msm_set.erase(msm_elem);
891 std::unordered_set<Node *> msm_connected_nodes;
896 for (
auto & n : element->node_ref_range())
897 msm_connected_nodes.insert(&n);
900 if (!msm_connected_nodes.count(node))
909 "All mortar segment elements should have valid " 924 mortar_segment_mesh_writer.
write(
"mortar_segment_mesh.e");
943 const auto primary_subd_id = pr.first;
944 const auto secondary_subd_id = pr.second;
949 3, mesh_adaptor, nanoflann::KDTreeSingleIndexAdaptorParams(10));
952 kd_tree.buildIndex();
955 auto get_sub_elem_nodes = [](
const ElemType type,
956 const unsigned int sub_elem) -> std::vector<unsigned int>
963 return {{0, 1, 2, 3}};
977 mooseError(
"get_sub_elem_nodes: Invalid sub_elem: ", sub_elem);
991 return {{4, 5, 6, 7}};
993 mooseError(
"get_sub_elem_nodes: Invalid sub_elem: ", sub_elem);
999 return {{0, 4, 8, 7}};
1001 return {{4, 1, 5, 8}};
1003 return {{8, 5, 2, 6}};
1005 return {{7, 8, 6, 3}};
1007 mooseError(
"get_sub_elem_nodes: Invalid sub_elem: ", sub_elem);
1010 mooseError(
"get_sub_elem_inds: Face element type: ",
1011 libMesh::Utility::enum_to_string<ElemType>(type),
1012 " invalid for 3D mortar");
1019 for (MeshBase::const_element_iterator el =
_mesh.active_local_elements_begin(),
1020 end_el =
_mesh.active_local_elements_end();
1024 const Elem * secondary_side_elem = *el;
1026 const Real secondary_volume = secondary_side_elem->
volume();
1029 if (secondary_side_elem->
subdomain_id() != secondary_subd_id)
1032 auto [secondary_elem_to_msm_map_it, insertion_happened] =
1034 std::set<Elem *, CompareDofObjectsByID>{});
1036 auto & secondary_to_msm_element_set = secondary_elem_to_msm_map_it->second;
1038 std::vector<std::unique_ptr<MortarSegmentHelper>> mortar_segment_helper(
1055 auto sub_elem_nodes = get_sub_elem_nodes(secondary_side_elem->
type(), sel);
1060 std::vector<Point> nodes(sub_elem_nodes.size());
1063 for (
auto iv :
make_range(sub_elem_nodes.size()))
1065 const auto n = sub_elem_nodes[iv];
1066 nodes[iv] = secondary_side_elem->
point(n);
1067 center += secondary_side_elem->
point(n);
1068 normal += nodal_normals[n];
1070 center /= sub_elem_nodes.size();
1071 normal = normal.
unit();
1074 mortar_segment_helper[sel] = std::make_unique<MortarSegmentHelper>(nodes, center, normal);
1085 std::array<Real, 3> query_pt;
1087 switch (secondary_side_elem->
type())
1091 center_point = mortar_segment_helper[0]->center();
1092 query_pt = {{center_point(0), center_point(1), center_point(2)}};
1096 center_point = mortar_segment_helper[1]->center();
1097 query_pt = {{center_point(0), center_point(1), center_point(2)}};
1100 center_point = mortar_segment_helper[4]->center();
1101 query_pt = {{center_point(0), center_point(1), center_point(2)}};
1104 center_point = secondary_side_elem->
point(8);
1105 query_pt = {{center_point(0), center_point(1), center_point(2)}};
1109 "Face element type: ", secondary_side_elem->
type(),
"not supported for 3D mortar");
1115 const std::size_t num_results = 3;
1118 std::vector<size_t> ret_index(num_results);
1119 std::vector<Real> out_dist_sqr(num_results);
1120 nanoflann::KNNResultSet<Real> result_set(num_results);
1121 result_set.init(&ret_index[0], &out_dist_sqr[0]);
1125 std::set<const Elem *, CompareDofObjectsByID> processed_primary_elems;
1129 bool primary_elem_found =
false;
1130 std::set<const Elem *, CompareDofObjectsByID> primary_elem_candidates;
1137 out_dist_sqr[r]) <= TOLERANCE,
1138 "Lower-dimensional element squared distance verification failed.");
1141 std::vector<const Elem *> & node_elems =
1145 for (
auto elem : node_elems)
1146 primary_elem_candidates.insert(elem);
1156 while (!primary_elem_candidates.empty())
1158 const Elem * primary_elem_candidate = *primary_elem_candidates.begin();
1161 if (processed_primary_elems.count(primary_elem_candidate))
1165 std::vector<Point> nodal_points;
1168 std::vector<std::vector<unsigned int>> elem_to_node_map;
1171 std::vector<std::pair<unsigned int, unsigned int>> sub_elem_map;
1180 auto sub_elem_nodes = get_sub_elem_nodes(primary_elem_candidate->
type(), p_el);
1183 std::vector<Point> primary_sub_elem(sub_elem_nodes.size());
1184 for (
auto iv :
make_range(sub_elem_nodes.size()))
1186 const auto n = sub_elem_nodes[iv];
1187 primary_sub_elem[iv] = primary_elem_candidate->
point(n);
1200 mortar_segment_helper[s_el]->getMortarSegments(
1201 primary_sub_elem, nodal_points, elem_to_node_map);
1204 for (
auto i = sub_elem_map.size(); i < elem_to_node_map.size(); ++i)
1205 sub_elem_map.push_back(std::make_pair(s_el, p_el));
1210 processed_primary_elems.insert(primary_elem_candidate);
1211 primary_elem_candidates.erase(primary_elem_candidate);
1214 if (!elem_to_node_map.empty())
1219 if (!primary_elem_found)
1221 primary_elem_found =
true;
1222 primary_elem_candidates.clear();
1229 if (neighbor ==
nullptr || neighbor->subdomain_id() != primary_subd_id)
1232 if (processed_primary_elems.count(neighbor))
1235 primary_elem_candidates.insert(neighbor);
1241 std::vector<Node *> new_nodes;
1242 for (
auto pt : nodal_points)
1250 std::unique_ptr<Elem> new_elem;
1251 if (elem_to_node_map[el].size() == 3)
1252 new_elem = std::make_unique<Tri3>();
1254 mooseError(
"Active mortar segments only supports TRI elements, 3 nodes expected " 1256 elem_to_node_map[el].size(),
1259 new_elem->processor_id() = secondary_side_elem->
processor_id();
1260 new_elem->subdomain_id() = secondary_side_elem->
subdomain_id();
1261 new_elem->set_id(local_id_index++);
1265 new_elem->set_node(i) = new_nodes[elem_to_node_map[el][i]];
1268 if (new_elem->volume() / secondary_volume < TOLERANCE)
1286 secondary_to_msm_element_set.insert(msm_new_elem);
1300 if (mortar_segment_helper[sel]->remainder() == 1.0)
1302 mooseWarning(
"Some secondary elements on mortar interface were unable to identify" 1303 " a corresponding primary element; this may be expected depending on" 1304 " problem geometry but may indicate a failure of the element search" 1308 if (secondary_to_msm_element_set.empty())
1321 if (msm_el->type() !=
TRI3)
1322 msm_el->subdomain_id()++;
1329 mortar_segment_mesh_writer.
write(
"mortar_segment_mesh.e");
1333 if (msm_el->type() !=
TRI3)
1334 msm_el->subdomain_id()--;
1345 mooseWarning(
"Mortar segment mesh statistics intended for debugging purposes in serial only, " 1346 "parallel will only provide statistics for local mortar segment mesh.");
1353 std::unordered_map<processor_id_type, std::vector<std::pair<dof_id_type, dof_id_type>>>
1364 const Elem * secondary_elem = pr.second.secondary_elem;
1365 const Elem * primary_elem = pr.second.primary_elem;
1368 coupling_info[secondary_elem->
processor_id()].emplace_back(
1377 coupling_info[secondary_elem->
processor_id()].emplace_back(
1386 coupling_info[secondary_elem->
processor_id()].emplace_back(secondary_elem->
id(),
1387 primary_elem->
id());
1411 auto action_functor =
1413 const std::vector<std::pair<dof_id_type, dof_id_type>> & coupling_info)
1415 for (
auto [i, j] : coupling_info)
1425 Moose::out <<
"Mortar Interface Statistics:" << std::endl;
1430 const auto primary_subd_id = pr.first;
1431 const auto secondary_subd_id = pr.second;
1438 for (
auto * el :
_mesh.active_element_ptr_range())
1441 if (el->subdomain_id() == secondary_subd_id)
1442 secondary.push_back(el->volume());
1443 else if (el->subdomain_id() == primary_subd_id)
1444 primary.push_back(el->volume());
1452 msm.push_back(msm_elem->volume());
1456 std::vector<std::string> col_names = {
"mesh",
"n_elems",
"max",
"min",
"median"};
1457 std::vector<std::string> subds = {
"secondary_lower",
"primary_lower",
"mortar_segment"};
1458 std::vector<size_t> n_elems = {secondary.size(), primary.size(), msm.size()};
1468 table.
addData<std::string>(col_names[0], subds[i]);
1469 table.
addData<
size_t>(col_names[1], n_elems[i]);
1475 Moose::out <<
"secondary subdomain: " << secondary_subd_id
1476 <<
" \tprimary subdomain: " << primary_subd_id << std::endl;
1491 const Real tol = (
dim() == 3) ? 0.1 : TOLERANCE;
1493 std::unordered_map<processor_id_type, std::set<dof_id_type>> proc_to_inactive_nodes_set;
1497 std::unordered_set<dof_id_type> inactive_node_ids;
1499 std::unordered_map<const Elem *, Real> active_volume{};
1503 active_volume[el] = 0.;
1511 active_volume[secondary_elem] += msm_elem->
volume();
1519 if (
std::abs(active_volume[el] / el->volume() - 1.0) > tol)
1523 inactive_node_ids.insert(el->node_id(n));
1529 const auto secondary_subd_id = pr.second;
1535 const auto pid = el->processor_id();
1542 for (
const auto n :
make_range(el->n_nodes()))
1544 const auto node_id = el->node_id(n);
1545 if (inactive_node_ids.find(node_id) != inactive_node_ids.end())
1546 proc_to_inactive_nodes_set[pid].insert(node_id);
1554 std::unordered_map<processor_id_type, std::vector<dof_id_type>> proc_to_inactive_nodes_vector;
1555 for (
const auto & proc_set : proc_to_inactive_nodes_set)
1556 proc_to_inactive_nodes_vector[proc_set.first].insert(
1557 proc_to_inactive_nodes_vector[proc_set.first].end(),
1558 proc_set.second.begin(),
1559 proc_set.second.end());
1563 const std::vector<dof_id_type> & sent_data)
1566 mooseError(
"Should not be communicating with self.");
1567 for (
const auto pr : sent_data)
1568 inactive_node_ids.insert(pr);
1573 for (
const auto node_id : inactive_node_ids)
1586 std::unordered_map<processor_id_type, std::set<dof_id_type>> proc_to_active_nodes_set;
1590 std::unordered_set<dof_id_type> active_local_nodes;
1599 active_local_nodes.insert(secondary_elem->
node_id(n));
1605 const auto secondary_subd_id = pr.second;
1618 for (
const auto n :
make_range(el->n_nodes()))
1620 const auto node_id = el->node_id(n);
1621 if (active_local_nodes.find(node_id) != active_local_nodes.end())
1622 proc_to_active_nodes_set[pid].insert(node_id);
1630 std::unordered_map<processor_id_type, std::vector<dof_id_type>> proc_to_active_nodes_vector;
1631 for (
const auto & proc_set : proc_to_active_nodes_set)
1633 proc_to_active_nodes_vector[proc_set.first].reserve(proc_to_active_nodes_set.size());
1634 for (
const auto node_id : proc_set.second)
1635 proc_to_active_nodes_vector[proc_set.first].push_back(node_id);
1640 const std::vector<dof_id_type> & sent_data)
1643 mooseError(
"Should not be communicating with self.");
1644 active_local_nodes.insert(sent_data.begin(), sent_data.end());
1655 for (
const auto n :
make_range(el->n_nodes()))
1656 if (active_local_nodes.find(el->node_id(n)) == active_local_nodes.end())
1665 std::unordered_set<const Elem *> active_local_elems;
1671 const Real tol = (
dim() == 3) ? 0.1 : TOLERANCE;
1673 std::unordered_map<const Elem *, Real> active_volume;
1682 active_volume[secondary_elem] += msm_elem->
volume();
1693 if (
std::abs(active_volume[secondary_elem] / secondary_elem->
volume() - 1.0) > tol)
1697 active_local_elems.insert(secondary_elem);
1705 if (active_local_elems.find(el) == active_local_elems.end())
1723 std::map<dof_id_type, std::vector<std::pair<Point, Real>>> node_to_normals_map;
1731 for (MeshBase::const_element_iterator el =
_mesh.active_elements_begin(),
1732 end_el =
_mesh.active_elements_end();
1736 const Elem * secondary_elem = *el;
1745 std::unique_ptr<FEBase> nnx_fe_face(FEBase::build(
dim, nnx_fe_type));
1746 nnx_fe_face->attach_quadrature_rule(&qface);
1747 const std::vector<Point> & face_normals = nnx_fe_face->get_normals();
1749 const auto & JxW = nnx_fe_face->get_JxW();
1754 mooseAssert(interior_parent,
1755 "No interior parent exists for element " 1756 << secondary_elem->
id()
1757 <<
". There may be a problem with your sideset set-up.");
1768 nnx_fe_face->reinit(interior_parent, s);
1770 for (MooseIndex(secondary_elem->
n_nodes()) n = 0; n < secondary_elem->
n_nodes(); ++n)
1772 auto & normals_and_weights_vec = node_to_normals_map[secondary_elem->
node_id(n)];
1773 normals_and_weights_vec.push_back(std::make_pair(
sign * face_normals[n], JxW[n]));
1780 for (
const auto & pr : node_to_normals_map)
1783 const auto & node_id = pr.first;
1784 const auto & normals_and_weights_vec = pr.second;
1787 for (
const auto & norm_and_weight : normals_and_weights_vec)
1788 nodal_normal += norm_and_weight.first * norm_and_weight.second;
1789 nodal_normal = nodal_normal.
unit();
1793 Point nodal_tangent_one;
1794 Point nodal_tangent_two;
1804 Point & nodal_tangent_one,
1805 Point & nodal_tangent_two)
const 1808 "The input nodal normal should have unity norm");
1810 const Real nx = nodal_normal(0);
1811 const Real ny = nodal_normal(1);
1812 const Real nz = nodal_normal(2);
1817 const Point h_vector(nx + 1.0, ny, nz);
1822 if (
std::abs(h_vector(0)) < TOLERANCE)
1824 nodal_tangent_one(0) = 0;
1825 nodal_tangent_one(1) = 1;
1826 nodal_tangent_one(2) = 0;
1828 nodal_tangent_two(0) = 0;
1829 nodal_tangent_two(1) = 0;
1830 nodal_tangent_two(2) = -1;
1837 nodal_tangent_one(0) = -2.0 * h_vector(0) * h_vector(1) / (h * h);
1838 nodal_tangent_one(1) = 1.0 - 2.0 * h_vector(1) * h_vector(1) / (h * h);
1839 nodal_tangent_one(2) = -2.0 * h_vector(1) * h_vector(2) / (h * h);
1841 nodal_tangent_two(0) = -2.0 * h_vector(0) * h_vector(2) / (h * h);
1842 nodal_tangent_two(1) = -2.0 * h_vector(1) * h_vector(2) / (h * h);
1843 nodal_tangent_two(2) = 1.0 - 2.0 * h_vector(2) * h_vector(2) / (h * h);
1859 SubdomainID lower_dimensional_primary_subdomain_id,
1860 SubdomainID lower_dimensional_secondary_subdomain_id)
1865 3, mesh_adaptor, nanoflann::KDTreeSingleIndexAdaptorParams(10));
1868 kd_tree.buildIndex();
1870 for (MeshBase::const_element_iterator el =
_mesh.active_elements_begin(),
1871 end_el =
_mesh.active_elements_end();
1875 const Elem * secondary_side_elem = *el;
1878 if (secondary_side_elem->
subdomain_id() != lower_dimensional_secondary_subdomain_id)
1885 for (MooseIndex(secondary_side_elem->
n_vertices()) n = 0; n < secondary_side_elem->
n_vertices();
1888 const Node * secondary_node = secondary_side_elem->
node_ptr(n);
1892 const std::vector<const Elem *> & secondary_node_neighbors =
1897 bool is_mapped =
true;
1898 for (MooseIndex(secondary_node_neighbors) snn = 0; snn < secondary_node_neighbors.size();
1901 auto secondary_key = std::make_pair(secondary_node, secondary_node_neighbors[snn]);
1917 std::array<Real, 3> query_pt = {
1918 {(*secondary_node)(0), (*secondary_node)(1), (*secondary_node)(2)}};
1924 const std::size_t num_results = 3;
1927 std::vector<size_t> ret_index(num_results);
1928 std::vector<Real> out_dist_sqr(num_results);
1929 nanoflann::KNNResultSet<Real> result_set(num_results);
1930 result_set.init(&ret_index[0], &out_dist_sqr[0]);
1934 bool projection_succeeded =
false;
1938 std::set<const Elem *> rejected_primary_elem_candidates;
1944 for (MooseIndex(result_set) r = 0; r < result_set.size(); ++r)
1948 out_dist_sqr[r]) <= TOLERANCE,
1949 "Lower-dimensional element squared distance verification failed.");
1953 std::vector<const Elem *> & primary_elem_candidates =
1957 for (MooseIndex(primary_elem_candidates) e = 0; e < primary_elem_candidates.size(); ++e)
1959 const Elem * primary_elem_candidate = primary_elem_candidates[e];
1962 if (rejected_primary_elem_candidates.count(primary_elem_candidate))
1968 unsigned int current_iterate = 0, max_iterates = 10;
1974 for (MooseIndex(primary_elem_candidate->
n_nodes()) n = 0;
1975 n < primary_elem_candidate->
n_nodes();
1979 const auto u = x2 - (*secondary_node);
1980 const auto F = u(0) * nodal_normal(1) - u(1) * nodal_normal(0);
1985 if (F.derivatives())
1987 Real dxi2 = -F.value() / F.derivatives();
1995 current_iterate = max_iterates;
1996 }
while (++current_iterate < max_iterates);
1998 Real xi2 = xi2_dn.value();
2004 (primary_elem_candidate->
point(0) - primary_elem_candidate->
point(1)).unit() *
2023 const Node * primary_node = (xi2 < 0) ? primary_elem_candidate->
node_ptr(0)
2024 : primary_elem_candidate->
node_ptr(1);
2026 const std::vector<const Elem *> & primary_node_neighbors =
2029 std::vector<bool> primary_elems_mapped(primary_node_neighbors.size(),
false);
2037 std::vector<Real> secondary_node_neighbor_cps(2), primary_node_neighbor_cps(2);
2041 for (MooseIndex(secondary_node_neighbors) nn = 0;
2042 nn < secondary_node_neighbors.size();
2045 const Elem * secondary_neigh = secondary_node_neighbors[nn];
2046 Point opposite = (secondary_neigh->
node_ptr(0) == secondary_node)
2047 ? secondary_neigh->
point(1)
2048 : secondary_neigh->
point(0);
2049 Point cp = nodal_normal.
cross(opposite - *secondary_node);
2050 secondary_node_neighbor_cps[nn] = cp(2);
2055 for (MooseIndex(primary_node_neighbors) nn = 0; nn < primary_node_neighbors.size();
2058 const Elem * primary_neigh = primary_node_neighbors[nn];
2059 Point opposite = (primary_neigh->
node_ptr(0) == primary_node)
2060 ? primary_neigh->
point(1)
2061 : primary_neigh->
point(0);
2062 Point cp = nodal_normal.
cross(opposite - *secondary_node);
2063 primary_node_neighbor_cps[nn] = cp(2);
2067 bool found_match =
false;
2068 for (MooseIndex(secondary_node_neighbors) snn = 0;
2069 snn < secondary_node_neighbors.size();
2071 for (MooseIndex(primary_node_neighbors) mnn = 0;
2072 mnn < primary_node_neighbors.size();
2074 if (secondary_node_neighbor_cps[snn] * primary_node_neighbor_cps[mnn] > 0)
2077 primary_elems_mapped[mnn] =
true;
2081 Real xi2 = (primary_node == primary_node_neighbors[mnn]->node_ptr(0)) ? -1 : +1;
2082 auto secondary_key =
2083 std::make_pair(secondary_node, secondary_node_neighbors[snn]);
2084 auto primary_val = std::make_pair(xi2, primary_node_neighbors[mnn]);
2090 (secondary_node == secondary_node_neighbors[snn]->node_ptr(0)) ? -1 : +1;
2092 auto primary_key = std::make_tuple(
2093 primary_node->
id(), primary_node, primary_node_neighbors[mnn]);
2094 auto secondary_val = std::make_pair(xi1, secondary_node_neighbors[snn]);
2103 rejected_primary_elem_candidates.insert(primary_elem_candidate);
2110 if (secondary_node_neighbors.size() == 1 && primary_node_neighbors.size() == 2)
2111 for (
auto it = primary_elems_mapped.begin(); it != primary_elems_mapped.end(); ++it)
2114 auto index = std::distance(primary_elems_mapped.begin(), it);
2117 primary_node->
id(), primary_node, primary_node_neighbors[index]),
2118 std::make_pair(1,
nullptr));
2124 for (MooseIndex(secondary_node_neighbors) nn = 0;
2125 nn < secondary_node_neighbors.size();
2128 const Elem * neigh = secondary_node_neighbors[nn];
2132 if (secondary_node == neigh_node)
2134 auto key = std::make_pair(neigh_node, neigh);
2135 auto val = std::make_pair(xi2, primary_elem_candidate);
2142 projection_succeeded =
true;
2147 rejected_primary_elem_candidates.insert(primary_elem_candidate);
2150 if (projection_succeeded)
2154 if (!projection_succeeded &&
_debug)
2155 _console <<
"Failed to find primary Elem into which secondary node " 2156 <<
static_cast<const Point &
>(*secondary_node) <<
" was projected." << std::endl
2175 SubdomainID lower_dimensional_primary_subdomain_id,
2176 SubdomainID lower_dimensional_secondary_subdomain_id)
2181 3, mesh_adaptor, nanoflann::KDTreeSingleIndexAdaptorParams(10));
2184 kd_tree.buildIndex();
2186 std::unordered_set<dof_id_type> primary_nodes_visited;
2188 for (
const auto & primary_side_elem :
_mesh.active_element_ptr_range())
2191 if (primary_side_elem->subdomain_id() != lower_dimensional_primary_subdomain_id)
2196 for (MooseIndex(primary_side_elem->n_vertices()) n = 0; n < primary_side_elem->n_vertices();
2200 const Node * primary_node = primary_side_elem->node_ptr(n);
2203 const std::vector<const Elem *> & primary_node_neighbors =
2211 std::make_tuple(primary_node->
id(), primary_node, primary_node_neighbors[0]);
2212 if (!primary_nodes_visited.insert(primary_node->
id()).second ||
2217 Real query_pt[3] = {(*primary_node)(0), (*primary_node)(1), (*primary_node)(2)};
2223 const size_t num_results = 3;
2226 std::vector<size_t> ret_index(num_results);
2227 std::vector<Real> out_dist_sqr(num_results);
2228 nanoflann::KNNResultSet<Real> result_set(num_results);
2229 result_set.init(&ret_index[0], &out_dist_sqr[0]);
2233 bool projection_succeeded =
false;
2238 std::set<const Elem *> rejected_secondary_elem_candidates;
2242 for (MooseIndex(result_set) r = 0; r < result_set.size(); ++r)
2246 out_dist_sqr[r]) <= TOLERANCE,
2247 "Lower-dimensional element squared distance verification failed.");
2251 const std::vector<const Elem *> & secondary_elem_candidates =
2255 for (MooseIndex(secondary_elem_candidates) e = 0; e < secondary_elem_candidates.size(); ++e)
2257 const Elem * secondary_elem_candidate = secondary_elem_candidates[e];
2260 if (rejected_secondary_elem_candidates.count(secondary_elem_candidate))
2263 std::vector<Point> nodal_normals(secondary_elem_candidate->
n_nodes());
2273 unsigned int current_iterate = 0, max_iterates = 10;
2284 for (MooseIndex(secondary_elem_candidate->
n_nodes()) n = 0;
2285 n < secondary_elem_candidate->
n_nodes();
2289 x1 += phi * secondary_elem_candidate->
point(n);
2290 normals += phi * nodal_normals[n];
2293 const auto u = x1 - (*primary_node);
2295 const auto F = u(0) * normals(1) - u(1) * normals(0);
2303 Real dxi1 = -F.value() / F.derivatives();
2308 }
while (++current_iterate < max_iterates);
2310 Real xi1 = xi1_dn.value();
2315 (
std::abs((primary_side_elem->point(0) - primary_side_elem->point(1)).unit() *
2327 throw MooseException(
"Nodes on primary and secondary surfaces are aligned. This is " 2328 "causing trouble when identifying projections from secondary " 2329 "nodes when performing primary node projections.");
2340 const Elem * neigh = primary_node_neighbors[0];
2344 if (primary_node == neigh_node)
2346 auto key = std::make_tuple(neigh_node->
id(), neigh_node, neigh);
2347 auto val = std::make_pair(xi1, secondary_elem_candidate);
2353 projection_succeeded =
true;
2359 rejected_secondary_elem_candidates.insert(secondary_elem_candidate);
2363 if (projection_succeeded)
2367 if (!projection_succeeded &&
_debug)
2369 _console <<
"Failed to find point from which primary node " 2370 <<
static_cast<const Point &
>(*primary_node) <<
" was projected." << std::endl
2377 std::vector<AutomaticMortarGeneration::MortarFilterIter>
2384 const auto & secondary_elems = secondary_it->second;
2385 std::vector<MortarFilterIter> ret;
2386 ret.reserve(secondary_elems.size());
2390 auto *
const secondary_elem = secondary_elems[i];
2396 mooseAssert(secondary_elem->active(),
2397 "We loop over active elements when building the mortar segment mesh, so we golly " 2398 "well hope this is active.");
2399 mooseAssert(!msm_it->second.empty(),
2400 "We should have removed all secondaries from this map if they do not have any " 2401 "mortar segments associated with them.");
2402 ret.push_back(msm_it);
virtual T maximum() const
std::set< SubdomainID > _primary_boundary_subdomain_ids
const Elem * getSecondaryLowerdElemFromSecondaryElem(dof_id_type secondary_elem_id) const
Return lower dimensional secondary element given its interior parent.
unique_id_type & set_unique_id()
std::unordered_set< const Elem * > _inactive_local_lm_elems
List of inactive lagrange multiplier nodes (for elemental variables)
auto norm() const -> decltype(std::norm(Real()))
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
unsigned int get_node_index(const Node *node_ptr) const
void computeInactiveLMNodes()
Get list of secondary nodes that don't contribute to interaction with any primary element...
std::vector< std::pair< BoundaryID, BoundaryID > > _primary_secondary_boundary_id_pairs
A list of primary/secondary boundary id pairs corresponding to each side of the mortar interface...
const Elem * interior_parent() const
unsigned int _t2x_var_num
void clear()
Clears the mortar segment mesh and accompanying data structures.
virtual void write_equation_systems(const std::string &, const EquationSystems &, const std::set< std::string > *system_names=nullptr)
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
std::set< SubdomainID > _secondary_boundary_subdomain_ids
The secondary/primary lower-dimensional boundary subdomain ids are the secondary/primary boundary ids...
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
std::unordered_map< dof_id_type, std::set< Elem *, CompareDofObjectsByID > > _secondary_elems_to_mortar_segments
We maintain a mapping from lower-dimensional secondary elements in the original mesh to (sets of) ele...
std::map< unsigned int, unsigned int > getPrimaryIpToLowerElementMap(const Elem &primary_elem, const Elem &primary_elem_ip, const Elem &lower_secondary_elem) const
Compute on-the-fly mapping from primary interior parent nodes to its corresponding lower dimensional ...
unsigned int _t2z_var_num
const bool _periodic
Whether this object will be generating a mortar segment mesh for periodic constraints.
void swap(std::vector< T > &data, const std::size_t idx0, const std::size_t idx1, const libMesh::Parallel::Communicator &comm)
Swap function for serial or distributed vector of data.
unsigned int which_side_am_i(const Elem *e) const
Real _newton_tolerance
Newton solve tolerance for node projections.
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
void output() override
Overload this function with the desired output activities.
MortarNodalGeometryOutput(const InputParameters ¶ms)
void push_back(const T &v)
std::unordered_map< const Elem *, MortarSegmentInfo > _msm_elem_to_info
Map between Elems in the mortar segment mesh and their info structs.
const bool _distributed
Whether the mortar segment mesh is distributed.
Base class for MOOSE-based applications.
void buildMortarSegmentMesh()
Builds the mortar segment mesh once the secondary and primary node projections have been completed...
const Parallel::Communicator & comm() const
void msmStatistics()
Outputs mesh statistics for mortar segment mesh.
unsigned int _nnx_var_num
void buildCouplingInformation()
build the _mortar_interface_coupling data
std::vector< Point > getNodalNormals(const Elem &secondary_elem) const
Special adaptor that works with subdomains of the Mesh.
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
static const Real invalid_xi
unsigned int _nny_var_num
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
FEProblemBase & feProblem() const
unsigned int _nnz_var_num
void projectSecondaryNodes()
Project secondary nodes (find xi^(2) values) to the closest points on the primary surface...
virtual EquationSystems & es()=0
const std::unordered_map< dof_id_type, std::set< Elem *, CompareDofObjectsByID > > & secondariesToMortarSegments() const
Utility class template for a semidynamic vector with a maximum size N and a chosen dynamic size...
This class is a container/interface for the objects involved in automatic generation of mortar spaces...
const bool _debug
Whether to print debug output.
ADRealEigenVector< T, D, asd > abs(const ADRealEigenVector< T, D, asd > &)
Based class for output objects.
const Elem * primary_elem
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
uint8_t processor_id_type
std::set< SubdomainID > _secondary_ip_sub_ids
All the secondary interior parent subdomain IDs associated with the mortar mesh.
processor_id_type n_processors() const
TypeVector< Real > unit() const
void libmesh_ignore(const Args &...)
void projectPrimaryNodes()
(Inverse) project primary nodes to the points on the secondary surface where they would have come fro...
unsigned int _t1z_var_num
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template cos(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(cos
const Node & node_ref(const unsigned int i) const
AutomaticMortarGeneration(MooseApp &app, MeshBase &mesh_in, const std::pair< BoundaryID, BoundaryID > &boundary_key, const std::pair< SubdomainID, SubdomainID > &subdomain_key, bool on_displaced, bool periodic, const bool debug, const bool correct_edge_dropping, const Real minimum_projection_angle)
Must be constructed with a reference to the Mesh we are generating mortar spaces for.
virtual unsigned int n_nodes() const=0
unsigned int _t2y_var_num
void buildNodeToElemMaps()
Once the secondary_requested_boundary_ids and primary_requested_boundary_ids containers have been fil...
std::unordered_map< dof_id_type, const Elem * > _secondary_element_to_secondary_lowerd_element
Map from full dimensional secondary element id to lower dimensional secondary element.
std::map< std::tuple< dof_id_type, const Node *, const Elem * >, std::pair< Real, const Elem * > > _primary_node_and_elem_to_xi1_secondary_elem
Same type of container, but for mapping (Primary Node ID, Primary Node, Primary Elem) -> (xi^(1)...
void projectPrimaryNodesSinglePair(SubdomainID lower_dimensional_primary_subdomain_id, SubdomainID lower_dimensional_secondary_subdomain_id)
Helper function used internally by AutomaticMortarGeneration::project_primary_nodes().
std::unordered_map< dof_id_type, std::vector< const Elem * > > _nodes_to_primary_elem_map
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
const bool _correct_edge_dropping
Flag to enable regressed treatment of edge dropping where all LM DoFs on edge dropping element are st...
An inteface for the _console for outputting to the Console object.
unsigned int _t1y_var_num
std::array< MooseUtils::SemidynamicVector< Point, 9 >, 2 > getNodalTangents(const Elem &secondary_elem) const
Compute the two nodal tangents, which are built on-the-fly.
std::unique_ptr< InputParameters > _output_params
Storage for the input parameters used by the mortar nodal geometry output.
std::set< BoundaryID > _primary_requested_boundary_ids
The boundary ids corresponding to all the primary surfaces.
std::map< unsigned int, unsigned int > getSecondaryIpToLowerElementMap(const Elem &lower_secondary_elem) const
Compute on-the-fly mapping from secondary interior parent nodes to lower dimensional nodes...
TypeVector< typename CompareTypes< Real, T2 >::supertype > cross(const TypeVector< T2 > &v) const
nanoflann::KDTreeSingleIndexAdaptor< subdomain_adatper_t, NanoflannMeshSubdomainAdaptor< 3 >, 3 > subdomain_kd_tree_t
void computeNodalGeometry()
Computes and stores the nodal normal/tangent vectors in a local data structure instead of using the E...
Real _xi_tolerance
Tolerance for checking projection xi values.
static InputParameters validParams()
void computeIncorrectEdgeDroppingInactiveLMNodes()
Computes inactive secondary nodes when incorrect edge dropping behavior is enabled (any node touching...
void buildMortarSegmentMesh3d()
Builds the mortar segment mesh once the secondary and primary node projections have been completed...
const Elem * secondary_elem
void projectSecondaryNodesSinglePair(SubdomainID lower_dimensional_primary_subdomain_id, SubdomainID lower_dimensional_secondary_subdomain_id)
Helper function responsible for projecting secondary nodes onto primary elements for a single primary...
Provides a way for users to bail out of the current solve.
virtual unsigned int n_vertices() const=0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::unordered_map< std::pair< const Node *, const Elem * >, std::pair< Real, const Elem * > > _secondary_node_and_elem_to_xi2_primary_elem
Similar to the map above, but associates a (Secondary Node, Secondary Elem) pair to a (xi^(2)...
Holds xi^(1), xi^(2), and other data for a given mortar segment.
Generic class for solving transient nonlinear problems.
virtual SimpleRange< element_iterator > active_local_subdomain_elements_ptr_range(subdomain_id_type sid)=0
subdomain_id_type subdomain_id() const
const bool _on_displaced
Whether this object is on the displaced mesh.
const Node * node_ptr(const unsigned int i) const
virtual void write(const std::string &fname) override
void initOutput()
initialize mortar-mesh based output
void addOutput(std::shared_ptr< Output > output)
Adds an existing output object to the warehouse.
virtual Real volume() const
std::unordered_map< const Node *, std::array< Point, 2 > > _secondary_node_to_hh_nodal_tangents
Container for storing the nodal tangent/binormal vectors associated with each secondary node (Househo...
IntRange< T > make_range(T beg, T end)
unsigned int _t1x_var_num
unsigned int mesh_dimension() const
unsigned int level ElemType type std::set< subdomain_id_type > ss processor_id_type pid unsigned int level std::set< subdomain_id_type > virtual ss SimpleRange< element_iterator > active_subdomain_elements_ptr_range(subdomain_id_type sid)=0
virtual T minimum() const
T fe_lagrange_1D_shape(const Order order, const unsigned int i, const T &xi)
const Real _minimum_projection_angle
Parameter to control which angle (in degrees) is admissible for the creation of mortar segments...
MeshBase & _mesh
Reference to the mesh stored in equation_systems.
virtual const Point & point(const dof_id_type i) const=0
std::vector< std::pair< SubdomainID, SubdomainID > > _primary_secondary_subdomain_id_pairs
A list of primary/secondary subdomain id pairs corresponding to each side of the mortar interface...
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
SimpleRange< NeighborPtrIter > neighbor_ptr_range()
virtual unsigned int n_sub_elem() const=0
std::set< BoundaryID > _secondary_requested_boundary_ids
The boundary ids corresponding to all the secondary surfaces.
std::unordered_map< const Node *, Point > _secondary_node_to_nodal_normal
Container for storing the nodal normal vector associated with each secondary node.
virtual const Node * node_ptr(const dof_id_type i) const=0
std::unordered_set< const Node * > _inactive_local_lm_nodes
processor_id_type processor_id() const
virtual Order default_order() const=0
std::unordered_map< dof_id_type, std::unordered_set< dof_id_type > > _mortar_interface_coupling
Used by the AugmentSparsityOnInterface functor to determine whether a given Elem is coupled to any ot...
std::set< SubdomainID > _primary_ip_sub_ids
All the primary interior parent subdomain IDs associated with the mortar mesh.
SearchParams SearchParameters
AutomaticMortarGeneration & _amg
The mortar generation object that we will query for nodal normal and tangent information.
void set_hdf5_writing(bool write_hdf5)
processor_id_type processor_id() const
void householderOrthogolization(const Point &normal, Point &tangent_one, Point &tangent_two) const
Householder orthogonalization procedure to obtain proper basis for tangent and binormal vectors...
virtual ElemType type() const=0
dof_id_type node_id(const unsigned int i) const
std::vector< Point > getNormals(const Elem &secondary_elem, const std::vector< Point > &xi1_pts) const
Compute the normals at given reference points on a secondary element.
static InputParameters validParams()
MooseApp & _app
The Moose app.
const Point & point(const unsigned int i) const
T fe_lagrange_2D_shape(const ElemType type, const Order order, const unsigned int i, const VectorType< T > &p)
std::unique_ptr< MeshBase > _mortar_segment_mesh
1D Mesh of mortar segment elements which gets built by the call to build_mortar_segment_mesh().
auto index_range(const T &sizable)
void set_extra_integer(const unsigned int index, const dof_id_type value)
OutputWarehouse & getOutputWarehouse()
Get the OutputWarehouse objects.
std::unordered_map< dof_id_type, std::vector< const Elem * > > _nodes_to_secondary_elem_map
Map from nodes to connected lower-dimensional elements on the secondary/primary subdomains.
std::unordered_map< const Elem *, unsigned int > _lower_elem_to_side_id
Keeps track of the mapping between lower-dimensional elements and the side_id of the interior_parent ...
void computeInactiveLMElems()
Get list of secondary elems without any corresponding primary elements.