https://mooseframework.inl.gov
BreakMeshByBlockGenerator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
11 #include "CastUniquePointer.h"
12 #include "MooseMeshUtils.h"
13 
14 #include "libmesh/distributed_mesh.h"
15 #include "libmesh/elem.h"
16 #include "libmesh/partitioner.h"
17 #include "timpi/parallel_sync.h"
18 
20 
23 {
25  params.addClassDescription(
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.");
28 
29  params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
30 
31  params.addParam<std::string>(
32  "interface_name",
33  "interface",
34  "the name of the new interface. Cannot be used with `split_interface=true`");
35  params.addParam<bool>("split_interface",
36  true,
37  "If true, it creates a "
38  "different interface for each block pair.");
39  params.addParam<std::vector<SubdomainName>>(
40  "surrounding_blocks",
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",
45  false,
46  "If true and block is not empty, a special boundary named "
47  "interface_transition is generate between listed blocks and other blocks.");
48  params.addParam<bool>(
49  "split_transition_interface", false, "Whether to split the transition interface by blocks.");
50  params.addParam<bool>("add_interface_on_two_sides",
51  true,
52  "Whether to add an additional interface boundary at the other side.");
53  params.addParam<BoundaryName>(
54  "interface_transition_name",
55  "interface_transition",
56  "the name of the interface transition boundary created when blocks are provided");
57 
58  params.addRelationshipManager("ElementSideNeighborLayers",
60  [](const InputParameters &, InputParameters & rm_params)
61  { rm_params.set<unsigned short>("layers") = 1; });
62 
63  return params;
64 }
65 
67  : MeshGenerator(parameters),
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"))
77 {
78  if (getParam<bool>("split_interface") && _pars.isParamSetByUser("interface_name"))
79  paramError("interface_name",
80  "if split_interface == true, the new interface_name cannot be specified by the "
81  "user. It will be automatically assigned");
83  paramError("block_pairs_restricted",
84  "BreakMeshByBlockGenerator: 'surrounding_blocks' and 'block_pairs' can not be used "
85  "at the same time.");
86 
88  paramError("split_transition_interface",
89  "BreakMeshByBlockGenerator cannot split the transition interface because "
90  "add_transition_interface is false");
91 
93  paramError("add_transition_interface",
94  "BreakMeshByBlockGenerator cannot split the transition interface when 'block_pairs' "
95  "are specified.");
96 }
97 
98 BoundaryName
100  subdomain_id_type primaryBlockID,
101  subdomain_id_type secondaryBlockID)
102 {
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);
109 
110  return primary_block_name + "_" + secondary_block_name;
111 }
112 
113 void
115  const std::string & boundaryName)
116 {
117  _bName_bID_set.insert(std::pair<std::string, int>(boundaryName, boundaryID));
118 }
119 
120 void
122  subdomain_id_type primaryBlockID,
123  subdomain_id_type secondaryBlockID,
124  BoundaryName & boundaryName,
125  boundary_id_type boundaryID,
126  BoundaryInfo & boundary_info)
127 {
128  boundaryName = generateBoundaryName(mesh, primaryBlockID, secondaryBlockID);
129 
130  // check if the boundary name already exist
131  bool checkBoundaryAlreadyExist = false;
132  for (auto b : _bName_bID_set)
133  {
134  if (b.first.compare(boundaryName) == 0)
135  {
136  mooseAssert(boundaryID == b.second, "Boundary with two inconsistent ids");
137  checkBoundaryAlreadyExist = true;
138  }
139  }
140 
141  if (checkBoundaryAlreadyExist)
142  return;
143  else
144  {
145  mapBoundaryIdAndBoundaryName(boundaryID, boundaryName);
146 
147  boundary_info.sideset_name(boundaryID) = boundaryName;
148 
149  return;
150  }
151 }
152 
153 std::unique_ptr<MeshBase>
155 {
156  std::unique_ptr<MeshBase> mesh = std::move(_input);
157 
158  // Make sure subdomain caches are up to date
159  if (!mesh->preparation().has_cached_elem_data)
160  mesh->cache_elem_data();
161 
162  // Max node id is used later to generate new unique node IDs
163  auto max_node_id = mesh->max_node_id();
164  // If the mesh is prepared, max_node_id is already the same on all processors so do not need to
165  // communicate.
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();
170 #endif
171 
172  BoundaryInfo & boundary_info = mesh->get_boundary_info();
173 
174  // Handle block restrictions
176  {
177  for (const auto & block_name_pair :
178  getParam<std::vector<std::vector<SubdomainName>>>("block_pairs"))
179  {
180  if (block_name_pair.size() != 2)
181  paramError("block_pairs",
182  "Each row of 'block_pairs' must have a size of two (block names).");
183 
184  // check that the blocks exist in the mesh
185  for (const auto & name : block_name_pair)
187  paramError("block_pairs", "The block '", name, "' was not found in the mesh");
188 
189  const auto block_pair = MooseMeshUtils::getSubdomainIDs(*mesh, block_name_pair);
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]));
192 
193  _block_pairs.insert(pair);
194  // Insert all SubdomainIDs from block_pair into _block_set (duplicates automatically
195  // ignored)
196  std::copy(block_pair.begin(), block_pair.end(), std::inserter(_block_set, _block_set.end()));
197  }
198  }
200  {
201  // check that the blocks exist in the mesh
202  for (const auto & name : getParam<std::vector<SubdomainName>>("surrounding_blocks"))
204  paramError("surrounding_blocks", "The block '", name, "' was not found in the mesh");
205 
206  const auto surrounding_block_ids = MooseMeshUtils::getSubdomainIDs(
207  *mesh, getParam<std::vector<SubdomainName>>("surrounding_blocks"));
208  std::copy(surrounding_block_ids.begin(),
209  surrounding_block_ids.end(),
210  std::inserter(_block_set, _block_set.end()));
211  }
212 
213  // check that a boundary named _interface_transition_name does not already exist in the mesh
215  boundary_info.get_id_by_name(_interface_transition_name) != Moose::INVALID_BOUNDARY_ID)
216  paramError("interface_transition_name",
217  "BreakMeshByBlockGenerator the specified interface transition boundary name "
218  "already exits");
219 
220  // initialize the node to element map
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());
225 
226  // Sync connected block info across all ranks
227  // a map from a node id to the set of connected block ids
228  const auto nodeid_to_connected_blocks = syncConnectedBlocks(node_to_elem_map, *mesh);
229 
230  // Main loop to duplicate nodes
231  for (const auto & [current_node_id, connected_elems] : node_to_elem_map)
232  {
233  const auto * current_node = mesh->node_ptr(current_node_id);
234  if (!current_node)
235  continue;
236 
237  // If the node is not connected to any blocks, skip it
238  auto it = nodeid_to_connected_blocks.find(current_node_id);
239  if (it == nodeid_to_connected_blocks.end())
240  continue;
241 
242  // Get the set of blocks connected to the current node.
243  // Make a local copy because we'll modify it (e.g., erase Elem::invalid_subdomain_id).
244  // Preserve the original map's information (including invalid_subdomain_id) for global
245  // synchronization/logic, but operate on the copy for duplication decisions.
246  auto connected_blocks = it->second;
247 
248  // Remove invalid subdomain if block pairs restriction is active
250  connected_blocks.erase(Elem::invalid_subdomain_id);
251 
252  const unsigned int node_multiplicity = connected_blocks.size();
253 
254  // check if current_node need to be duplicated
255  if (node_multiplicity > 1)
256  {
257  // find reference_subdomain_id (e.g. the subdomain with lower id or the
258  // Elem::invalid_subdomain_id)
259  auto subdomain_it = connected_blocks.begin();
260  subdomain_id_type reference_subdomain_id =
262  connected_blocks.find(Elem::invalid_subdomain_id) != connected_blocks.end()
263  ? Elem::invalid_subdomain_id
264  : *subdomain_it;
265 
266  // For the block_pairs option, the node that is only shared by specific block pairs will be
267  // newly created.
268  bool should_create_new_node = true;
270  {
271  // Default to false; only set to true if this is exactly the pair boundary we want
272  should_create_new_node = false;
273 
274  // Directly use the global info synchronized earlier by syncConnectedBlocks
275  // Invalid subdomain is included for correct logic
276  const auto & sets_blocks_for_this_node = it->second;
277 
278  // Check if this node is exactly at the boundary between two blocks
279  // If it is a junction between more than two blocks, we do not split it
280  // For the block_pairs option, only nodes shared by the specified block pairs are newly
281  // created.
282  if (sets_blocks_for_this_node.size() == 2)
283  {
284  // Get the two block IDs from the set
285  auto set_blocks_for_this_node_it = sets_blocks_for_this_node.begin();
286  subdomain_id_type block1 = *set_blocks_for_this_node_it;
287  subdomain_id_type block2 = *std::next(set_blocks_for_this_node_it);
288 
289  // Check if this block pair is one of the user-specified pairs to split
290  if (findBlockPairs(block1, block2))
291  should_create_new_node = true;
292  }
293  }
294 
295  // multiplicity counter to keep track of how many nodes we added
296  unsigned int multiplicity_counter = node_multiplicity;
297  for (auto elem_id : connected_elems)
298  {
299  // all the duplicate nodes are added and assigned
300  if (multiplicity_counter == 0)
301  break;
302 
303  Elem * current_elem = mesh->elem_ptr(elem_id);
304  subdomain_id_type block_id = blockRestrictedElementSubdomainID(current_elem);
305 
306  // Work on duplicating the node only from the element that is not on the reference subdomain
307  if ((block_id != reference_subdomain_id) ||
308  (_block_pairs_restricted && findBlockPairs(reference_subdomain_id, block_id)))
309  {
310  // assign the newly added node to current_elem
311  Node * new_node = nullptr;
312 
313  std::vector<boundary_id_type> node_boundary_ids;
314 
315  for (const auto local_node_id : make_range(current_elem->n_nodes()))
316  if (current_elem->node_id(local_node_id) ==
317  current_node->id()) // if current node == node on element
318  {
319  if (should_create_new_node)
320  {
321  // The maximum number of duplications per subdomain will not exceed the original
322  // number of nodes. Since max_node_id is always greater than the original node
323  // count, it is safe to use max_node_id as a stride to generate new unique node ID
324  // values.
325  // max_node_id > original node count > number of duplications per subdomain
326  new_node = Node::build(*current_node,
327  mesh->is_replicated()
328  ? mesh->n_nodes()
329  : (current_elem->subdomain_id() + 1) * max_node_id +
330  current_node->id())
331  .release();
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());
335 #endif
336  // We're duplicating nodes so that each subdomain elem has its own copy, so it
337  // seems natural to assign this new node the same proc id as corresponding
338  // subdomain elem
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);
342  // Add boundary info to the new node
343  boundary_info.boundary_ids(current_node, node_boundary_ids);
344  boundary_info.add_node(new_node, node_boundary_ids);
345  }
346  multiplicity_counter--; // node created, update multiplicity counter
347  break; // once the proper node has been fixed in one element we can break the
348  // loop
349  }
350 
351  if (should_create_new_node)
352  {
353  for (auto connected_elem_id : connected_elems)
354  {
355  Elem * connected_elem = mesh->elem_ptr(connected_elem_id);
356 
357  // Assign the newly added node to other connected elements with the same
358  // block_id
359  if (connected_elem->subdomain_id() == current_elem->subdomain_id() &&
360  connected_elem != current_elem)
361  {
362  for (const auto local_node_id : make_range(connected_elem->n_nodes()))
363  if (connected_elem->node_id(local_node_id) ==
364  current_node->id()) // if current node == node on element
365  {
366  connected_elem->set_node(local_node_id, new_node);
367  break;
368  }
369  }
370  }
371  }
372  }
373  }
374 
375  // create blocks pair and assign element side to new interface boundary map
376  for (auto elem_id : connected_elems)
377  {
378  for (auto connected_elem_id : connected_elems)
379  {
380  Elem * current_elem = mesh->elem_ptr(elem_id);
381  Elem * connected_elem = mesh->elem_ptr(connected_elem_id);
382 
383  // Early out if both elements are the same
384  if (current_elem == connected_elem)
385  continue;
386 
387  subdomain_id_type curr_elem_subid = blockRestrictedElementSubdomainID(current_elem);
388  subdomain_id_type connected_elem_subid =
389  blockRestrictedElementSubdomainID(connected_elem);
390 
391  if (curr_elem_subid < connected_elem_subid)
392  {
393  if (current_elem->has_neighbor(connected_elem))
394  {
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);
397 
398  // in this case we need to play a game to reorder the sides
399  // Ensure consistent ordering: (min, max) for block (subdomain) IDs
400  bool need_to_switch = false;
402  {
403  connected_elem_subid = connected_elem->subdomain_id();
404  if (curr_elem_subid > connected_elem_subid) // we need to switch the ids
405  {
406  connected_elem_subid = current_elem->subdomain_id();
407  curr_elem_subid = connected_elem->subdomain_id();
408 
409  side = connected_elem->which_neighbor_am_i(current_elem);
410 
411  connected_elem_side = current_elem->which_neighbor_am_i(connected_elem);
412  need_to_switch = true;
413  }
414  }
415 
416  std::pair<subdomain_id_type, subdomain_id_type> blocks_pair =
417  std::make_pair(curr_elem_subid, connected_elem_subid);
418 
419  std::pair<subdomain_id_type, subdomain_id_type> blocks_pair2 =
420  std::make_pair(connected_elem_subid, curr_elem_subid);
421 
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,
425  Elem * current_elem,
426  Elem * connected_elem,
427  unsigned int side,
428  unsigned int connected_elem_side,
429  bool need_to_switch)
430  {
431  _neighboring_block_list.insert(blocks_pair);
432  _subid_pairs_to_sides[blocks_pair].emplace(
433  !need_to_switch ? current_elem : connected_elem, side);
435  {
436  _neighboring_block_list.insert(blocks_pair2);
437  _subid_pairs_to_sides[blocks_pair2].emplace(
438  !need_to_switch ? connected_elem : current_elem, connected_elem_side);
439  }
440  };
441 
443  {
445  blockRestrictedElementSubdomainID(connected_elem)))
446  add_boundary_sides(blocks_pair,
447  blocks_pair2,
448  current_elem,
449  connected_elem,
450  side,
451  connected_elem_side,
452  need_to_switch);
453  }
454  else
455  add_boundary_sides(blocks_pair,
456  blocks_pair2,
457  current_elem,
458  connected_elem,
459  side,
460  connected_elem_side,
461  need_to_switch);
462  }
463  }
464  }
465  }
466 
467  } // end multiplicity check
468  } // end loop to duplicate nodes
469 
470  addInterface(*mesh);
471 
472  // We want to reset node processor ids, which may need to change
473  // because of elements being disconnected, but we may have
474  // completely disconnected some ghosted nodes on some processors
475  // from all ghosted elements that should own them, in which case
476  // libMesh on those processors may not be able to tell who to query
477  // for an authoritative node processor id. Let's just delete
478  // orphaned nodes so we don't end up with invalid_processor_id on
479  // some processors (confusing consistency checks).
480  if (!mesh->is_serial())
481  mesh->remove_orphaned_nodes();
482  Partitioner::set_node_processor_ids(*mesh);
483 
484  mesh->unset_is_prepared();
485 
486  return dynamic_pointer_cast<MeshBase>(mesh);
487 }
488 
489 void
491 {
492  BoundaryInfo & boundary_info = mesh.get_boundary_info();
493 
494  boundary_id_type boundary_id;
495  boundary_id_type boundary_id_interface = Moose::INVALID_BOUNDARY_ID;
496  boundary_id_type boundary_id_interface_transition = Moose::INVALID_BOUNDARY_ID;
497 
498  BoundaryName boundary_name;
499 
500  const std::set<boundary_id_type> & ids = boundary_info.get_boundary_ids();
501  boundary_id_type new_boundaryID = ids.empty() ? 0 : *ids.rbegin() + 1;
502 
503  // Make sure the new boundary ID is the same on every processor
504  mesh.comm().set_union(_neighboring_block_list);
505  mesh.comm().max(new_boundaryID);
506  mooseAssert(new_boundaryID >= 0, "Invalid new boundary ID computed.");
507 
508  // loop over boundary sides
509  // All ranks will process the pairs in exactly the same order.
510  std::vector<std::pair<subdomain_id_type, subdomain_id_type>> sorted_pairs(
512 
513  for (auto & boundary_side : sorted_pairs)
514  {
517  {
518  // find the appropriate boundary name and id
519  // given primary and secondary block ID
520  if (_split_interface)
521  {
522  boundary_id = new_boundaryID;
524  boundary_side.first,
525  boundary_side.second,
526  boundary_name,
527  boundary_id,
528  boundary_info);
529  }
530  else
531  {
532  // When _split_interface == false, all pairs share the same boundary_id_interface
533  // (This would cause self-pairing)
534  boundary_name = _interface_name;
535  // assign a unique boundary ID for the interface boundary
536  boundary_id_interface = boundary_id_interface == Moose::INVALID_BOUNDARY_ID
537  ? new_boundaryID
538  : boundary_id_interface;
539  boundary_id = boundary_id_interface;
540  boundary_info.sideset_name(boundary_id_interface) = boundary_name;
541  }
542  }
543  else // block resticted with transition boundary
544  {
545 
546  const bool interior_boundary = _block_set.find(boundary_side.first) != _block_set.end() &&
547  _block_set.find(boundary_side.second) != _block_set.end();
548 
549  if ((_split_interface && interior_boundary) ||
550  (_split_transition_interface && !interior_boundary))
551  {
552  boundary_id = new_boundaryID;
554  boundary_side.first,
555  boundary_side.second,
556  boundary_name,
557  boundary_id,
558  boundary_info);
559  }
560  else if (interior_boundary)
561  {
562  boundary_name = _interface_name;
563  // assign a unique boundary ID for the interface boundary
564  boundary_id_interface = boundary_id_interface == Moose::INVALID_BOUNDARY_ID
565  ? new_boundaryID
566  : boundary_id_interface;
567 
568  boundary_id = boundary_id_interface;
569  boundary_info.sideset_name(boundary_id_interface) = boundary_name;
570  }
571  else
572  {
573  // assign a unique boundary ID for the interface transition boundary
574  boundary_id_interface_transition =
575  boundary_id_interface_transition == Moose::INVALID_BOUNDARY_ID
576  ? new_boundaryID
577  : boundary_id_interface_transition;
578  boundary_id = boundary_id_interface_transition;
579  boundary_info.sideset_name(boundary_id) = _interface_transition_name;
580  }
581  }
582 
583  // Map the block pair (subdomain_id_A, subdomain_id_B) to the generated boundary_id
584  _subid_pairs_to_boundary_id[boundary_side] = boundary_id;
585 
586  // loop over all the side belonging to each pair and add it to the proper interface
587  auto boundary_side_map = _subid_pairs_to_sides.find(boundary_side);
588  if (boundary_side_map != _subid_pairs_to_sides.end())
589  for (const auto & [elem, side] : boundary_side_map->second)
590  boundary_info.add_side(elem, side, boundary_id);
591  new_boundaryID++;
592  }
593 
594  // Sync boundary info
595  boundary_info.parallel_sync_side_ids();
596  boundary_info.parallel_sync_node_ids();
597 
598  // Generate boundary_id pairs mapping based on _subid_pairs_to_sides
599  for (const auto & [sub_pair, boundary_id] : _subid_pairs_to_boundary_id)
600  {
601  const auto rev_pair = std::make_pair(sub_pair.second, sub_pair.first);
602  const bool has_reverse =
604  if (has_reverse)
605  // Normal disconnected boundary pair: blockA_blockB <-> blockB_blockA
606  mesh.add_disjoint_neighbor_boundary_pairs(
607  boundary_id, _subid_pairs_to_boundary_id[rev_pair], RealVectorValue(0.0, 0.0, 0.0));
608  }
609 }
610 
613 {
614  subdomain_id_type elem_subdomain_id = elem->subdomain_id();
616  (_block_set.find(elem_subdomain_id) == _block_set.end()))
617  elem_subdomain_id = Elem::invalid_subdomain_id;
618 
619  return elem_subdomain_id;
620 }
621 
622 bool
624 {
625  for (auto block_pair : _block_pairs)
626  if ((block_pair.first == block_one && block_pair.second == block_two) ||
627  (block_pair.first == block_two && block_pair.second == block_one))
628  return true;
629  return false;
630 }
631 
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)
636 {
637  std::unordered_map<dof_id_type, std::set<subdomain_id_type>> nodeid_to_connected_blocks;
638 
639  // Phase 0: Each rank computes its local connected blocks for nodes it holds
640  for (const auto & [node_id, elem_ids] : node_to_elem_map)
641  {
642  std::set<subdomain_id_type> connected_blocks;
643  for (const dof_id_type elem_id : elem_ids)
644  {
645  const Elem * current_elem = mesh.elem_ptr(elem_id);
646  subdomain_id_type block_id = blockRestrictedElementSubdomainID(current_elem);
647  nodeid_to_connected_blocks[node_id].insert(block_id);
648  }
649  }
650 
651  // No synchronization needed for replicated meshes
652  if (mesh.is_replicated())
653  return nodeid_to_connected_blocks;
654 
655  auto & comm = mesh.comm();
656  const auto mesh_pid = mesh.processor_id();
657 
658  // Phase 1: Ghost nodes push their connected blocks to the owner
659 
660  // (node_id, connected_blocks, ghost_pid)
661  using NodeConnectedBlocksTuple =
662  std::tuple<dof_id_type, std::vector<subdomain_id_type>, processor_id_type>;
663  std::map<processor_id_type, std::vector<NodeConnectedBlocksTuple>> to_owner;
664  for (const auto & [node_id, blocks] : nodeid_to_connected_blocks)
665  {
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);
670  }
671 
672  // Owner receives and merges ghost info into its local map, and tracks subscribers
673  std::unordered_map<dof_id_type, std::set<processor_id_type>> subscribers;
674  Parallel::push_parallel_vector_data(
675  comm,
676  to_owner,
677  [&](processor_id_type, const std::vector<NodeConnectedBlocksTuple> & recv_data)
678  {
679  for (const auto & [node_id, blocks_vec, ghost_pid] : recv_data)
680  {
681  subscribers[node_id].insert(ghost_pid);
682  nodeid_to_connected_blocks[node_id].insert(blocks_vec.begin(), blocks_vec.end());
683  }
684  });
685 
686  // Phase 2: Node owners broadcast complete connected blocks back to subscribers
687  std::map<processor_id_type, std::vector<NodeConnectedBlocksPair>> from_owner;
688  for (const auto & [node_id, sub_pids] : subscribers)
689  {
690  const Node * node = mesh.node_ptr(node_id);
691  if (node && node->processor_id() == mesh_pid)
692  {
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()));
697  }
698  }
699 
700  Parallel::push_parallel_vector_data(
701  comm,
702  from_owner,
703  [&](processor_id_type, const std::vector<NodeConnectedBlocksPair> & recv_data)
704  {
705  for (const auto & [node_id, blocks_vec] : recv_data)
706  nodeid_to_connected_blocks[node_id].insert(blocks_vec.begin(), blocks_vec.end());
707  });
708  return nodeid_to_connected_blocks;
709 }
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&#39;s parameters.
Definition: MooseBase.h:394
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition: MooseBase.h:467
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
Definition: MooseBase.h:416
char ** blocks
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
Definition: MooseTypes.C:22
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 ...
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
void addInterface(MeshBase &mesh)
generate the new boundary interface
MeshBase & mesh
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
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.
void addRelationshipManager(const std::string &name, Moose::RelationshipManagerType rm_type, Moose::RelationshipManagerInputParameterCallback input_parameter_callback=nullptr)
Tells MOOSE about a RelationshipManager that this object needs.
const bool _block_pairs_restricted
whether interfaces will be generated between block pairs
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
bool findBlockPairs(subdomain_id_type block_one, subdomain_id_type block_two)
Return true if block_one and block_two are found in users&#39; 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.
Definition: MooseBase.h:103
int8_t boundary_id_type
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()
Definition: MeshGenerator.C:23
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.
bool isParamSetByUser(const std::string &name) const
Method returns true if the parameter was set by the user.
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 &parameters)
const bool _add_interface_on_two_sides
whether to add two sides interface boundaries
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
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.
Definition: MeshGenerator.h:33
uint8_t dof_id_type