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" 41 std::map<boundary_id_type, boundary_id_type> same_name_ids;
43 auto populate_map = [](
const std::map<boundary_id_type, std::string> & map,
44 std::map<boundary_id_type, boundary_id_type> & same_ids)
46 for (
const auto & pair_outer : map)
47 for (
const auto & pair_inner : map)
49 if (pair_outer.second == pair_inner.second && pair_outer.first != pair_inner.first &&
50 same_ids.find(pair_inner.first) == same_ids.end())
51 same_ids[pair_outer.first] = pair_inner.first;
54 populate_map(side_bd_name_map, same_name_ids);
55 populate_map(node_bd_name_map, same_name_ids);
57 for (
const auto & [id1, id2] : same_name_ids)
71 std::vector<boundary_id_type> old_ids;
74 for (
auto & elem :
as_range(
mesh.level_elements_begin(0),
mesh.level_elements_end(0)))
76 unsigned int n_sides = elem->n_sides();
80 if (std::find(old_ids.begin(), old_ids.end(), old_id) != old_ids.end())
82 std::vector<boundary_id_type> new_ids(old_ids);
83 std::replace(new_ids.begin(), new_ids.end(), old_id, new_id);
87 boundary_info.
add_side(elem, s, new_ids);
90 boundary_info.
add_side(elem, s, new_ids);
105 std::vector<boundary_id_type>
107 const std::vector<BoundaryName> & boundary_name,
108 bool generate_unknown)
114 std::vector<boundary_id_type>
116 const std::vector<BoundaryName> & boundary_name,
117 bool generate_unknown,
118 const std::set<BoundaryID> & mesh_boundary_ids)
130 if (generate_unknown)
134 max_boundary_local_id = bids.empty() ? 0 : *(bids.rbegin());
140 BoundaryID max_boundary_id = mesh_boundary_ids.empty() ? 0 : *(mesh_boundary_ids.rbegin());
143 max_boundary_id > max_boundary_local_id ? max_boundary_id : max_boundary_local_id;
145 std::vector<BoundaryID> ids(boundary_name.size());
148 if (boundary_name[i] ==
"ANY_BOUNDARY_ID")
150 ids.assign(mesh_boundary_ids.begin(), mesh_boundary_ids.end());
152 mooseWarning(
"You passed \"ANY_BOUNDARY_ID\" in addition to other boundary_names. This " 153 "may be a logic error.");
157 if (boundary_name[i].empty() && !generate_unknown)
158 mooseError(
"Incoming boundary name is empty and we are not generating unknown boundary IDs. " 170 if (generate_unknown &&
173 id = ++max_boundary_id;
178 id = getIDFromName<BoundaryName, BoundaryID>(boundary_name[i]);
188 const std::vector<BoundaryName> & boundary_name,
189 bool generate_unknown)
192 return std::set<BoundaryID>(boundaries.begin(), boundaries.end());
195 std::vector<subdomain_id_type>
198 std::vector<subdomain_id_type> ids;
201 if (subdomain_names.size() == 1 && subdomain_names[0] ==
"ANY_BLOCK_ID")
205 "getSubdomainIDs() should only be called on a prepared mesh if ANY_BLOCK_ID is " 206 "used to query all block IDs");
212 ids.resize(subdomain_names.size());
215 if (subdomain_names[i] ==
"ANY_BLOCK_ID")
216 mooseError(
"getSubdomainIDs() accepts \"ANY_BLOCK_ID\" if and only if it is the only " 217 "subdomain name being queried.");
224 std::set<subdomain_id_type>
228 mesh, std::vector<SubdomainName>(subdomain_names.begin(), subdomain_names.end()));
229 return {blk_ids.begin(), blk_ids.end()};
236 if (boundary_name.empty())
242 id = getIDFromName<BoundaryName, BoundaryID>(boundary_name);
250 if (subdomain_name ==
"ANY_BLOCK_ID")
251 mooseError(
"getSubdomainID() does not work with \"ANY_BLOCK_ID\"");
254 if (subdomain_name.empty())
260 id = getIDFromName<SubdomainName, SubdomainID>(subdomain_name);
268 for (
const auto & elem :
mesh.element_ptr_range())
269 if (elem->subdomain_id() == old_id)
270 elem->subdomain_id() = new_id;
281 for (
const auto & elem :
282 as_range(
mesh.active_local_elements_begin(),
mesh.active_local_elements_end()))
284 Real elem_vol = elem->volume();
285 centroid_pt += (elem->true_centroid()) * elem_vol;
290 centroid_pt /= vol_tmp;
294 std::unordered_map<dof_id_type, dof_id_type>
296 const std::set<SubdomainID> & block_ids,
297 std::vector<ExtraElementIDName> extra_ids)
300 const bool block_restricted = !block_ids.empty();
302 ExtraElementIDName id_name = extra_ids.back();
303 extra_ids.pop_back();
307 if (extra_ids.empty())
310 std::vector<dof_id_type> ids;
312 std::set<dof_id_type> ids_set;
313 for (
const auto & elem :
mesh.active_element_ptr_range())
315 if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
317 const auto id = elem->get_extra_integer(id_index);
321 ids.assign(ids_set.begin(), ids_set.end());
325 std::unordered_map<dof_id_type, dof_id_type> parsed_ids;
326 for (
auto & elem :
mesh.active_element_ptr_range())
328 if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
330 parsed_ids[elem->id()] = std::distance(
331 ids.begin(), std::lower_bound(ids.begin(), ids.end(), elem->get_extra_integer(id_index)));
337 const auto base_parsed_ids =
340 std::vector<std::pair<dof_id_type, dof_id_type>> unique_ids;
342 std::set<std::pair<dof_id_type, dof_id_type>> unique_ids_set;
343 for (
const auto & elem :
mesh.active_element_ptr_range())
345 if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
347 const dof_id_type id1 = libmesh_map_find(base_parsed_ids, elem->id());
348 const dof_id_type id2 = elem->get_extra_integer(id_index);
349 const std::pair<dof_id_type, dof_id_type> ids = std::make_pair(id1, id2);
350 unique_ids_set.insert(ids);
353 unique_ids.assign(unique_ids_set.begin(), unique_ids_set.end());
356 std::unordered_map<dof_id_type, dof_id_type> parsed_ids;
358 for (
const auto & elem :
mesh.active_element_ptr_range())
360 if (block_restricted && block_ids.find(elem->subdomain_id()) == block_ids.end())
362 const dof_id_type id1 = libmesh_map_find(base_parsed_ids, elem->id());
363 const dof_id_type id2 = elem->get_extra_integer(id_index);
366 std::lower_bound(unique_ids.begin(), unique_ids.end(), std::make_pair(id1, id2)));
367 parsed_ids[elem->id()] = new_id;
376 for (
const auto & pt : vec_pts)
385 return isCoPlanar(vec_pts, plane_nvec, vec_pts.front());
393 std::vector<Point> vec_pts_nonzero{vec_pts[0]};
396 vec_pts_nonzero.push_back(vec_pts[i]);
398 if (vec_pts_nonzero.size() <= 3)
402 for (
const auto i :
make_range(vec_pts_nonzero.size() - 1))
404 const Point tmp_pt = (vec_pts_nonzero[i] - vec_pts_nonzero[0])
405 .cross(vec_pts_nonzero[i + 1] - vec_pts_nonzero[0]);
421 std::set<SubdomainID> preexisting_subdomain_ids;
423 if (preexisting_subdomain_ids.empty())
427 const auto highest_subdomain_id =
428 *std::max_element(preexisting_subdomain_ids.begin(), preexisting_subdomain_ids.end());
430 "A SubdomainID with max possible value was found");
431 return highest_subdomain_id + 1;
439 if (boundary_ids.empty())
441 return (*boundary_ids.rbegin() + 1);
447 std::set<SubdomainID> mesh_blocks;
488 std::vector<dof_id_type> & elem_id_list,
489 std::vector<dof_id_type> & midpoint_node_list,
490 std::vector<dof_id_type> & ordered_node_list,
491 std::vector<dof_id_type> & ordered_elem_id_list)
494 bool is_flipped =
false;
496 mooseAssert(node_assm.size(),
"Node list must not be empty");
497 ordered_node_list.push_back(node_assm.front().first);
499 ordered_node_list.push_back(midpoint_node_list.front());
500 ordered_node_list.push_back(node_assm.front().second);
501 ordered_elem_id_list.push_back(elem_id_list.front());
503 node_assm.erase(node_assm.begin());
504 midpoint_node_list.erase(midpoint_node_list.begin());
505 elem_id_list.erase(elem_id_list.begin());
506 const unsigned int node_assm_size_0 = node_assm.size();
507 for (
unsigned int i = 0; i < node_assm_size_0; i++)
510 dof_id_type end_node_id = ordered_node_list.back();
511 auto isMatch1 = [end_node_id](std::pair<dof_id_type, dof_id_type> old_id_pair)
512 {
return old_id_pair.first == end_node_id; };
513 auto isMatch2 = [end_node_id](std::pair<dof_id_type, dof_id_type> old_id_pair)
514 {
return old_id_pair.second == end_node_id; };
515 auto result = std::find_if(node_assm.begin(), node_assm.end(), isMatch1);
517 if (result == node_assm.end())
520 result = std::find_if(node_assm.begin(), node_assm.end(), isMatch2);
527 if (result != node_assm.end())
529 const auto elem_index = std::distance(node_assm.begin(), result);
531 ordered_node_list.push_back(midpoint_node_list[elem_index]);
532 ordered_node_list.push_back(match_first ? (*result).second : (*result).first);
533 node_assm.erase(result);
534 midpoint_node_list.erase(midpoint_node_list.begin() + elem_index);
535 ordered_elem_id_list.push_back(elem_id_list[elem_index]);
536 elem_id_list.erase(elem_id_list.begin() + elem_index);
546 throw MooseException(
"The node list provided has more than one segments.");
550 std::reverse(ordered_node_list.begin(), ordered_node_list.end());
551 std::reverse(midpoint_node_list.begin(), midpoint_node_list.end());
552 std::reverse(ordered_elem_id_list.begin(), ordered_elem_id_list.end());
561 std::vector<dof_id_type> & elem_id_list,
562 std::vector<dof_id_type> & ordered_node_list,
563 std::vector<dof_id_type> & ordered_elem_id_list)
567 node_assm, elem_id_list, dummy_midpoint_node_list, ordered_node_list, ordered_elem_id_list);
580 const std::string & class_name,
581 const unsigned int num_sections,
582 const unsigned int num_integers,
583 const std::vector<std::vector<std::vector<dof_id_type>>> & elem_integers_swaps,
584 std::vector<std::unordered_map<dof_id_type, dof_id_type>> & elem_integers_swap_pairs)
586 elem_integers_swap_pairs.reserve(num_sections * num_integers);
589 const auto & elem_integer_swaps = elem_integers_swaps[i];
590 std::vector<std::unordered_map<dof_id_type, dof_id_type>> elem_integer_swap_pairs;
594 "elem_integers_swaps",
596 elem_integer_swap_pairs,
604 elem_integers_swap_pairs.insert(elem_integers_swap_pairs.end(),
605 elem_integer_swap_pairs.begin(),
606 elem_integer_swap_pairs.end());
610 std::unique_ptr<ReplicatedMesh>
613 auto poly_mesh = std::make_unique<ReplicatedMesh>(input_mesh.
comm());
617 std::unordered_map<dof_id_type, dof_id_type> old_new_node_map;
618 for (
const auto & bside : side_list)
620 if (std::get<2>(bside) != boundary_id)
623 const Elem * elem = input_mesh.
elem_ptr(std::get<0>(bside));
624 const auto side = std::get<1>(bside);
626 auto copy = side_elem->build(side_elem->type());
628 for (
const auto i : side_elem->node_index_range())
630 auto & n = side_elem->node_ref(i);
632 if (old_new_node_map.count(n.id()))
633 copy->set_node(i, poly_mesh->node_ptr(old_new_node_map[n.id()]));
636 Node * node = poly_mesh->add_point(side_elem->point(i));
637 copy->set_node(i, node);
638 old_new_node_map[n.id()] = node->
id();
641 poly_mesh->add_elem(copy.release());
643 poly_mesh->skip_partitioning(
true);
644 poly_mesh->prepare_for_use();
645 if (poly_mesh->n_elem() == 0)
646 mooseError(
"The input mesh does not have a boundary with id ", boundary_id);
653 std::vector<BoundaryName> boundary_names,
655 const SubdomainName new_subdomain_name,
656 const std::string type_name)
673 for (
const auto &
sideset : boundary_names)
675 mooseException(
"The sideset '",
sideset,
"' was not found within the mesh");
678 std::set<boundary_id_type> sidesets(sideset_ids.begin(), sideset_ids.end());
682 std::vector<Elem *> elements_to_send;
683 unsigned short i_need_boundary_elems = 0;
684 for (
const auto & [elem_id, side, bc_id] : side_list)
687 if (sidesets.count(bc_id))
691 i_need_boundary_elems = 1;
694 elements_to_send.push_back(elem);
698 std::set<const Elem *, libMesh::CompareElemIdsByLevel> connected_elements(
699 elements_to_send.begin(), elements_to_send.end());
700 std::set<const Node *> connected_nodes;
702 std::set<dof_id_type> connected_node_ids;
703 for (
auto * nd : connected_nodes)
704 connected_node_ids.insert(nd->id());
706 std::vector<unsigned short> need_boundary_elems(
mesh->
comm().
size());
708 std::unordered_map<processor_id_type, decltype(elements_to_send)> push_element_data;
709 std::unordered_map<processor_id_type, decltype(connected_nodes)> push_node_data;
715 if (elements_to_send.size())
716 push_element_data[pid] = elements_to_send;
717 if (connected_nodes.size())
718 push_node_data[pid] = connected_nodes;
725 Parallel::push_parallel_packed_range(
726 mesh->
comm(), push_node_data,
mesh.get(), node_action_functor);
732 mesh->
comm(), push_element_data,
mesh.get(), elem_action_functor);
738 std::vector<std::pair<dof_id_type, ElemSidePair>> element_sides_on_boundary;
740 for (
const auto & triple : side_list)
741 if (sidesets.count(std::get<2>(triple)))
747 "Only active, level 0 elements can be made interior parents of new level 0 lower-d " 748 "elements. Make sure that ",
750 "s are run before any refinement generators");
751 element_sides_on_boundary.push_back(
752 std::make_pair(counter,
ElemSidePair(elem, std::get<1>(triple))));
763 for (
auto & [i, elem_side] : element_sides_on_boundary)
765 Elem * elem = elem_side.elem;
767 const auto side = elem_side.side;
776 side_elem->subdomain_id() = new_block_id;
780 side_elem->set_interior_parent(elem);
783 side_elem->set_id(max_elem_id + i);
784 side_elem->set_unique_id(max_unique_id + i);
791 if (new_subdomain_name.size())
802 std::unique_ptr<MeshBase> & target_mesh,
803 const std::vector<SubdomainName> & target_blocks)
805 if (!source_mesh->is_replicated())
806 mooseError(
"This generator does not support distributed meshes.");
811 std::set<SubdomainID> mesh_blocks;
812 source_mesh->subdomain_ids(mesh_blocks);
817 mooseException(
"The target_block '", target_blocks[i],
"' was not found within the mesh.");
821 std::unordered_map<dof_id_type, dof_id_type> old_new_node_map;
823 for (
const auto target_block_id : target_block_ids)
826 for (
auto elem : source_mesh->active_subdomain_elements_ptr_range(target_block_id))
828 if (elem->level() != 0)
829 mooseError(
"Refined blocks are not supported by this generator. " 830 "Can you re-organize mesh generators to refine after converting the block?");
833 auto copy = elem->build(elem->type());
842 auto & n = elem->node_ref(i);
844 if (old_new_node_map.count(n.id()))
849 copy->set_node(copy_n_index++, target_mesh->node_ptr(old_new_node_map[n.id()]));
859 Node * node = target_mesh->add_point(elem->point(i));
862 copy->set_node(copy_n_index++, node);
865 old_new_node_map[n.id()] = node->
id();
871 target_mesh->add_elem(copy.release());
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)
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
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)
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.
std::unique_ptr< ReplicatedMesh > buildBoundaryMesh(const ReplicatedMesh &input_mesh, const boundary_id_type boundary_id)
virtual unique_id_type parallel_max_unique_id() const=0
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.
bool hasBoundaryName(const MeshBase &input_mesh, const BoundaryName &name)
void createSubdomainFromSidesets(std::unique_ptr< MeshBase > &mesh, std::vector< BoundaryName > boundary_names, const SubdomainID new_subdomain_id, const SubdomainName new_subdomain_name, const std::string type_name)
void reconnect_nodes(connected_elem_set_type &connected_elements, connected_node_set_type &connected_nodes)
std::set< std::string > sideset
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.
bool doesMapContainValue(const std::map< T1, T2 > &the_map, const T2 &value)
This routine is a simple helper function for searching a map by values instead of keys...
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
const Parallel::Communicator & comm() const
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...
const BoundaryInfo & get_boundary_info() const
void renumber_id(boundary_id_type old_id, boundary_id_type new_id)
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
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
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
virtual const Elem * elem_ptr(const dof_id_type i) const override final
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 &...)
const std::map< boundary_id_type, std::string > & get_nodeset_name_map() const
void push_parallel_packed_range(const Communicator &comm, MapToContainers &&data, Context *context, const ActionFunctor &act_on_data)
virtual Elem * add_elem(Elem *e)=0
boundary_id_type BoundaryID
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
virtual dof_id_type max_elem_id() const=0
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const
static const dof_id_type invalid_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)
std::string & subdomain_name(subdomain_id_type id)
bool hasSubdomainName(const MeshBase &input_mesh, const SubdomainName &name)
bool isCoPlanar(const std::vector< Point > vec_pts)
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)
void remove_side(const Elem *elem, const unsigned short int side)
Provides a way for users to bail out of the current solve.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual const Elem * query_elem_ptr(const dof_id_type i) const=0
void max(const T &r, T &o, Request &req) const
const Node * node_ptr(const unsigned int i) const
virtual bool is_replicated() const
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
const std::set< boundary_id_type > & get_global_boundary_ids() const
IntRange< T > make_range(T beg, T end)
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.
Point meshCentroidCalculator(const MeshBase &mesh)
void mergeBoundaryIDsWithSameName(MeshBase &mesh)
void convertBlockToMesh(std::unique_ptr< MeshBase > &source_mesh, std::unique_ptr< MeshBase > &target_mesh, const std::vector< SubdomainName > &target_blocks)
SubdomainID getNextFreeSubdomainID(MeshBase &input_mesh)
bool isDigits(const std::string &str)
Courtesy https://stackoverflow.com/a/8889045 and https://en.cppreference.com/w/cpp/string/byte/isdigi...
BoundaryID getNextFreeBoundaryID(MeshBase &input_mesh)
processor_id_type processor_id() const
processor_id_type processor_id() const
auto index_range(const T &sizable)
void set_union(T &data, const unsigned int root_id) const