https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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{
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
98BoundaryName
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
113void
115 const std::string & boundaryName)
116{
117 _bName_bID_set.insert(std::pair<std::string, int>(boundaryName, boundaryID));
118}
119
120void
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
153std::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
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
489void
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
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
611subdomain_id_type
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
622bool
623BreakMeshByBlockGenerator::findBlockPairs(subdomain_id_type block_one, subdomain_id_type block_two)
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
632std::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)
char ** blocks
std::unordered_set< SubdomainID > _block_set
set of the blocks to split the mesh on
std::set< std::pair< std::string, BoundaryID > > _bName_bID_set
A container holding (boundary name, boundary ID) associations.
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.
BreakMeshByBlockGenerator(const InputParameters &parameters)
std::unique_ptr< MeshBase > & _input
the mesh to modify
const bool _surrounding_blocks_restricted
whether interfaces will be generated surrounding blocks
subdomain_id_type blockRestrictedElementSubdomainID(const Elem *elem)
This is a helper method to avoid recoding the same if everywhere.
BoundaryName generateBoundaryName(const MeshBase &mesh, subdomain_id_type primaryBlockID, subdomain_id_type secondaryBlockID)
this method generate the boundary name by assembling subdomain names
const bool _add_transition_interface
whether to add a boundary when splitting the mesh
std::unordered_map< SubdomainPair, std::set< ElemSide > > _subid_pairs_to_sides
Map from a pair of block ids to a set of ElemSide tuples.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
const bool _split_transition_interface
whether to split the transition boundary between the blocks and the rest of the mesh
const BoundaryName _interface_transition_name
the name of the transition interface
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
static InputParameters validParams()
bool _split_interface
the flag to split the interface by block
std::unordered_set< std::pair< SubdomainID, SubdomainID > > _block_pairs
set of subdomain pairs between which interfaces will be generated.
void addInterface(MeshBase &mesh)
generate the new boundary interface
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.
std::set< SubdomainPair > _neighboring_block_list
Set of pairs of block ids between which new boundary sides are created.
const bool _block_pairs_restricted
whether interfaces will be generated between block pairs
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.
void mapBoundaryIdAndBoundaryName(boundary_id_type boundaryID, const std::string &boundaryName)
this method save the boundary name/id pair
std::string _interface_name
the name of the new interface
const bool _add_interface_on_two_sides
whether to add two sides interface boundaries
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
bool isParamSetByUser(const std::string &name) const
Method returns true if the parameter was set by the user.
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.
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...
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.
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.
T & set(const std::string &name, bool quiet_mode=false)
Returns a writable reference to the named parameters.
MeshGenerators are objects that can modify or add to an existing mesh.
static InputParameters validParams()
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
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:457
const InputParameters & _pars
The object's parameters.
Definition MooseBase.h:384
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
Definition MooseBase.h:406
const Parallel::Communicator & comm() const
MeshBase & mesh
bool hasSubdomainName(const MeshBase &input_mesh, const SubdomainName &name)
Whether a particular subdomain name exists in the mesh.
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 BoundaryID INVALID_BOUNDARY_ID
Definition MooseTypes.C:22