14 #include "libmesh/distributed_mesh.h" 15 #include "libmesh/elem.h" 16 #include "libmesh/partitioner.h" 26 "Break the mesh at interfaces between blocks. New nodes will be generated so elements on " 27 "each side of the break are no longer connected.");
29 params.
addRequiredParam<MeshGeneratorName>(
"input",
"The mesh we want to modify");
34 "the name of the new interface. Cannot be used with `split_interface=true`");
35 params.
addParam<
bool>(
"split_interface",
37 "If true, it creates a " 38 "different interface for each block pair.");
39 params.
addParam<std::vector<SubdomainName>>(
41 "The list of subdomain names surrounding which interfaces will be generated.");
42 params.
addParam<std::vector<std::vector<SubdomainName>>>(
43 "block_pairs",
"The list of subdomain pairs between which interfaces will be generated.");
44 params.
addParam<
bool>(
"add_transition_interface",
46 "If true and block is not empty, a special boundary named " 47 "interface_transition is generate between listed blocks and other blocks.");
49 "split_transition_interface",
false,
"Whether to split the transition interface by blocks.");
50 params.
addParam<
bool>(
"add_interface_on_two_sides",
52 "Whether to add an additional interface boundary at the other side.");
54 "interface_transition_name",
55 "interface_transition",
56 "the name of the interface transition boundary created when blocks are provided");
61 { rm_params.
set<
unsigned short>(
"layers") = 1; });
68 _input(getMesh(
"input")),
69 _interface_name(getParam<
std::string>(
"interface_name")),
70 _split_interface(getParam<bool>(
"split_interface")),
71 _block_pairs_restricted(parameters.isParamSetByUser(
"block_pairs")),
72 _surrounding_blocks_restricted(parameters.isParamSetByUser(
"surrounding_blocks")),
73 _add_transition_interface(getParam<bool>(
"add_transition_interface")),
74 _split_transition_interface(getParam<bool>(
"split_transition_interface")),
75 _interface_transition_name(getParam<BoundaryName>(
"interface_transition_name")),
76 _add_interface_on_two_sides(getParam<bool>(
"add_interface_on_two_sides"))
80 "if split_interface == true, the new interface_name cannot be specified by the " 81 "user. It will be automatically assigned");
84 "BreakMeshByBlockGenerator: 'surrounding_blocks' and 'block_pairs' can not be used " 89 "BreakMeshByBlockGenerator cannot split the transition interface because " 90 "add_transition_interface is false");
94 "BreakMeshByBlockGenerator cannot split the transition interface when 'block_pairs' " 103 std::string primary_block_name =
mesh.subdomain_name(primaryBlockID);
104 std::string secondary_block_name =
mesh.subdomain_name(secondaryBlockID);
105 if (primary_block_name.empty())
106 primary_block_name =
"Block" + std::to_string(primaryBlockID);
107 if (secondary_block_name.empty())
108 secondary_block_name =
"Block" + std::to_string(secondaryBlockID);
110 return primary_block_name +
"_" + secondary_block_name;
115 const std::string & boundaryName)
117 _bName_bID_set.insert(std::pair<std::string, int>(boundaryName, boundaryID));
124 BoundaryName & boundaryName,
126 BoundaryInfo & boundary_info)
131 bool checkBoundaryAlreadyExist =
false;
134 if (b.first.compare(boundaryName) == 0)
136 mooseAssert(boundaryID == b.second,
"Boundary with two inconsistent ids");
137 checkBoundaryAlreadyExist =
true;
141 if (checkBoundaryAlreadyExist)
147 boundary_info.sideset_name(boundaryID) = boundaryName;
153 std::unique_ptr<MeshBase>
156 std::unique_ptr<MeshBase>
mesh = std::move(
_input);
159 if (!
mesh->preparation().has_cached_elem_data)
160 mesh->cache_elem_data();
163 auto max_node_id =
mesh->max_node_id();
166 if (!
mesh->is_replicated() && !
mesh->is_prepared())
167 mesh->comm().max(max_node_id);
168 #if LIBMESH_ENABLE_UNIQUE_ID 169 const auto max_unique_id =
mesh->parallel_max_unique_id();
172 BoundaryInfo & boundary_info =
mesh->get_boundary_info();
177 for (
const auto & block_name_pair :
178 getParam<std::vector<std::vector<SubdomainName>>>(
"block_pairs"))
180 if (block_name_pair.size() != 2)
182 "Each row of 'block_pairs' must have a size of two (block names).");
185 for (
const auto &
name : block_name_pair)
187 paramError(
"block_pairs",
"The block '",
name,
"' was not found in the mesh");
190 std::pair<SubdomainID, SubdomainID> pair = std::make_pair(
191 std::min(block_pair[0], block_pair[1]),
std::max(block_pair[0], block_pair[1]));
202 for (
const auto &
name :
getParam<std::vector<SubdomainName>>(
"surrounding_blocks"))
204 paramError(
"surrounding_blocks",
"The block '",
name,
"' was not found in the mesh");
207 *
mesh,
getParam<std::vector<SubdomainName>>(
"surrounding_blocks"));
208 std::copy(surrounding_block_ids.begin(),
209 surrounding_block_ids.end(),
217 "BreakMeshByBlockGenerator the specified interface transition boundary name " 221 std::unordered_map<dof_id_type, std::vector<dof_id_type>> node_to_elem_map;
222 for (
const auto & elem :
mesh->active_element_ptr_range())
223 for (
const auto n :
make_range(elem->n_nodes()))
224 node_to_elem_map[elem->node_id(n)].push_back(elem->id());
231 for (
const auto & [current_node_id, connected_elems] : node_to_elem_map)
233 const auto * current_node =
mesh->node_ptr(current_node_id);
238 auto it = nodeid_to_connected_blocks.find(current_node_id);
239 if (it == nodeid_to_connected_blocks.end())
246 auto connected_blocks = it->second;
250 connected_blocks.erase(Elem::invalid_subdomain_id);
252 const unsigned int node_multiplicity = connected_blocks.size();
255 if (node_multiplicity > 1)
259 auto subdomain_it = connected_blocks.begin();
262 connected_blocks.find(Elem::invalid_subdomain_id) != connected_blocks.end()
263 ? Elem::invalid_subdomain_id
268 bool should_create_new_node =
true;
272 should_create_new_node =
false;
276 const auto & sets_blocks_for_this_node = it->second;
282 if (sets_blocks_for_this_node.size() == 2)
285 auto set_blocks_for_this_node_it = sets_blocks_for_this_node.begin();
291 should_create_new_node =
true;
296 unsigned int multiplicity_counter = node_multiplicity;
297 for (
auto elem_id : connected_elems)
300 if (multiplicity_counter == 0)
303 Elem * current_elem =
mesh->elem_ptr(elem_id);
307 if ((block_id != reference_subdomain_id) ||
311 Node * new_node =
nullptr;
313 std::vector<boundary_id_type> node_boundary_ids;
315 for (
const auto local_node_id :
make_range(current_elem->n_nodes()))
316 if (current_elem->node_id(local_node_id) ==
319 if (should_create_new_node)
326 new_node = Node::build(*current_node,
327 mesh->is_replicated()
329 : (current_elem->subdomain_id() + 1) * max_node_id +
332 #if LIBMESH_ENABLE_UNIQUE_ID 333 new_node->set_unique_id((current_elem->subdomain_id() + 1) * max_unique_id +
334 current_node->unique_id());
339 new_node->processor_id() = current_elem->processor_id();
340 mesh->add_node(new_node);
341 current_elem->set_node(local_node_id, new_node);
343 boundary_info.boundary_ids(current_node, node_boundary_ids);
344 boundary_info.add_node(new_node, node_boundary_ids);
346 multiplicity_counter--;
351 if (should_create_new_node)
353 for (
auto connected_elem_id : connected_elems)
355 Elem * connected_elem =
mesh->elem_ptr(connected_elem_id);
359 if (connected_elem->subdomain_id() == current_elem->subdomain_id() &&
360 connected_elem != current_elem)
362 for (
const auto local_node_id :
make_range(connected_elem->n_nodes()))
363 if (connected_elem->node_id(local_node_id) ==
366 connected_elem->set_node(local_node_id, new_node);
376 for (
auto elem_id : connected_elems)
378 for (
auto connected_elem_id : connected_elems)
380 Elem * current_elem =
mesh->elem_ptr(elem_id);
381 Elem * connected_elem =
mesh->elem_ptr(connected_elem_id);
384 if (current_elem == connected_elem)
391 if (curr_elem_subid < connected_elem_subid)
393 if (current_elem->has_neighbor(connected_elem))
395 unsigned int side = current_elem->which_neighbor_am_i(connected_elem);
396 unsigned int connected_elem_side = connected_elem->which_neighbor_am_i(current_elem);
400 bool need_to_switch =
false;
403 connected_elem_subid = connected_elem->subdomain_id();
404 if (curr_elem_subid > connected_elem_subid)
406 connected_elem_subid = current_elem->subdomain_id();
407 curr_elem_subid = connected_elem->subdomain_id();
409 side = connected_elem->which_neighbor_am_i(current_elem);
411 connected_elem_side = current_elem->which_neighbor_am_i(connected_elem);
412 need_to_switch =
true;
416 std::pair<subdomain_id_type, subdomain_id_type> blocks_pair =
417 std::make_pair(curr_elem_subid, connected_elem_subid);
419 std::pair<subdomain_id_type, subdomain_id_type> blocks_pair2 =
420 std::make_pair(connected_elem_subid, curr_elem_subid);
422 auto add_boundary_sides =
423 [&](
const std::pair<subdomain_id_type, subdomain_id_type> & blocks_pair,
424 const std::pair<subdomain_id_type, subdomain_id_type> & blocks_pair2,
426 Elem * connected_elem,
428 unsigned int connected_elem_side,
433 !need_to_switch ? current_elem : connected_elem, side);
438 !need_to_switch ? connected_elem : current_elem, connected_elem_side);
446 add_boundary_sides(blocks_pair,
455 add_boundary_sides(blocks_pair,
480 if (!
mesh->is_serial())
481 mesh->remove_orphaned_nodes();
482 Partitioner::set_node_processor_ids(*
mesh);
484 mesh->unset_is_prepared();
492 BoundaryInfo & boundary_info =
mesh.get_boundary_info();
498 BoundaryName boundary_name;
500 const std::set<boundary_id_type> & ids = boundary_info.get_boundary_ids();
505 mesh.comm().max(new_boundaryID);
506 mooseAssert(new_boundaryID >= 0,
"Invalid new boundary ID computed.");
510 std::vector<std::pair<subdomain_id_type, subdomain_id_type>> sorted_pairs(
513 for (
auto & boundary_side : sorted_pairs)
522 boundary_id = new_boundaryID;
525 boundary_side.second,
538 : boundary_id_interface;
539 boundary_id = boundary_id_interface;
540 boundary_info.sideset_name(boundary_id_interface) = boundary_name;
552 boundary_id = new_boundaryID;
555 boundary_side.second,
560 else if (interior_boundary)
566 : boundary_id_interface;
568 boundary_id = boundary_id_interface;
569 boundary_info.sideset_name(boundary_id_interface) = boundary_name;
574 boundary_id_interface_transition =
577 : boundary_id_interface_transition;
578 boundary_id = boundary_id_interface_transition;
589 for (
const auto & [elem, side] : boundary_side_map->second)
590 boundary_info.add_side(elem, side, boundary_id);
595 boundary_info.parallel_sync_side_ids();
596 boundary_info.parallel_sync_node_ids();
601 const auto rev_pair = std::make_pair(sub_pair.second, sub_pair.first);
602 const bool has_reverse =
606 mesh.add_disjoint_neighbor_boundary_pairs(
617 elem_subdomain_id = Elem::invalid_subdomain_id;
619 return elem_subdomain_id;
626 if ((block_pair.first == block_one && block_pair.second == block_two) ||
627 (block_pair.first == block_two && block_pair.second == block_one))
632 std::unordered_map<dof_id_type, std::set<subdomain_id_type>>
634 const std::unordered_map<
dof_id_type, std::vector<dof_id_type>> & node_to_elem_map,
635 const MeshBase & mesh)
637 std::unordered_map<dof_id_type, std::set<subdomain_id_type>> nodeid_to_connected_blocks;
640 for (
const auto & [node_id, elem_ids] : node_to_elem_map)
642 std::set<subdomain_id_type> connected_blocks;
645 const Elem * current_elem =
mesh.elem_ptr(elem_id);
647 nodeid_to_connected_blocks[node_id].insert(block_id);
652 if (
mesh.is_replicated())
653 return nodeid_to_connected_blocks;
656 const auto mesh_pid =
mesh.processor_id();
661 using NodeConnectedBlocksTuple =
663 std::map<processor_id_type, std::vector<NodeConnectedBlocksTuple>> to_owner;
664 for (
const auto & [node_id,
blocks] : nodeid_to_connected_blocks)
666 const Node * node =
mesh.node_ptr(node_id);
667 if (node && node->processor_id() != mesh_pid)
668 to_owner[node->processor_id()].emplace_back(
669 node_id, std::vector<subdomain_id_type>(
blocks.begin(),
blocks.end()), mesh_pid);
673 std::unordered_map<dof_id_type, std::set<processor_id_type>> subscribers;
674 Parallel::push_parallel_vector_data(
679 for (
const auto & [node_id, blocks_vec, ghost_pid] : recv_data)
681 subscribers[node_id].insert(ghost_pid);
682 nodeid_to_connected_blocks[node_id].insert(blocks_vec.begin(), blocks_vec.end());
687 std::map<processor_id_type, std::vector<NodeConnectedBlocksPair>> from_owner;
688 for (
const auto & [node_id, sub_pids] : subscribers)
690 const Node * node =
mesh.node_ptr(node_id);
691 if (node && node->processor_id() == mesh_pid)
693 const auto & blocks_set = libmesh_map_find(nodeid_to_connected_blocks, node_id);
694 for (
const auto pid : sub_pids)
695 from_owner[pid].emplace_back(
696 node_id, std::vector<subdomain_id_type>(blocks_set.begin(), blocks_set.end()));
700 Parallel::push_parallel_vector_data(
705 for (
const auto & [node_id, blocks_vec] : recv_data)
706 nodeid_to_connected_blocks[node_id].insert(blocks_vec.begin(), blocks_vec.end());
708 return nodeid_to_connected_blocks;
registerMooseObject("MooseApp", BreakMeshByBlockGenerator)
std::unordered_set< std::pair< SubdomainID, SubdomainID > > _block_pairs
set of subdomain pairs between which interfaces will be generated.
static InputParameters validParams()
const InputParameters & _pars
The object's parameters.
void paramError(const std::string ¶m, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
std::unordered_map< std::pair< subdomain_id_type, subdomain_id_type >, boundary_id_type > _subid_pairs_to_boundary_id
Map from a pair of block ids to the corresponding boundary id.
const BoundaryID INVALID_BOUNDARY_ID
const bool _surrounding_blocks_restricted
whether interfaces will be generated surrounding blocks
const bool _split_transition_interface
whether to split the transition boundary between the blocks and the rest of the mesh ...
void addInterface(MeshBase &mesh)
generate the new boundary interface
const Parallel::Communicator & comm() const
void findBoundaryName(const MeshBase &mesh, subdomain_id_type primaryBlockID, subdomain_id_type secondaryBlockID, BoundaryName &boundary_name, boundary_id_type boundaryID, BoundaryInfo &boundary_info)
given the primary and secondary blocks this method return the appropriate boundary id and name ...
std::set< std::pair< std::string, BoundaryID > > _bName_bID_set
A container holding (boundary name, boundary ID) associations.
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
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.
const bool _block_pairs_restricted
whether interfaces will be generated between block pairs
bool findBlockPairs(subdomain_id_type block_one, subdomain_id_type block_two)
Return true if block_one and block_two are found in users' provided block_pairs list.
auto max(const L &left, const R &right)
void mapBoundaryIdAndBoundaryName(boundary_id_type boundaryID, const std::string &boundaryName)
this method save the boundary name/id pair
std::unordered_map< SubdomainPair, std::set< ElemSide > > _subid_pairs_to_sides
Map from a pair of block ids to a set of ElemSide tuples.
uint8_t processor_id_type
const std::string & name() const
Get the name of the class.
const BoundaryName _interface_transition_name
the name of the transition interface
std::unique_ptr< MeshBase > & _input
the mesh to modify
BoundaryName generateBoundaryName(const MeshBase &mesh, subdomain_id_type primaryBlockID, subdomain_id_type secondaryBlockID)
this method generate the boundary name by assembling subdomain names
std::set< SubdomainPair > _neighboring_block_list
Set of pairs of block ids between which new boundary sides are created.
static InputParameters validParams()
std::unordered_map< dof_id_type, std::set< subdomain_id_type > > syncConnectedBlocks(const std::unordered_map< dof_id_type, std::vector< dof_id_type >> &node_to_elem_map, const MeshBase &mesh)
Synchronizes connected blocks across all MPI ranks.
subdomain_id_type blockRestrictedElementSubdomainID(const Elem *elem)
This is a helper method to avoid recoding the same if everywhere.
bool hasSubdomainName(const MeshBase &input_mesh, const SubdomainName &name)
Whether a particular subdomain name exists in the mesh.
std::unordered_set< SubdomainID > _block_set
set of the blocks to split the mesh on
const bool _add_transition_interface
whether to add a boundary when splitting the mesh
IntRange< T > make_range(T beg, T end)
bool _split_interface
the flag to split the interface by block
BreakMeshByBlockGenerator(const InputParameters ¶meters)
const bool _add_interface_on_two_sides
whether to add two sides interface boundaries
std::string _interface_name
the name of the new interface
auto min(const L &left, const R &right)
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
MeshGenerators are objects that can modify or add to an existing mesh.