Line data Source code
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 :
10 : #include "BreakMeshByBlockGenerator.h"
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 :
19 : registerMooseObject("MooseApp", BreakMeshByBlockGenerator);
20 :
21 : InputParameters
22 3641 : BreakMeshByBlockGenerator::validParams()
23 : {
24 3641 : InputParameters params = MeshGenerator::validParams();
25 7282 : 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 14564 : params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
30 :
31 14564 : params.addParam<std::string>(
32 : "interface_name",
33 : "interface",
34 : "the name of the new interface. Cannot be used with `split_interface=true`");
35 10923 : params.addParam<bool>("split_interface",
36 7282 : true,
37 : "If true, it creates a "
38 : "different interface for each block pair.");
39 14564 : params.addParam<std::vector<SubdomainName>>(
40 : "surrounding_blocks",
41 : "The list of subdomain names surrounding which interfaces will be generated.");
42 14564 : params.addParam<std::vector<std::vector<SubdomainName>>>(
43 : "block_pairs", "The list of subdomain pairs between which interfaces will be generated.");
44 10923 : params.addParam<bool>("add_transition_interface",
45 7282 : 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 10923 : params.addParam<bool>(
49 7282 : "split_transition_interface", false, "Whether to split the transition interface by blocks.");
50 10923 : params.addParam<bool>("add_interface_on_two_sides",
51 7282 : true,
52 : "Whether to add an additional interface boundary at the other side.");
53 10923 : 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 10923 : params.addRelationshipManager("ElementSideNeighborLayers",
59 : Moose::RelationshipManagerType::GEOMETRIC,
60 492 : [](const InputParameters &, InputParameters & rm_params)
61 984 : { rm_params.set<unsigned short>("layers") = 1; });
62 :
63 3641 : return params;
64 0 : }
65 :
66 290 : BreakMeshByBlockGenerator::BreakMeshByBlockGenerator(const InputParameters & parameters)
67 : : MeshGenerator(parameters),
68 580 : _input(getMesh("input")),
69 580 : _interface_name(getParam<std::string>("interface_name")),
70 580 : _split_interface(getParam<bool>("split_interface")),
71 580 : _block_pairs_restricted(parameters.isParamSetByUser("block_pairs")),
72 290 : _surrounding_blocks_restricted(parameters.isParamSetByUser("surrounding_blocks")),
73 580 : _add_transition_interface(getParam<bool>("add_transition_interface")),
74 580 : _split_transition_interface(getParam<bool>("split_transition_interface")),
75 580 : _interface_transition_name(getParam<BoundaryName>("interface_transition_name")),
76 1450 : _add_interface_on_two_sides(getParam<bool>("add_interface_on_two_sides"))
77 : {
78 1260 : if (getParam<bool>("split_interface") && _pars.isParamSetByUser("interface_name"))
79 0 : paramError("interface_name",
80 : "if split_interface == true, the new interface_name cannot be specified by the "
81 : "user. It will be automatically assigned");
82 290 : if (_block_pairs_restricted && _surrounding_blocks_restricted)
83 0 : paramError("block_pairs_restricted",
84 : "BreakMeshByBlockGenerator: 'surrounding_blocks' and 'block_pairs' can not be used "
85 : "at the same time.");
86 :
87 290 : if (!_add_transition_interface && _split_transition_interface)
88 0 : paramError("split_transition_interface",
89 : "BreakMeshByBlockGenerator cannot split the transition interface because "
90 : "add_transition_interface is false");
91 :
92 290 : if (_add_transition_interface && _block_pairs_restricted)
93 0 : paramError("add_transition_interface",
94 : "BreakMeshByBlockGenerator cannot split the transition interface when 'block_pairs' "
95 : "are specified.");
96 290 : }
97 :
98 : BoundaryName
99 3654 : BreakMeshByBlockGenerator::generateBoundaryName(const MeshBase & mesh,
100 : subdomain_id_type primaryBlockID,
101 : subdomain_id_type secondaryBlockID)
102 : {
103 3654 : std::string primary_block_name = mesh.subdomain_name(primaryBlockID);
104 3654 : std::string secondary_block_name = mesh.subdomain_name(secondaryBlockID);
105 3654 : if (primary_block_name.empty())
106 3654 : primary_block_name = "Block" + std::to_string(primaryBlockID);
107 3654 : if (secondary_block_name.empty())
108 3654 : secondary_block_name = "Block" + std::to_string(secondaryBlockID);
109 :
110 7308 : return primary_block_name + "_" + secondary_block_name;
111 3654 : }
112 :
113 : void
114 3654 : BreakMeshByBlockGenerator::mapBoundaryIdAndBoundaryName(boundary_id_type boundaryID,
115 : const std::string & boundaryName)
116 : {
117 3654 : _bName_bID_set.insert(std::pair<std::string, int>(boundaryName, boundaryID));
118 3654 : }
119 :
120 : void
121 3654 : BreakMeshByBlockGenerator::findBoundaryName(const MeshBase & mesh,
122 : subdomain_id_type primaryBlockID,
123 : subdomain_id_type secondaryBlockID,
124 : BoundaryName & boundaryName,
125 : boundary_id_type boundaryID,
126 : BoundaryInfo & boundary_info)
127 : {
128 3654 : boundaryName = generateBoundaryName(mesh, primaryBlockID, secondaryBlockID);
129 :
130 : // check if the boundary name already exist
131 3654 : bool checkBoundaryAlreadyExist = false;
132 122136 : for (auto b : _bName_bID_set)
133 : {
134 118482 : if (b.first.compare(boundaryName) == 0)
135 : {
136 : mooseAssert(boundaryID == b.second, "Boundary with two inconsistent ids");
137 0 : checkBoundaryAlreadyExist = true;
138 : }
139 118482 : }
140 :
141 3654 : if (checkBoundaryAlreadyExist)
142 0 : return;
143 : else
144 : {
145 3654 : mapBoundaryIdAndBoundaryName(boundaryID, boundaryName);
146 :
147 3654 : boundary_info.sideset_name(boundaryID) = boundaryName;
148 :
149 3654 : return;
150 : }
151 : }
152 :
153 : std::unique_ptr<MeshBase>
154 290 : BreakMeshByBlockGenerator::generate()
155 : {
156 290 : std::unique_ptr<MeshBase> mesh = std::move(_input);
157 :
158 : // Make sure subdomain caches are up to date
159 290 : if (!mesh->preparation().has_cached_elem_data)
160 121 : mesh->cache_elem_data();
161 :
162 : // Max node id is used later to generate new unique node IDs
163 290 : 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 290 : if (!mesh->is_replicated() && !mesh->is_prepared())
167 18 : mesh->comm().max(max_node_id);
168 : #if LIBMESH_ENABLE_UNIQUE_ID
169 290 : const auto max_unique_id = mesh->parallel_max_unique_id();
170 : #endif
171 :
172 290 : BoundaryInfo & boundary_info = mesh->get_boundary_info();
173 :
174 : // Handle block restrictions
175 290 : if (_block_pairs_restricted)
176 : {
177 76 : for (const auto & block_name_pair :
178 407 : getParam<std::vector<std::vector<SubdomainName>>>("block_pairs"))
179 : {
180 106 : if (block_name_pair.size() != 2)
181 0 : 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 315 : for (const auto & name : block_name_pair)
186 212 : if (!MooseMeshUtils::hasSubdomainName(*mesh, name))
187 6 : paramError("block_pairs", "The block '", name, "' was not found in the mesh");
188 :
189 103 : const auto block_pair = MooseMeshUtils::getSubdomainIDs(*mesh, block_name_pair);
190 206 : std::pair<SubdomainID, SubdomainID> pair = std::make_pair(
191 206 : std::min(block_pair[0], block_pair[1]), std::max(block_pair[0], block_pair[1]));
192 :
193 103 : _block_pairs.insert(pair);
194 : // Insert all SubdomainIDs from block_pair into _block_set (duplicates automatically
195 : // ignored)
196 103 : std::copy(block_pair.begin(), block_pair.end(), std::inserter(_block_set, _block_set.end()));
197 103 : }
198 : }
199 214 : else if (_surrounding_blocks_restricted)
200 : {
201 : // check that the blocks exist in the mesh
202 1709 : for (const auto & name : getParam<std::vector<SubdomainName>>("surrounding_blocks"))
203 1553 : if (!MooseMeshUtils::hasSubdomainName(*mesh, name))
204 6 : paramError("surrounding_blocks", "The block '", name, "' was not found in the mesh");
205 :
206 : const auto surrounding_block_ids = MooseMeshUtils::getSubdomainIDs(
207 100 : *mesh, getParam<std::vector<SubdomainName>>("surrounding_blocks"));
208 100 : std::copy(surrounding_block_ids.begin(),
209 : surrounding_block_ids.end(),
210 50 : std::inserter(_block_set, _block_set.end()));
211 50 : }
212 :
213 : // check that a boundary named _interface_transition_name does not already exist in the mesh
214 314 : if ((_block_pairs_restricted || _surrounding_blocks_restricted) && _add_transition_interface &&
215 30 : boundary_info.get_id_by_name(_interface_transition_name) != Moose::INVALID_BOUNDARY_ID)
216 0 : paramError("interface_transition_name",
217 : "BreakMeshByBlockGenerator the specified interface transition boundary name "
218 : "already exits");
219 :
220 : // initialize the node to element map
221 284 : std::unordered_map<dof_id_type, std::vector<dof_id_type>> node_to_elem_map;
222 21922 : for (const auto & elem : mesh->active_element_ptr_range())
223 125653 : for (const auto n : make_range(elem->n_nodes()))
224 104299 : 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 284 : const auto nodeid_to_connected_blocks = syncConnectedBlocks(node_to_elem_map, *mesh);
229 :
230 : // Main loop to duplicate nodes
231 19359 : for (const auto & [current_node_id, connected_elems] : node_to_elem_map)
232 : {
233 19075 : const auto * current_node = mesh->node_ptr(current_node_id);
234 19075 : if (!current_node)
235 0 : continue;
236 :
237 : // If the node is not connected to any blocks, skip it
238 19075 : auto it = nodeid_to_connected_blocks.find(current_node_id);
239 19075 : if (it == nodeid_to_connected_blocks.end())
240 0 : 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 19075 : auto connected_blocks = it->second;
247 :
248 : // Remove invalid subdomain if block pairs restriction is active
249 19075 : if (_block_pairs_restricted)
250 2307 : connected_blocks.erase(Elem::invalid_subdomain_id);
251 :
252 19075 : const unsigned int node_multiplicity = connected_blocks.size();
253 :
254 : // check if current_node need to be duplicated
255 19075 : 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 9226 : auto subdomain_it = connected_blocks.begin();
260 : subdomain_id_type reference_subdomain_id =
261 17876 : !_block_pairs_restricted &&
262 8650 : connected_blocks.find(Elem::invalid_subdomain_id) != connected_blocks.end()
263 18452 : ? Elem::invalid_subdomain_id
264 6626 : : *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 9226 : bool should_create_new_node = true;
269 9226 : if (_block_pairs_restricted)
270 : {
271 : // Default to false; only set to true if this is exactly the pair boundary we want
272 576 : 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 576 : 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 576 : if (sets_blocks_for_this_node.size() == 2)
283 : {
284 : // Get the two block IDs from the set
285 513 : auto set_blocks_for_this_node_it = sets_blocks_for_this_node.begin();
286 513 : subdomain_id_type block1 = *set_blocks_for_this_node_it;
287 513 : 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 513 : if (findBlockPairs(block1, block2))
291 443 : should_create_new_node = true;
292 : }
293 : }
294 :
295 : // multiplicity counter to keep track of how many nodes we added
296 9226 : unsigned int multiplicity_counter = node_multiplicity;
297 71359 : for (auto elem_id : connected_elems)
298 : {
299 : // all the duplicate nodes are added and assigned
300 62146 : if (multiplicity_counter == 0)
301 13 : break;
302 :
303 62133 : Elem * current_elem = mesh->elem_ptr(elem_id);
304 62133 : 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 89102 : if ((block_id != reference_subdomain_id) ||
308 26969 : (_block_pairs_restricted && findBlockPairs(reference_subdomain_id, block_id)))
309 : {
310 : // assign the newly added node to current_elem
311 35164 : Node * new_node = nullptr;
312 :
313 35164 : std::vector<boundary_id_type> node_boundary_ids;
314 :
315 167595 : for (const auto local_node_id : make_range(current_elem->n_nodes()))
316 150695 : if (current_elem->node_id(local_node_id) ==
317 150695 : current_node->id()) // if current node == node on element
318 : {
319 18264 : 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 36116 : new_node = Node::build(*current_node,
327 18058 : mesh->is_replicated()
328 15226 : ? mesh->n_nodes()
329 2832 : : (current_elem->subdomain_id() + 1) * max_node_id +
330 2832 : current_node->id())
331 18058 : .release();
332 : #if LIBMESH_ENABLE_UNIQUE_ID
333 18058 : new_node->set_unique_id((current_elem->subdomain_id() + 1) * max_unique_id +
334 18058 : 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 18058 : new_node->processor_id() = current_elem->processor_id();
340 18058 : mesh->add_node(new_node);
341 18058 : current_elem->set_node(local_node_id, new_node);
342 : // Add boundary info to the new node
343 18058 : boundary_info.boundary_ids(current_node, node_boundary_ids);
344 18058 : boundary_info.add_node(new_node, node_boundary_ids);
345 : }
346 18264 : multiplicity_counter--; // node created, update multiplicity counter
347 18264 : break; // once the proper node has been fixed in one element we can break the
348 : // loop
349 : }
350 :
351 35164 : if (should_create_new_node)
352 : {
353 493159 : for (auto connected_elem_id : connected_elems)
354 : {
355 458201 : 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 458201 : if (connected_elem->subdomain_id() == current_elem->subdomain_id() &&
360 : connected_elem != current_elem)
361 : {
362 745183 : for (const auto local_node_id : make_range(connected_elem->n_nodes()))
363 607055 : if (connected_elem->node_id(local_node_id) ==
364 607055 : current_node->id()) // if current node == node on element
365 : {
366 16900 : connected_elem->set_node(local_node_id, new_node);
367 16900 : break;
368 : }
369 : }
370 : }
371 : }
372 35164 : }
373 : }
374 :
375 : // create blocks pair and assign element side to new interface boundary map
376 71380 : for (auto elem_id : connected_elems)
377 : {
378 872650 : for (auto connected_elem_id : connected_elems)
379 : {
380 810496 : Elem * current_elem = mesh->elem_ptr(elem_id);
381 810496 : Elem * connected_elem = mesh->elem_ptr(connected_elem_id);
382 :
383 : // Early out if both elements are the same
384 810496 : if (current_elem == connected_elem)
385 62154 : continue;
386 :
387 748342 : subdomain_id_type curr_elem_subid = blockRestrictedElementSubdomainID(current_elem);
388 : subdomain_id_type connected_elem_subid =
389 748342 : blockRestrictedElementSubdomainID(connected_elem);
390 :
391 748342 : if (curr_elem_subid < connected_elem_subid)
392 : {
393 227305 : if (current_elem->has_neighbor(connected_elem))
394 : {
395 35588 : unsigned int side = current_elem->which_neighbor_am_i(connected_elem);
396 35588 : 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 35588 : bool need_to_switch = false;
401 35588 : if (_block_pairs_restricted || _surrounding_blocks_restricted)
402 : {
403 19124 : connected_elem_subid = connected_elem->subdomain_id();
404 19124 : if (curr_elem_subid > connected_elem_subid) // we need to switch the ids
405 : {
406 3466 : connected_elem_subid = current_elem->subdomain_id();
407 3466 : curr_elem_subid = connected_elem->subdomain_id();
408 :
409 3466 : side = connected_elem->which_neighbor_am_i(current_elem);
410 :
411 3466 : connected_elem_side = current_elem->which_neighbor_am_i(connected_elem);
412 3466 : need_to_switch = true;
413 : }
414 : }
415 :
416 : std::pair<subdomain_id_type, subdomain_id_type> blocks_pair =
417 35588 : std::make_pair(curr_elem_subid, connected_elem_subid);
418 :
419 : std::pair<subdomain_id_type, subdomain_id_type> blocks_pair2 =
420 35588 : std::make_pair(connected_elem_subid, curr_elem_subid);
421 :
422 : auto add_boundary_sides =
423 35382 : [&](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 35382 : _neighboring_block_list.insert(blocks_pair);
432 35382 : _subid_pairs_to_sides[blocks_pair].emplace(
433 : !need_to_switch ? current_elem : connected_elem, side);
434 35382 : if (_add_interface_on_two_sides)
435 : {
436 851 : _neighboring_block_list.insert(blocks_pair2);
437 851 : _subid_pairs_to_sides[blocks_pair2].emplace(
438 : !need_to_switch ? connected_elem : current_elem, connected_elem_side);
439 : }
440 70970 : };
441 :
442 35588 : if (_block_pairs_restricted)
443 : {
444 1164 : if (findBlockPairs(blockRestrictedElementSubdomainID(current_elem),
445 1164 : blockRestrictedElementSubdomainID(connected_elem)))
446 958 : 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 34424 : 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 19075 : } // end loop to duplicate nodes
469 :
470 284 : 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 284 : if (!mesh->is_serial())
481 26 : mesh->remove_orphaned_nodes();
482 284 : Partitioner::set_node_processor_ids(*mesh);
483 :
484 284 : mesh->unset_is_prepared();
485 :
486 568 : return dynamic_pointer_cast<MeshBase>(mesh);
487 284 : }
488 :
489 : void
490 284 : BreakMeshByBlockGenerator::addInterface(MeshBase & mesh)
491 : {
492 284 : BoundaryInfo & boundary_info = mesh.get_boundary_info();
493 :
494 : boundary_id_type boundary_id;
495 284 : boundary_id_type boundary_id_interface = Moose::INVALID_BOUNDARY_ID;
496 284 : boundary_id_type boundary_id_interface_transition = Moose::INVALID_BOUNDARY_ID;
497 :
498 284 : BoundaryName boundary_name;
499 :
500 284 : const std::set<boundary_id_type> & ids = boundary_info.get_boundary_ids();
501 284 : 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 284 : mesh.comm().set_union(_neighboring_block_list);
505 284 : 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(
511 284 : _neighboring_block_list.begin(), _neighboring_block_list.end());
512 :
513 6270 : for (auto & boundary_side : sorted_pairs)
514 : {
515 5986 : if (!(_block_pairs_restricted || _surrounding_blocks_restricted) ||
516 4896 : ((_block_pairs_restricted || _surrounding_blocks_restricted) && !_add_transition_interface))
517 : {
518 : // find the appropriate boundary name and id
519 : // given primary and secondary block ID
520 3136 : if (_split_interface)
521 : {
522 1754 : boundary_id = new_boundaryID;
523 1754 : findBoundaryName(mesh,
524 1754 : boundary_side.first,
525 1754 : 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 1382 : boundary_name = _interface_name;
535 : // assign a unique boundary ID for the interface boundary
536 2764 : boundary_id_interface = boundary_id_interface == Moose::INVALID_BOUNDARY_ID
537 1382 : ? new_boundaryID
538 : : boundary_id_interface;
539 1382 : boundary_id = boundary_id_interface;
540 1382 : boundary_info.sideset_name(boundary_id_interface) = boundary_name;
541 : }
542 : }
543 : else // block resticted with transition boundary
544 : {
545 :
546 5160 : const bool interior_boundary = _block_set.find(boundary_side.first) != _block_set.end() &&
547 2310 : _block_set.find(boundary_side.second) != _block_set.end();
548 :
549 2850 : if ((_split_interface && interior_boundary) ||
550 1650 : (_split_transition_interface && !interior_boundary))
551 : {
552 1900 : boundary_id = new_boundaryID;
553 1900 : findBoundaryName(mesh,
554 1900 : boundary_side.first,
555 1900 : boundary_side.second,
556 : boundary_name,
557 : boundary_id,
558 : boundary_info);
559 : }
560 950 : else if (interior_boundary)
561 : {
562 600 : boundary_name = _interface_name;
563 : // assign a unique boundary ID for the interface boundary
564 1200 : boundary_id_interface = boundary_id_interface == Moose::INVALID_BOUNDARY_ID
565 600 : ? new_boundaryID
566 : : boundary_id_interface;
567 :
568 600 : boundary_id = boundary_id_interface;
569 600 : 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 350 : boundary_id_interface_transition =
575 350 : boundary_id_interface_transition == Moose::INVALID_BOUNDARY_ID
576 350 : ? new_boundaryID
577 : : boundary_id_interface_transition;
578 350 : boundary_id = boundary_id_interface_transition;
579 350 : 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 5986 : _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 5986 : auto boundary_side_map = _subid_pairs_to_sides.find(boundary_side);
588 5986 : if (boundary_side_map != _subid_pairs_to_sides.end())
589 15949 : for (const auto & [elem, side] : boundary_side_map->second)
590 10254 : boundary_info.add_side(elem, side, boundary_id);
591 5986 : new_boundaryID++;
592 : }
593 :
594 : // Sync boundary info
595 284 : boundary_info.parallel_sync_side_ids();
596 284 : boundary_info.parallel_sync_node_ids();
597 :
598 : // Generate boundary_id pairs mapping based on _subid_pairs_to_sides
599 6270 : for (const auto & [sub_pair, boundary_id] : _subid_pairs_to_boundary_id)
600 : {
601 5986 : const auto rev_pair = std::make_pair(sub_pair.second, sub_pair.first);
602 : const bool has_reverse =
603 5986 : _subid_pairs_to_boundary_id.find(rev_pair) != _subid_pairs_to_boundary_id.end();
604 5986 : if (has_reverse)
605 : // Normal disconnected boundary pair: blockA_blockB <-> blockB_blockA
606 144 : mesh.add_disjoint_neighbor_boundary_pairs(
607 144 : boundary_id, _subid_pairs_to_boundary_id[rev_pair], RealVectorValue(0.0, 0.0, 0.0));
608 : }
609 284 : }
610 :
611 : subdomain_id_type
612 1665160 : BreakMeshByBlockGenerator::blockRestrictedElementSubdomainID(const Elem * elem)
613 : {
614 1665160 : subdomain_id_type elem_subdomain_id = elem->subdomain_id();
615 1922309 : if ((_block_pairs_restricted || _surrounding_blocks_restricted) &&
616 1922309 : (_block_set.find(elem_subdomain_id) == _block_set.end()))
617 85426 : elem_subdomain_id = Elem::invalid_subdomain_id;
618 :
619 1665160 : return elem_subdomain_id;
620 : }
621 :
622 : bool
623 2781 : BreakMeshByBlockGenerator::findBlockPairs(subdomain_id_type block_one, subdomain_id_type block_two)
624 : {
625 4735 : for (auto block_pair : _block_pairs)
626 3355 : if ((block_pair.first == block_one && block_pair.second == block_two) ||
627 1954 : (block_pair.first == block_two && block_pair.second == block_one))
628 1401 : return true;
629 1380 : return false;
630 : }
631 :
632 : std::unordered_map<dof_id_type, std::set<subdomain_id_type>>
633 284 : BreakMeshByBlockGenerator::syncConnectedBlocks(
634 : const std::unordered_map<dof_id_type, std::vector<dof_id_type>> & node_to_elem_map,
635 : const MeshBase & mesh)
636 : {
637 284 : 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 19359 : for (const auto & [node_id, elem_ids] : node_to_elem_map)
641 : {
642 19075 : std::set<subdomain_id_type> connected_blocks;
643 123090 : for (const dof_id_type elem_id : elem_ids)
644 : {
645 104015 : const Elem * current_elem = mesh.elem_ptr(elem_id);
646 104015 : subdomain_id_type block_id = blockRestrictedElementSubdomainID(current_elem);
647 104015 : nodeid_to_connected_blocks[node_id].insert(block_id);
648 : }
649 19075 : }
650 :
651 : // No synchronization needed for replicated meshes
652 284 : if (mesh.is_replicated())
653 234 : return nodeid_to_connected_blocks;
654 :
655 50 : auto & comm = mesh.comm();
656 50 : 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 50 : std::map<processor_id_type, std::vector<NodeConnectedBlocksTuple>> to_owner;
664 2901 : for (const auto & [node_id, blocks] : nodeid_to_connected_blocks)
665 : {
666 2851 : const Node * node = mesh.node_ptr(node_id);
667 2851 : if (node && node->processor_id() != mesh_pid)
668 2802 : to_owner[node->processor_id()].emplace_back(
669 4203 : 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 50 : std::unordered_map<dof_id_type, std::set<processor_id_type>> subscribers;
674 50 : Parallel::push_parallel_vector_data(
675 : comm,
676 : to_owner,
677 50 : [&](processor_id_type, const std::vector<NodeConnectedBlocksTuple> & recv_data)
678 : {
679 1571 : for (const auto & [node_id, blocks_vec, ghost_pid] : recv_data)
680 : {
681 1401 : subscribers[node_id].insert(ghost_pid);
682 1401 : nodeid_to_connected_blocks[node_id].insert(blocks_vec.begin(), blocks_vec.end());
683 : }
684 170 : });
685 :
686 : // Phase 2: Node owners broadcast complete connected blocks back to subscribers
687 50 : std::map<processor_id_type, std::vector<NodeConnectedBlocksPair>> from_owner;
688 851 : for (const auto & [node_id, sub_pids] : subscribers)
689 : {
690 801 : const Node * node = mesh.node_ptr(node_id);
691 801 : if (node && node->processor_id() == mesh_pid)
692 : {
693 801 : const auto & blocks_set = libmesh_map_find(nodeid_to_connected_blocks, node_id);
694 2202 : for (const auto pid : sub_pids)
695 2802 : from_owner[pid].emplace_back(
696 4203 : node_id, std::vector<subdomain_id_type>(blocks_set.begin(), blocks_set.end()));
697 : }
698 : }
699 :
700 50 : Parallel::push_parallel_vector_data(
701 : comm,
702 : from_owner,
703 50 : [&](processor_id_type, const std::vector<NodeConnectedBlocksPair> & recv_data)
704 : {
705 1571 : for (const auto & [node_id, blocks_vec] : recv_data)
706 1401 : nodeid_to_connected_blocks[node_id].insert(blocks_vec.begin(), blocks_vec.end());
707 170 : });
708 50 : return nodeid_to_connected_blocks;
709 50 : }
|