13 #include "libmesh/elem.h" 14 #include "libmesh/boundary_info.h" 15 #include "libmesh/id_types.h" 16 #include "libmesh/int_range.h" 17 #include "libmesh/parallel.h" 18 #include "libmesh/parallel_algebra.h" 19 #include "libmesh/utility.h" 21 #include "libmesh/distributed_mesh.h" 22 #include "libmesh/parallel_elem.h" 23 #include "libmesh/parallel_node.h" 24 #include "libmesh/compare_elems_by_level.h" 25 #include "libmesh/mesh_communication.h" 26 #include "libmesh/edge_edge3.h" 27 #include "libmesh/enum_to_string.h" 28 #include "libmesh/unstructured_mesh.h" 44 std::map<boundary_id_type, boundary_id_type> same_name_ids;
46 auto populate_map = [](
const std::map<boundary_id_type, std::string> & map,
47 std::map<boundary_id_type, boundary_id_type> & same_ids)
49 for (
const auto & pair_outer : map)
50 for (
const auto & pair_inner : map)
52 if (pair_outer.second == pair_inner.second && pair_outer.first != pair_inner.first &&
53 same_ids.find(pair_inner.first) == same_ids.end())
54 same_ids[pair_outer.first] = pair_inner.first;
57 populate_map(side_bd_name_map, same_name_ids);
58 populate_map(node_bd_name_map, same_name_ids);
60 for (
const auto & [id1, id2] : same_name_ids)
74 std::vector<boundary_id_type> old_ids;
77 for (
auto & elem :
as_range(
mesh.level_elements_begin(0),
mesh.level_elements_end(0)))
79 unsigned int n_sides = elem->n_sides();
83 if (
std::find(old_ids.begin(), old_ids.end(), old_id) != old_ids.end())
85 std::vector<boundary_id_type> new_ids(old_ids);
86 std::replace(new_ids.begin(), new_ids.end(), old_id, new_id);
90 boundary_info.
add_side(elem, s, new_ids);
93 boundary_info.
add_side(elem, s, new_ids);
108 std::vector<boundary_id_type>
110 const std::vector<BoundaryName> & boundary_name,
111 bool generate_unknown)
117 std::vector<boundary_id_type>
119 const std::vector<BoundaryName> & boundary_name,
120 bool generate_unknown,
121 const std::set<BoundaryID> & mesh_boundary_ids)
133 if (generate_unknown)
137 max_boundary_local_id = bids.empty() ? 0 : *(bids.rbegin());
143 BoundaryID max_boundary_id = mesh_boundary_ids.empty() ? 0 : *(mesh_boundary_ids.rbegin());
146 max_boundary_id > max_boundary_local_id ? max_boundary_id : max_boundary_local_id;
148 std::vector<BoundaryID> ids(boundary_name.size());
151 if (boundary_name[i] ==
"ANY_BOUNDARY_ID")
153 ids.assign(mesh_boundary_ids.begin(), mesh_boundary_ids.end());
155 mooseWarning(
"You passed \"ANY_BOUNDARY_ID\" in addition to other boundary_names. This " 156 "may be a logic error.");
160 if (boundary_name[i].empty() && !generate_unknown)
161 mooseError(
"Incoming boundary name is empty and we are not generating unknown boundary IDs. " 166 if (boundary_name[i].empty() || !MooseUtils::isDigits(boundary_name[i]))
173 if (generate_unknown &&
174 !MooseUtils::doesMapContainValue(sideset_map, std::string(boundary_name[i])) &&
175 !MooseUtils::doesMapContainValue(nodeset_map, std::string(boundary_name[i])))
176 id = ++max_boundary_id;
181 id = getIDFromName<BoundaryName, BoundaryID>(boundary_name[i]);
191 const std::vector<BoundaryName> & boundary_name,
192 bool generate_unknown)
195 return std::set<BoundaryID>(boundaries.begin(), boundaries.end());
198 std::vector<subdomain_id_type>
201 std::vector<subdomain_id_type> ids;
204 if (subdomain_names.size() == 1 && subdomain_names[0] ==
"ANY_BLOCK_ID")
208 "getSubdomainIDs() should only be called on a prepared mesh if ANY_BLOCK_ID is " 209 "used to query all block IDs");
215 ids.resize(subdomain_names.size());
218 if (subdomain_names[i] ==
"ANY_BLOCK_ID")
219 mooseError(
"getSubdomainIDs() accepts \"ANY_BLOCK_ID\" if and only if it is the only " 220 "subdomain name being queried.");
227 std::set<subdomain_id_type>
231 mesh, std::vector<SubdomainName>(subdomain_names.begin(), subdomain_names.end()));
232 return {blk_ids.begin(), blk_ids.end()};
239 if (boundary_name.empty())
242 if (!MooseUtils::isDigits(boundary_name))
245 id = getIDFromName<BoundaryName, BoundaryID>(boundary_name);
253 if (subdomain_name ==
"ANY_BLOCK_ID")
254 mooseError(
"getSubdomainID() does not work with \"ANY_BLOCK_ID\"");
257 if (subdomain_name.empty())
260 if (!MooseUtils::isDigits(subdomain_name))
263 id = getIDFromName<SubdomainName, SubdomainID>(subdomain_name);
271 for (
const auto & elem :
mesh.element_ptr_range())
272 if (elem->subdomain_id() == old_id)
273 elem->subdomain_id() = new_id;
284 for (
const auto & elem :
mesh.active_local_element_ptr_range())
286 Real elem_vol = elem->volume();
287 centroid_pt += (elem->true_centroid()) * elem_vol;
292 centroid_pt /= vol_tmp;
308 Point volume_weighted_centroid_sum(0, 0, 0);
310 for (
const auto & [eid, side_i, bid] : side_list)
312 if (bid != boundary_id)
317 const auto side = elem->
side_ptr(side_i);
319 volume_sum += side->volume();
320 volume_weighted_centroid_sum += side->volume() * side->true_centroid();
326 return volume_weighted_centroid_sum / volume_sum;
343 for (
const auto & [eid, side_i, bid] : side_list)
345 if (bid != boundary_id)
350 const auto side = elem->
side_ptr(side_i);
352 volume_sum += side->volume();
353 volume_weighted_normal_sum += side->volume() * elem->side_vertex_average_normal(side_i);
359 return volume_weighted_normal_sum / volume_sum;
364 const Point & origin,
368 mooseAssert(MooseUtils::absoluteFuzzyEqual(direction.
norm_sq(), 1),
369 "Direction should be normalized");
370 for (
const auto & node :
mesh.node_ptr_range())
371 if (
const auto dist_node = (*node - origin).cross(direction).norm(); dist_node >
distance)
377 std::unordered_map<dof_id_type, dof_id_type>
379 const std::set<SubdomainID> & block_ids,
380 std::vector<ExtraElementIDName> extra_ids)
383 const bool block_restricted = !block_ids.empty();
385 ExtraElementIDName id_name = extra_ids.back();
386 extra_ids.pop_back();
390 if (extra_ids.empty())
393 std::vector<dof_id_type> ids;
395 std::set<dof_id_type> ids_set;
396 for (
const auto & elem :
mesh.active_element_ptr_range())
398 if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
400 const auto id = elem->get_extra_integer(id_index);
404 ids.assign(ids_set.begin(), ids_set.end());
408 std::unordered_map<dof_id_type, dof_id_type> parsed_ids;
409 for (
auto & elem :
mesh.active_element_ptr_range())
411 if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
413 parsed_ids[elem->id()] = std::distance(
414 ids.begin(), std::lower_bound(ids.begin(), ids.end(), elem->get_extra_integer(id_index)));
420 const auto base_parsed_ids =
423 std::vector<std::pair<dof_id_type, dof_id_type>> unique_ids;
425 std::set<std::pair<dof_id_type, dof_id_type>> unique_ids_set;
426 for (
const auto & elem :
mesh.active_element_ptr_range())
428 if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
430 const dof_id_type id1 = libmesh_map_find(base_parsed_ids, elem->id());
431 const dof_id_type id2 = elem->get_extra_integer(id_index);
432 const std::pair<dof_id_type, dof_id_type> ids = std::make_pair(id1, id2);
433 unique_ids_set.insert(ids);
436 unique_ids.assign(unique_ids_set.begin(), unique_ids_set.end());
439 std::unordered_map<dof_id_type, dof_id_type> parsed_ids;
441 for (
const auto & elem :
mesh.active_element_ptr_range())
443 if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
445 const dof_id_type id1 = libmesh_map_find(base_parsed_ids, elem->id());
446 const dof_id_type id2 = elem->get_extra_integer(id_index);
449 std::lower_bound(unique_ids.begin(), unique_ids.end(), std::make_pair(id1, id2)));
450 parsed_ids[elem->id()] = new_id;
459 for (
const auto & pt : vec_pts)
460 if (!MooseUtils::absoluteFuzzyEqual((pt - fixed_pt) * plane_nvec, 0.0))
468 return isCoPlanar(vec_pts, plane_nvec, vec_pts.front());
476 std::vector<Point> vec_pts_nonzero{vec_pts[0]};
478 if (!MooseUtils::absoluteFuzzyEqual((vec_pts[i] - vec_pts[0]).
norm(), 0.0))
479 vec_pts_nonzero.push_back(vec_pts[i]);
481 if (vec_pts_nonzero.size() <= 3)
485 for (
const auto i :
make_range(vec_pts_nonzero.size() - 1))
487 const Point tmp_pt = (vec_pts_nonzero[i] - vec_pts_nonzero[0])
488 .cross(vec_pts_nonzero[i + 1] - vec_pts_nonzero[0]);
490 if (!MooseUtils::absoluteFuzzyEqual(tmp_pt.
norm(), 0.0))
504 std::set<SubdomainID> preexisting_subdomain_ids;
506 if (preexisting_subdomain_ids.empty())
510 const auto highest_subdomain_id =
511 *std::max_element(preexisting_subdomain_ids.begin(), preexisting_subdomain_ids.end());
513 "A SubdomainID with max possible value was found");
514 return highest_subdomain_id + 1;
525 if (boundary_ids.empty())
527 return (*boundary_ids.rbegin() + 1);
533 std::set<SubdomainID> mesh_blocks;
574 std::vector<dof_id_type> & elem_id_list,
575 std::vector<dof_id_type> & midpoint_node_list,
576 std::vector<dof_id_type> & ordered_node_list,
577 std::vector<dof_id_type> & ordered_elem_id_list)
580 bool is_flipped =
false;
582 mooseAssert(node_assm.size(),
"Node list must not be empty");
583 ordered_node_list.push_back(node_assm.front().first);
585 ordered_node_list.push_back(midpoint_node_list.front());
586 ordered_node_list.push_back(node_assm.front().second);
587 ordered_elem_id_list.push_back(elem_id_list.front());
589 node_assm.erase(node_assm.begin());
590 midpoint_node_list.erase(midpoint_node_list.begin());
591 elem_id_list.erase(elem_id_list.begin());
592 const unsigned int node_assm_size_0 = node_assm.size();
593 for (
unsigned int i = 0; i < node_assm_size_0; i++)
596 dof_id_type end_node_id = ordered_node_list.back();
597 auto isMatch1 = [end_node_id](std::pair<dof_id_type, dof_id_type> old_id_pair)
598 {
return old_id_pair.first == end_node_id; };
599 auto isMatch2 = [end_node_id](std::pair<dof_id_type, dof_id_type> old_id_pair)
600 {
return old_id_pair.second == end_node_id; };
601 auto result = std::find_if(node_assm.begin(), node_assm.end(), isMatch1);
603 if (result == node_assm.end())
606 result = std::find_if(node_assm.begin(), node_assm.end(), isMatch2);
613 if (result != node_assm.end())
615 const auto elem_index = std::distance(node_assm.begin(), result);
617 ordered_node_list.push_back(midpoint_node_list[elem_index]);
618 ordered_node_list.push_back(match_first ? (*result).second : (*result).first);
619 node_assm.erase(result);
620 midpoint_node_list.erase(midpoint_node_list.begin() + elem_index);
621 ordered_elem_id_list.push_back(elem_id_list[elem_index]);
622 elem_id_list.erase(elem_id_list.begin() + elem_index);
632 throw MooseException(
"The node list provided has more than one segments.");
636 std::reverse(ordered_node_list.begin(), ordered_node_list.end());
637 std::reverse(midpoint_node_list.begin(), midpoint_node_list.end());
638 std::reverse(ordered_elem_id_list.begin(), ordered_elem_id_list.end());
647 std::vector<dof_id_type> & elem_id_list,
648 std::vector<dof_id_type> & ordered_node_list,
649 std::vector<dof_id_type> & ordered_elem_id_list)
653 node_assm, elem_id_list, dummy_midpoint_node_list, ordered_node_list, ordered_elem_id_list);
666 const std::string & class_name,
667 const unsigned int num_sections,
668 const unsigned int num_integers,
669 const std::vector<std::vector<std::vector<dof_id_type>>> & elem_integers_swaps,
670 std::vector<std::unordered_map<dof_id_type, dof_id_type>> & elem_integers_swap_pairs)
672 elem_integers_swap_pairs.reserve(num_sections * num_integers);
675 const auto & elem_integer_swaps = elem_integers_swaps[i];
676 std::vector<std::unordered_map<dof_id_type, dof_id_type>> elem_integer_swap_pairs;
680 "elem_integers_swaps",
682 elem_integer_swap_pairs,
690 elem_integers_swap_pairs.insert(elem_integers_swap_pairs.end(),
691 elem_integer_swap_pairs.begin(),
692 elem_integer_swap_pairs.end());
696 std::unique_ptr<ReplicatedMesh>
700 ::
mooseError(
"Input mesh should be serialized for extracting the boundary mesh.\nInput mesh:" +
702 auto poly_mesh = std::make_unique<ReplicatedMesh>(input_mesh.
comm());
706 std::unordered_map<dof_id_type, dof_id_type> old_new_node_map;
707 for (
const auto & [eid, side_i, bid] : side_list)
709 if (bid != boundary_id)
713 const auto elem = input_mesh.
elem_ptr(eid);
714 const auto side = elem->
side_ptr(side_i);
715 auto side_elem = elem->build_side_ptr(side_i);
716 auto copy = side_elem->build(side_elem->type());
718 for (
const auto i : side_elem->node_index_range())
720 auto & n = side_elem->node_ref(i);
722 if (old_new_node_map.count(n.id()))
723 copy->set_node(i, poly_mesh->node_ptr(old_new_node_map[n.id()]));
726 Node * node = poly_mesh->add_point(side_elem->point(i));
727 copy->set_node(i, node);
728 old_new_node_map[n.id()] = node->
id();
731 poly_mesh->add_elem(copy.release());
733 poly_mesh->skip_partitioning(
true);
734 poly_mesh->prepare_for_use();
735 if (poly_mesh->n_elem() == 0)
736 mooseError(
"The input mesh to extract the boundary from does not have a boundary with id ",
744 std::unique_ptr<ReplicatedMesh>
749 "Input 2D mesh should be serialized for extracting the loop boundary mesh.\nInput mesh:" +
751 auto edge_mesh = std::make_unique<ReplicatedMesh>(input_mesh.
comm());
753 std::set<BoundaryInfo::BCTuple> visited;
754 bool already_seen_this_side_tuple =
false;
763 for (
const auto & bside : side_list)
765 if (std::get<2>(bside) != boundary_id)
769 if (bside != first_side_visited)
771 if (visited.size() && !visited.count(bside))
773 "Boundary " + std::to_string(boundary_id) +
775 ") was not visited after a single pass around the boundary. Boundary sides visited: " +
777 else if (visited.empty())
778 first_side_visited = bside;
785 const Elem * elem = input_mesh.
elem_ptr(std::get<0>(bside));
786 auto current_side = std::get<1>(bside);
790 if (elem->
dim() != 2)
792 "Finding the loop boundary of a 2D mesh cannot be done with non-2D elements such as ",
797 bool looped_back =
false;
798 const Node * starting_node = side_elem->node_ptr(0);
799 const auto new_mesh_starting_node = edge_mesh->add_point(side_elem->point(0));
800 Node * new_first_node = new_mesh_starting_node;
801 [[maybe_unused]]
dof_id_type first_node_index = starting_node->
id();
804 while (!looped_back && !already_seen_this_side_tuple)
806 if (MooseUtils::absoluteFuzzyEqual(input_mesh.
point(second_node_index),
807 Point(*starting_node)))
811 Node * new_second_node = looped_back
812 ? new_mesh_starting_node
813 : edge_mesh->add_point(input_mesh.
point(second_node_index));
817 auto copy = side_elem->build(side_elem->type());
818 copy->set_node(0, new_first_node);
819 copy->set_node(1, new_second_node);
820 edge_mesh->add_elem(copy.release());
823 std::tuple<dof_id_type, unsigned short int, boundary_id_type> bc_tuple = {
824 elem->
id(), current_side, boundary_id};
825 const auto & visit_iter = visited.insert(bc_tuple);
826 if (!looped_back && !visit_iter.second)
827 already_seen_this_side_tuple =
true;
830 auto & connected_elems = libmesh_map_find(node_to_elem_map, second_node_index);
831 bool found_match =
false;
832 const auto current_eid = elem->
id();
834 for (
const auto eid : connected_elems)
836 mooseAssert(!found_match,
837 "We should only find one node on a connected element on this boundary");
838 if (eid != current_eid)
847 const auto local_second_node_index =
854 std::tuple<dof_id_type, unsigned short int, boundary_id_type> side_bc_tuple = {
855 elem->
id(), si, boundary_id};
857 if (
std::find(side_list.begin(), side_list.end(), side_bc_tuple) == side_list.end())
861 for (
const auto local_side_node_id : elem->
nodes_on_side(si))
863 const auto side_node_id = elem->
node_id(local_side_node_id);
866 if (side_node_id == second_node_index)
868 mooseAssert(side_node_id != first_node_index,
869 "Somehow looped back in a single element");
872 second_node_index = side_node_id;
887 else if (connected_elems.size() == 1)
890 const auto local_second_node_index =
895 if (si != current_side && elem->
is_node_on_side(local_second_node_index, si))
898 for (
const auto local_side_node_id : elem->
nodes_on_side(si))
900 const auto side_node_id = elem->
node_id(local_side_node_id);
902 if (side_node_id == second_node_index)
904 mooseAssert((side_node_id != first_node_index) ||
905 (side_list.size() == elem->
n_sides()),
906 "Somehow looped back in a single element");
909 const auto bids_range = node_to_bids.equal_range(input_mesh.
node_ptr(side_node_id));
911 for (
auto iter = bids_range.first; iter != bids_range.second; iter++)
912 if (iter->second == boundary_id)
915 second_node_index = side_node_id;
929 new_first_node = new_second_node;
930 first_node_index = second_node_index;
935 mooseWarning(
"Search for next element in loop boundary failed. Is boundary '" +
936 std::to_string(boundary_id) +
"' of mesh ",
938 " a loop boundary?");
944 if (already_seen_this_side_tuple)
945 mooseWarning(
"Boundary " + std::to_string(boundary_id) +
946 " seems to have cycles. A single-cycle loop should be used");
948 edge_mesh->skip_partitioning(
true);
949 edge_mesh->prepare_for_use();
950 if (edge_mesh->n_elem() == 0)
951 mooseError(
"The input mesh to extract the boundary from does not have a boundary with id ",
959 std::unordered_map<dof_id_type, std::unordered_set<dof_id_type>>
964 "Input 2D mesh should be serialized for extracting the loop boundary mesh.\nInput mesh:" +
971 std::unordered_map<dof_id_type, std::unordered_set<dof_id_type>> nid_to_eids_map;
973 for (
const auto & elem :
974 as_range(input_mesh.active_elements_begin(), input_mesh.active_elements_end()))
976 for (
const auto & nd : elem->node_ref_range())
979 if (!particular_node_ids.count(nd.id()))
982 auto & elem_ids = nid_to_eids_map[nd.id()];
983 elem_ids.insert(elem->id());
986 return nid_to_eids_map;
989 std::set<dof_id_type>
992 std::set<dof_id_type> boundary_node_ids;
996 const auto & bc_sides =
998 for (
const auto & [elem_id, side, bc_id] : bc_sides)
1000 if (bc_id == boundary_id)
1003 for (
const auto ni : elem->nodes_on_side(side))
1004 boundary_node_ids.insert(elem->node_id(ni));
1010 for (
const auto & [n_id, bc_id] : bc_nodes)
1011 if (bc_id == boundary_id)
1012 boundary_node_ids.insert(n_id);
1014 return boundary_node_ids;
1019 std::vector<BoundaryName> boundary_names,
1021 const SubdomainName new_subdomain_name,
1022 const std::string type_name)
1039 for (
const auto &
sideset : boundary_names)
1041 mooseException(
"The sideset '",
sideset,
"' was not found within the mesh");
1044 std::set<boundary_id_type> sidesets(sideset_ids.begin(), sideset_ids.end());
1048 std::vector<Elem *> elements_to_send;
1049 unsigned short i_need_boundary_elems = 0;
1050 for (
const auto & [elem_id, side, bc_id] : side_list)
1053 if (sidesets.count(bc_id))
1057 i_need_boundary_elems = 1;
1060 elements_to_send.push_back(elem);
1064 std::set<const Elem *, libMesh::CompareElemIdsByLevel> connected_elements(
1065 elements_to_send.begin(), elements_to_send.end());
1066 std::set<const Node *> connected_nodes;
1068 std::set<dof_id_type> connected_node_ids;
1069 for (
auto * nd : connected_nodes)
1070 connected_node_ids.insert(nd->id());
1072 std::vector<unsigned short> need_boundary_elems(
mesh.
comm().
size());
1074 std::unordered_map<processor_id_type, decltype(elements_to_send)> push_element_data;
1075 std::unordered_map<processor_id_type, decltype(connected_nodes)> push_node_data;
1081 if (elements_to_send.size())
1082 push_element_data[pid] = elements_to_send;
1083 if (connected_nodes.size())
1084 push_node_data[pid] = connected_nodes;
1091 Parallel::push_parallel_packed_range(
mesh.
comm(), push_node_data, &
mesh, node_action_functor);
1102 std::vector<std::pair<dof_id_type, ElemSidePair>> element_sides_on_boundary;
1104 for (
const auto & [eid, side, bid] : side_list)
1105 if (sidesets.count(bid))
1109 if (!elem->active())
1111 "Only active, level 0 elements can be made interior parents of new level 0 lower-d " 1112 "elements. Make sure that ",
1114 "s are run before any refinement generators");
1115 element_sides_on_boundary.push_back(std::make_pair(counter,
ElemSidePair(elem, side)));
1126 for (
auto & [i, elem_side] : element_sides_on_boundary)
1128 Elem * elem = elem_side.elem;
1130 const auto side = elem_side.side;
1139 side_elem->subdomain_id() = new_block_id;
1143 side_elem->set_interior_parent(elem);
1146 side_elem->set_id(max_elem_id + i);
1147 side_elem->set_unique_id(max_unique_id + i);
1154 if (new_subdomain_name.size())
1166 const std::vector<SubdomainName> & target_blocks)
1169 mooseError(
"This generator does not support distributed meshes.");
1174 std::set<SubdomainID> mesh_blocks;
1177 for (
const auto i :
index_range(target_block_ids))
1180 mooseException(
"The target_block '", target_blocks[i],
"' was not found within the mesh.");
1184 std::unordered_map<dof_id_type, dof_id_type> old_new_node_map;
1186 for (
const auto target_block_id : target_block_ids)
1191 if (elem->level() != 0)
1192 mooseError(
"Refined blocks are not supported by this generator. " 1193 "Can you re-organize mesh generators to refine after converting the block?");
1196 auto copy = elem->build(elem->type());
1199 copy->subdomain_id() = elem->subdomain_id();
1208 auto & n = elem->node_ref(i);
1210 if (old_new_node_map.count(n.id()))
1215 copy->set_node(copy_n_index++, target_mesh.
node_ptr(old_new_node_map[n.id()]));
1228 copy->set_node(copy_n_index++, node);
1231 old_new_node_map[n.id()] = node->
id();
1237 target_mesh.
add_elem(copy.release());
1242 for (
const auto sbd_id : target_block_ids)
1250 const bool avoid_merging_subdomains,
1251 const bool avoid_merging_boundaries,
1258 #ifdef LIBMESH_ENABLE_UNIQUE_ID 1265 std::unordered_map<subdomain_id_type, subdomain_id_type> id_remapping;
1266 unsigned int block_offset = 0;
1267 if (avoid_merging_subdomains)
1270 std::set<subdomain_id_type> source_ids;
1271 std::set<subdomain_id_type> dest_ids;
1283 mooseAssert(source_ids.size(),
"Should have a subdomain");
1284 mooseAssert(dest_ids.size(),
"Should have a subdomain");
1285 unsigned int max_dest_bid = *dest_ids.rbegin();
1286 unsigned int min_source_bid = *source_ids.begin();
1289 block_offset = 1 + max_dest_bid - min_source_bid;
1290 for (
const auto bid : source_ids)
1291 id_remapping[bid] = block_offset + bid;
1303 avoid_merging_subdomains ? &id_remapping :
nullptr);
1309 unsigned int bid_offset = 0;
1310 if (avoid_merging_boundaries)
1314 unsigned int max_dest_bid = boundary_ids.size() ? *boundary_ids.rbegin() : 0;
1315 unsigned int min_source_bid = other_boundary_ids.size() ? *other_boundary_ids.begin() : 0;
1318 bid_offset = 1 + max_dest_bid - min_source_bid;
1327 boundary.
add_node(std::get<0>(t) + node_delta, bid_offset + std::get<1>(t));
1330 boundary.
add_side(std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t));
1333 boundary.
add_edge(std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t));
1337 std::get<0>(t) + elem_delta, std::get<1>(t), bid_offset + std::get<2>(t));
1340 if (avoid_merging_subdomains)
1343 "Missing parameter in the mesh generator calling this function: " 1344 "avoid_merging_subdomains. Considering setting avoid_merging_subdomains to true.");
1347 if (block_name == source_name)
1349 "avoid_merging_subdomains",
1350 "Not merging subdomains is creating two subdomains with the same name '" +
1351 block_name +
"' but different ids: " + std::to_string(source_id) +
" & " +
1352 std::to_string(block_id + block_offset) +
1353 ".\n We recommend using a RenameBlockGenerator to prevent this as you " 1354 "will get errors reading the Exodus output later.");
1359 std::make_pair<SubdomainID, SubdomainName>(block_id + block_offset, block_name));
1362 if (avoid_merging_boundaries)
1365 "Missing parameter in the mesh generator calling this function: " 1366 "avoid_merging_boundaries. Considering setting avoid_merging_boundaries to true.");
1369 if (b_name == source_name)
1371 "avoid_merging_boundaries",
1372 "Not merging boundaries is creating two sidesets with the same name '" + b_name +
1373 "' but different ids: " + std::to_string(source_id) +
" & " +
1374 std::to_string(b_id + bid_offset) +
1375 ".\n We recommend using a RenameBoundaryGenerator to prevent this as you " 1376 "will get errors reading the Exodus output later.");
1379 if (b_name == source_name)
1381 "avoid_merging_boundaries",
1382 "Not merging boundaries is creating two nodesets with the same name '" + b_name +
1383 "' but different ids: " + std::to_string(source_id) +
" & " +
1384 std::to_string(b_id + bid_offset) +
1385 ".\n We recommend using a RenameBoundaryGenerator to prevent this as you " 1386 "will get errors reading the Exodus output later.");
1391 std::make_pair<BoundaryID, BoundaryName>(nodeset_id + bid_offset, nodeset_name));
1395 std::make_pair<BoundaryID, BoundaryName>(sideset_id + bid_offset, sideset_name));
1399 std::make_pair<BoundaryID, BoundaryName>(edgeset_id + bid_offset, edgeset_name));
1404 const std::vector<Point> & points,
1405 const std::vector<Point> & mid_points,
1407 const BoundaryName & start_boundary,
1408 const BoundaryName & end_boundary,
1409 const std::vector<unsigned int> & nums_edges_between_points)
1411 mooseAssert(nums_edges_between_points.size() == 1 ||
1412 nums_edges_between_points.size() == points.size() - 1 + loop,
1413 "nums_edges_between_points must be either a single value or have the same number of " 1414 "entries as segments defined by the points.");
1416 mid_points.size() == 0 || mid_points.size() == points.size() - (loop ? 0 : 1),
1417 "mid_points must be either empty or have the consistent number of entries as points.");
1419 mid_points.size() == 0 ||
1420 (nums_edges_between_points.size() == 1 && nums_edges_between_points.front() == 1) ||
1421 (nums_edges_between_points.size() == points.size() - 1 + loop &&
1422 std::all_of(nums_edges_between_points.begin(),
1423 nums_edges_between_points.end(),
1424 [](
unsigned int n) {
return n == 1; })),
1425 "mid_points can only be provided if each segment has exactly one edge.");
1427 const auto n_points = points.size();
1430 const auto & num_edges_between_points =
1431 (nums_edges_between_points.size() == 1)
1432 ? nums_edges_between_points[0]
1433 : (i == nums_edges_between_points.size() ? 0 : nums_edges_between_points[i]);
1435 Point p = points[i];
1436 const auto pt_counter = (nums_edges_between_points.size() == 1)
1438 : std::accumulate(nums_edges_between_points.begin(),
1439 nums_edges_between_points.begin() + i,
1442 p, nums_edges_between_points.size() == 1 ? (i * num_edges_between_points) : pt_counter);
1444 if (num_edges_between_points > 1)
1446 if (!loop && (i + 1) == n_points)
1449 const auto ip1 = (i + 1) % n_points;
1450 const Point pvec = (points[ip1] - p) / num_edges_between_points;
1452 for (
auto j :
make_range(1u, num_edges_between_points))
1457 (nums_edges_between_points.size() == 1 ? (i * num_edges_between_points) : pt_counter) +
1464 for (
const auto & i :
make_range(mid_points.size()))
1467 const auto n_segments = loop ? n_points : (n_points - 1);
1469 nums_edges_between_points.size() == 1
1470 ? n_segments * nums_edges_between_points[0]
1471 : std::accumulate(nums_edges_between_points.begin(), nums_edges_between_points.end(), 0);
1472 const auto max_nodes =
1473 (nums_edges_between_points.size() == 1 ? n_segments * nums_edges_between_points[0]
1474 : std::accumulate(nums_edges_between_points.begin(),
1475 nums_edges_between_points.end(),
1480 std::unique_ptr<Elem> elem;
1481 if (mid_points.size())
1483 elem = std::make_unique<Edge3>();
1488 const auto ip1 = (i + 1) % max_nodes;
1498 std::vector<BoundaryName> bdy_names{start_boundary, end_boundary};
1504 mooseAssert(start_boundary.empty() && end_boundary.empty(),
1505 "Cannot assign start/end boundaries on a looped polyline.");
1512 const std::vector<Point> & points,
1514 const BoundaryName & start_boundary,
1515 const BoundaryName & end_boundary,
1516 const std::vector<unsigned int> & nums_edges_between_points)
1519 mesh, points, {}, loop, start_boundary, end_boundary, nums_edges_between_points);
1524 const std::vector<Point> & points,
1526 const BoundaryName & start_boundary,
1527 const BoundaryName & end_boundary,
1528 const Real max_elem_size)
1530 std::vector<unsigned int> nums_edges_between_points;
1531 const auto n_points = points.size();
1534 if (!loop && (i + 1) == n_points)
1537 const auto ip1 = (i + 1) % n_points;
1538 const Real length = (points[ip1] - points[i]).
norm();
1539 const unsigned int n_elems =
std::max(
1540 static_cast<unsigned int>(std::ceil(length / max_elem_size)), static_cast<unsigned int>(1));
1541 nums_edges_between_points.push_back(n_elems);
1545 mesh, points, {}, loop, start_boundary, end_boundary, nums_edges_between_points);
1552 for (
const auto & elem :
mesh.active_element_ptr_range())
1553 for (
const auto & i_side : elem->side_index_range())
1554 if (elem->neighbor_ptr(i_side) ==
nullptr)
1556 has_external_bid =
true;
1557 binfo.
add_side(elem, i_side, extern_bid);
void swapNodesInElem(Elem &elem, const unsigned int nd1, const unsigned int nd2)
std::string name(const ElemQuality q)
void remove_id(boundary_id_type id, bool global=false)
std::tuple< dof_id_type, unsigned short int, boundary_id_type > BCTuple
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
std::vector< BCTuple > build_shellface_list() const
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
void makeOrderedNodeList(std::vector< std::pair< dof_id_type, dof_id_type >> &node_assm, std::vector< dof_id_type > &elem_id_list, std::vector< dof_id_type > &midpoint_node_list, std::vector< dof_id_type > &ordered_node_list, std::vector< dof_id_type > &ordered_elem_id_list)
Convert a list of sides in the form of a vector of pairs of node ids into a list of ordered nodes bas...
std::unordered_map< dof_id_type, dof_id_type > getExtraIDUniqueCombinationMap(const MeshBase &mesh, const std::set< SubdomainID > &block_ids, std::vector< ExtraElementIDName > extra_ids)
void buildPolyLineMesh(MeshBase &mesh, const std::vector< Point > &points, const bool loop, const BoundaryName &start_boundary, const BoundaryName &end_boundary, const Real max_elem_size)
virtual Node *& set_node(const unsigned int i)
std::set< BoundaryID > getBoundaryIDSet(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown)
Gets the boundary IDs into a set with their names.
virtual const char * what() const
Get out the error message.
virtual unique_id_type parallel_max_unique_id() const=0
const unsigned int invalid_uint
unsigned int get_node_index(const Node *node_ptr) const
RealVectorValue boundaryWeightedNormal(const BoundaryName &boundary, MeshBase &mesh)
bool hasBoundaryName(const MeshBase &input_mesh, const BoundaryName &name)
void reconnect_nodes(connected_elem_set_type &connected_elements, connected_node_set_type &connected_nodes)
void convertBlockToMesh(MeshBase &source_mesh, MeshBase &target_mesh, const std::vector< SubdomainName > &target_blocks)
void synchronize_global_id_set()
std::set< std::string > sideset
IntRange< unsigned short > side_index_range() const
void skip_partitioning(bool skip)
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
const BoundaryID INVALID_BOUNDARY_ID
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
const InputParameters & parameters() const
Get the parameters of the object.
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
const std::map< boundary_id_type, std::string > & get_sideset_name_map() const
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const=0
bool isCoPlanar(const std::vector< Point > &vec_pts)
const Parallel::Communicator & comm() const
std::unique_ptr< ReplicatedMesh > buildBoundaryMesh(const MeshBase &input_mesh, const boundary_id_type boundary_id)
bool hasBoundaryID(const MeshBase &input_mesh, const BoundaryID id)
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
std::vector< subdomain_id_type > getSubdomainIDs(const libMesh::MeshBase &mesh, const std::vector< SubdomainName > &subdomain_name)
Get the associated subdomainIDs for the subdomain names that are passed in.
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
std::unique_ptr< ReplicatedMesh > buildLoopBoundaryOf2DMesh(const MeshBase &input_mesh, const boundary_id_type boundary_id)
const BoundaryInfo & get_boundary_info() const
std::vector< BCTuple > build_side_list(BCTupleSortBy sort_by=BCTupleSortBy::ELEM_ID) const
void renumber_id(boundary_id_type old_id, boundary_id_type new_id)
bool has_cached_elem_data
Real distance(const Point &p)
Preparation preparation() const
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
uint8_t processor_id_type
SubdomainID getSubdomainID(const SubdomainName &subdomain_name, const MeshBase &mesh)
Gets the subdomain ID associated with the given SubdomainName.
auto max(const L &left, const R &right)
subdomain_id_type get_id_by_name(std::string_view name) const
std::set< dof_id_type > getBoundaryNodes(const MeshBase &mesh, const BoundaryID boundary_id)
bool hasSubdomainID(const MeshBase &input_mesh, const SubdomainID &id)
const SubdomainID INVALID_BLOCK_ID
boundary_id_type get_id_by_name(std::string_view name) const
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
processor_id_type size() const
std::map< boundary_id_type, std::string > & set_sideset_name_map()
unsigned int get_elem_integer_index(std::string_view name) const
void allow_remote_element_removal(bool allow)
virtual bool is_serial() const
TypeVector< Real > unit() const
void libmesh_ignore(const Args &...)
void add_node(const Node *node, const boundary_id_type id)
const std::map< boundary_id_type, std::string > & get_nodeset_name_map() const
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
static constexpr dof_id_type invalid_id
void push_parallel_packed_range(const Communicator &comm, MapToContainers &&data, Context *context, const ActionFunctor &act_on_data)
virtual Elem * add_elem(Elem *e)=0
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
boundary_id_type BoundaryID
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
const std::map< boundary_id_type, std::string > & get_edgeset_name_map() const
virtual dof_id_type max_elem_id() const=0
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const
std::vector< BCTuple > build_edge_list() const
std::unordered_map< dof_id_type, std::unordered_set< dof_id_type > > buildBoundaryNodeToElemMap(const MeshBase &input_mesh, const boundary_id_type boundary_id)
void idSwapParametersProcessor(const std::string &class_name, const std::string &id_name, const std::vector< std::vector< T >> &id_swaps, std::vector< std::unordered_map< T, T >> &id_swap_pairs, const unsigned int row_index_shift=0)
Reprocess the swap related input parameters to make pairs out of them to ease further processing...
std::vector< BoundaryID > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown, const std::set< BoundaryID > &mesh_boundary_ids)
Gets the boundary IDs with their names.
void changeBoundaryId(MeshBase &mesh, const boundary_id_type old_id, const boundary_id_type new_id, bool delete_prev)
void copyIntoMesh(MeshGenerator &mg, UnstructuredMesh &destination, const UnstructuredMesh &source, const bool avoid_merging_subdomains, const bool avoid_merging_boundaries, const Parallel::Communicator &communicator)
std::string & subdomain_name(subdomain_id_type id)
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
std::string stringify(const T &t)
conversion to string
void regenerate_id_sets()
bool hasSubdomainName(const MeshBase &input_mesh, const SubdomainName &name)
const std::set< subdomain_id_type > & get_mesh_subdomains() const
const std::set< boundary_id_type > & get_boundary_ids() const
virtual const Elem * elem_ptr(const dof_id_type i) const=0
void changeSubdomainId(MeshBase &mesh, const subdomain_id_type old_id, const subdomain_id_type new_id)
virtual unsigned int n_sides() const=0
void remove_side(const Elem *elem, const unsigned short int side)
void createSubdomainFromSidesets(MeshBase &mesh, std::vector< BoundaryName > boundary_names, const SubdomainID new_subdomain_id, const SubdomainName new_subdomain_name, const std::string type_name)
Provides a way for users to bail out of the current solve.
std::string get_info(const unsigned int verbosity=0, const bool global=true) const
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
virtual const Elem * query_elem_ptr(const dof_id_type i) const=0
void max(const T &r, T &o, Request &req) const
DIE A HORRIBLE DEATH HERE typedef MPI_Comm communicator
virtual unsigned short dim() const=0
const Node * node_ptr(const unsigned int i) const
std::map< boundary_id_type, std::string > & set_nodeset_name_map()
Point boundaryCentroidCalculator(const BoundaryName &boundary, MeshBase &mesh)
virtual bool is_replicated() const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
void add_shellface(const dof_id_type elem, const unsigned short int shellface, const boundary_id_type id)
const std::set< boundary_id_type > & get_global_boundary_ids() const
IntRange< T > make_range(T beg, T end)
virtual void copy_nodes_and_elements(const MeshBase &other_mesh, const bool skip_find_neighbors=false, dof_id_type element_id_offset=0, dof_id_type node_id_offset=0, unique_id_type unique_id_offset=0, std::unordered_map< subdomain_id_type, subdomain_id_type > *id_remapping=nullptr, const bool skip_preparation=false)
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
bool has_boundary_id_sets
void extraElemIntegerSwapParametersProcessor(const std::string &class_name, const unsigned int num_sections, const unsigned int num_integers, const std::vector< std::vector< std::vector< dof_id_type >>> &elem_integers_swaps, std::vector< std::unordered_map< dof_id_type, dof_id_type >> &elem_integers_swap_pairs)
Reprocess the elem_integers_swaps into maps so they are easier to use.
virtual const Point & point(const dof_id_type i) const=0
std::vector< NodeBCTuple > build_node_list(NodeBCTupleSortBy sort_by=NodeBCTupleSortBy::NODE_ID) const
Point meshCentroidCalculator(const MeshBase &mesh)
void paramWarning(const std::string ¶m, Args... args) const
Real computeMaxDistanceToAxis(const MeshBase &mesh, const Point &origin, const RealVectorValue &direction)
void mergeBoundaryIDsWithSameName(MeshBase &mesh)
SubdomainID getNextFreeSubdomainID(MeshBase &input_mesh)
virtual dof_id_type max_node_id() const=0
virtual const Node * node_ptr(const dof_id_type i) const=0
BoundaryID getNextFreeBoundaryID(MeshBase &input_mesh)
processor_id_type processor_id() const
processor_id_type processor_id() const
const std::multimap< const Node *, boundary_id_type > & get_nodeset_map() const
dof_id_type node_id(const unsigned int i) const
MeshGenerators are objects that can modify or add to an existing mesh.
auto index_range(const T &sizable)
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const=0
std::map< boundary_id_type, std::string > & set_edgeset_name_map()
void add_edge(const dof_id_type elem, const unsigned short int edge, const boundary_id_type id)
void addExternalBoundary(MeshBase &mesh, const BoundaryID extern_bid, bool &has_external_bid)
void set_union(T &data, const unsigned int root_id) const