LCOV - code coverage report
Current view: top level - src/meshgenerators - BreakMeshByBlockGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 209 218 95.9 %
Date: 2025-08-08 20:01:16 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          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 <typeinfo>
      18             : 
      19             : registerMooseObject("MooseApp", BreakMeshByBlockGenerator);
      20             : 
      21             : InputParameters
      22       14801 : BreakMeshByBlockGenerator::validParams()
      23             : {
      24       14801 :   InputParameters params = BreakMeshByBlockGeneratorBase::validParams();
      25       14801 :   params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
      26       14801 :   params.addClassDescription(
      27             :       "Break the mesh at interfaces between blocks. New nodes will be generated so elements on "
      28             :       "each side of the break are no longer connected. At the moment, this only works on a "
      29             :       "REPLICATED mesh");
      30       14801 :   params.addParam<std::vector<SubdomainName>>(
      31             :       "surrounding_blocks",
      32             :       "The list of subdomain names surrounding which interfaces will be generated.");
      33       14801 :   params.addParam<std::vector<std::vector<SubdomainName>>>(
      34             :       "block_pairs", "The list of subdomain pairs between which interfaces will be generated.");
      35       44403 :   params.addParam<bool>("add_transition_interface",
      36       29602 :                         false,
      37             :                         "If true and block is not empty, a special boundary named "
      38             :                         "interface_transition is generate between listed blocks and other blocks.");
      39       44403 :   params.addParam<bool>(
      40       29602 :       "split_transition_interface", false, "Whether to split the transition interface by blocks.");
      41       44403 :   params.addParam<bool>("add_interface_on_two_sides",
      42       29602 :                         false,
      43             :                         "Whether to add an additional interface boundary at the other side.");
      44       14801 :   params.addParam<BoundaryName>(
      45             :       "interface_transition_name",
      46             :       "interface_transition",
      47             :       "the name of the interface transition boundary created when blocks are provided");
      48       14801 :   return params;
      49           0 : }
      50             : 
      51         268 : BreakMeshByBlockGenerator::BreakMeshByBlockGenerator(const InputParameters & parameters)
      52             :   : BreakMeshByBlockGeneratorBase(parameters),
      53         268 :     _input(getMesh("input")),
      54         268 :     _block_pairs_restricted(parameters.isParamSetByUser("block_pairs")),
      55         268 :     _surrounding_blocks_restricted(parameters.isParamSetByUser("surrounding_blocks")),
      56         268 :     _add_transition_interface(getParam<bool>("add_transition_interface")),
      57         268 :     _split_transition_interface(getParam<bool>("split_transition_interface")),
      58         268 :     _interface_transition_name(getParam<BoundaryName>("interface_transition_name")),
      59         804 :     _add_interface_on_two_sides(getParam<bool>("add_interface_on_two_sides"))
      60             : {
      61         268 :   if (_block_pairs_restricted && _surrounding_blocks_restricted)
      62           0 :     paramError("block_pairs_restricted",
      63             :                "BreakMeshByBlockGenerator: 'surrounding_blocks' and 'block_pairs' can not be used "
      64             :                "at the same time.");
      65             : 
      66         268 :   if (!_add_transition_interface && _split_transition_interface)
      67           0 :     paramError("split_transition_interface",
      68             :                "BreakMeshByBlockGenerator cannot split the transition interface because "
      69             :                "add_transition_interface is false");
      70             : 
      71         268 :   if (_add_transition_interface && _block_pairs_restricted)
      72           0 :     paramError("add_transition_interface",
      73             :                "BreakMeshByBlockGenerator cannot split the transition interface when 'block_pairs' "
      74             :                "are specified.");
      75         268 : }
      76             : 
      77             : std::unique_ptr<MeshBase>
      78         268 : BreakMeshByBlockGenerator::generate()
      79             : {
      80         268 :   std::unique_ptr<MeshBase> mesh = std::move(_input);
      81         268 :   if (!mesh->is_replicated())
      82           0 :     mooseError("BreakMeshByBlockGenerator is not implemented for distributed meshes");
      83             : 
      84             :   // Try to trick the rest of the world into thinking we're prepared
      85         268 :   mesh->prepare_for_use();
      86             : 
      87         268 :   BoundaryInfo & boundary_info = mesh->get_boundary_info();
      88             : 
      89             :   // Handle block restrictions
      90         268 :   if (_block_pairs_restricted)
      91             :   {
      92          67 :     for (const auto & block_name_pair :
      93         224 :          getParam<std::vector<std::vector<SubdomainName>>>("block_pairs"))
      94             :     {
      95          94 :       if (block_name_pair.size() != 2)
      96           0 :         paramError("block_pairs",
      97             :                    "Each row of 'block_pairs' must have a size of two (block names).");
      98             : 
      99             :       // check that the blocks exist in the mesh
     100         278 :       for (const auto & name : block_name_pair)
     101         188 :         if (!MooseMeshUtils::hasSubdomainName(*mesh, name))
     102           4 :           paramError("block_pairs", "The block '", name, "' was not found in the mesh");
     103             : 
     104          90 :       const auto block_pair = MooseMeshUtils::getSubdomainIDs(*mesh, block_name_pair);
     105         180 :       std::pair<SubdomainID, SubdomainID> pair = std::make_pair(
     106         180 :           std::min(block_pair[0], block_pair[1]), std::max(block_pair[0], block_pair[1]));
     107             : 
     108          90 :       _block_pairs.insert(pair);
     109          90 :       std::copy(block_pair.begin(), block_pair.end(), std::inserter(_block_set, _block_set.end()));
     110          90 :     }
     111             :   }
     112         201 :   else if (_surrounding_blocks_restricted)
     113             :   {
     114             :     // check that the blocks exist in the mesh
     115        1444 :     for (const auto & name : getParam<std::vector<SubdomainName>>("surrounding_blocks"))
     116        1399 :       if (!MooseMeshUtils::hasSubdomainName(*mesh, name))
     117           4 :         paramError("surrounding_blocks", "The block '", name, "' was not found in the mesh");
     118             : 
     119             :     const auto surrounding_block_ids = MooseMeshUtils::getSubdomainIDs(
     120          45 :         *mesh, getParam<std::vector<SubdomainName>>("surrounding_blocks"));
     121          90 :     std::copy(surrounding_block_ids.begin(),
     122             :               surrounding_block_ids.end(),
     123          45 :               std::inserter(_block_set, _block_set.end()));
     124          45 :   }
     125             : 
     126             :   // check that a boundary named _interface_transition_name does not already exist in the mesh
     127         287 :   if ((_block_pairs_restricted || _surrounding_blocks_restricted) && _add_transition_interface &&
     128          27 :       boundary_info.get_id_by_name(_interface_transition_name) != Moose::INVALID_BOUNDARY_ID)
     129           0 :     paramError("interface_transition_name",
     130             :                "BreakMeshByBlockGenerator the specified  interface transition boundary name "
     131             :                "already exits");
     132             : 
     133             :   // initialize the node to element map
     134         260 :   std::map<dof_id_type, std::vector<dof_id_type>> node_to_elem_map;
     135       41874 :   for (const auto & elem : mesh->active_element_ptr_range())
     136      120642 :     for (unsigned int n = 0; n < elem->n_nodes(); n++)
     137      100095 :       node_to_elem_map[elem->node_id(n)].push_back(elem->id());
     138             : 
     139       18429 :   for (auto node_it = node_to_elem_map.begin(); node_it != node_to_elem_map.end(); ++node_it)
     140             :   {
     141       18169 :     const dof_id_type current_node_id = node_it->first;
     142       18169 :     const Node * current_node = mesh->node_ptr(current_node_id);
     143             : 
     144       18169 :     if (current_node != nullptr)
     145             :     {
     146             :       // find node multiplicity
     147       18169 :       std::set<subdomain_id_type> connected_blocks;
     148      118004 :       for (auto elem_id = node_it->second.begin(); elem_id != node_it->second.end(); elem_id++)
     149             :       {
     150       99835 :         const Elem * current_elem = mesh->elem_ptr(*elem_id);
     151       99835 :         subdomain_id_type block_id = blockRestrictedElementSubdomainID(current_elem);
     152       99835 :         if (!_block_pairs_restricted)
     153       93067 :           connected_blocks.insert(block_id);
     154             :         else
     155             :         {
     156        6768 :           if (block_id != Elem::invalid_subdomain_id)
     157        6480 :             connected_blocks.insert(block_id);
     158             :         }
     159             :       }
     160             : 
     161       18169 :       unsigned int node_multiplicity = connected_blocks.size();
     162             : 
     163             :       // check if current_node need to be duplicated
     164       18169 :       if (node_multiplicity > 1)
     165             :       {
     166             :         // retrieve connected elements from the map
     167        8792 :         const std::vector<dof_id_type> & connected_elems = node_it->second;
     168             : 
     169             :         // find reference_subdomain_id (e.g. the subdomain with lower id or the
     170             :         // Elem::invalid_subdomain_id)
     171        8792 :         auto subdomain_it = connected_blocks.begin();
     172             :         subdomain_id_type reference_subdomain_id =
     173        8792 :             connected_blocks.find(Elem::invalid_subdomain_id) != connected_blocks.end()
     174       15154 :                 ? Elem::invalid_subdomain_id
     175        6362 :                 : *subdomain_it;
     176             : 
     177             :         // For the block_pairs option, the node that is only shared by specific block pairs will be
     178             :         // newly created.
     179        8792 :         bool should_create_new_node = true;
     180        8792 :         if (_block_pairs_restricted)
     181             :         {
     182         513 :           auto elems = node_to_elem_map[current_node->id()];
     183         513 :           should_create_new_node = false;
     184         513 :           std::set<subdomain_id_type> sets_blocks_for_this_node;
     185        2493 :           for (auto elem_id = elems.begin(); elem_id != elems.end(); elem_id++)
     186        1980 :             sets_blocks_for_this_node.insert(
     187        1980 :                 blockRestrictedElementSubdomainID(mesh->elem_ptr(*elem_id)));
     188         513 :           if (sets_blocks_for_this_node.size() == 2)
     189             :           {
     190         459 :             auto setIt = sets_blocks_for_this_node.begin();
     191         459 :             if (findBlockPairs(*setIt, *std::next(setIt, 1)))
     192         396 :               should_create_new_node = true;
     193             :           }
     194         513 :         }
     195             : 
     196             :         // multiplicity counter to keep track of how many nodes we added
     197        8792 :         unsigned int multiplicity_counter = node_multiplicity;
     198       68289 :         for (auto elem_id : connected_elems)
     199             :         {
     200             :           // all the duplicate nodes are added and assigned
     201       59506 :           if (multiplicity_counter == 0)
     202           9 :             break;
     203             : 
     204       59497 :           Elem * current_elem = mesh->elem_ptr(elem_id);
     205       59497 :           subdomain_id_type block_id = blockRestrictedElementSubdomainID(current_elem);
     206             : 
     207       85299 :           if ((blockRestrictedElementSubdomainID(current_elem) != reference_subdomain_id) ||
     208       25802 :               (_block_pairs_restricted & findBlockPairs(reference_subdomain_id, block_id)))
     209             :           {
     210             :             // assign the newly added node to current_elem
     211       33695 :             Node * new_node = nullptr;
     212             : 
     213       33695 :             std::vector<boundary_id_type> node_boundary_ids;
     214             : 
     215      160937 :             for (unsigned int node_id = 0; node_id < current_elem->n_nodes(); ++node_id)
     216      144701 :               if (current_elem->node_id(node_id) ==
     217      144701 :                   current_node->id()) // if current node == node on element
     218             :               {
     219       17459 :                 if (should_create_new_node)
     220             :                 {
     221             :                   // add new node
     222       17279 :                   new_node = Node::build(*current_node, mesh->n_nodes()).release();
     223             : 
     224             :                   // We're duplicating nodes so that each subdomain elem has its own copy, so it
     225             :                   // seems natural to assign this new node the same proc id as corresponding
     226             :                   // subdomain elem
     227       17279 :                   new_node->processor_id() = current_elem->processor_id();
     228       17279 :                   mesh->add_node(new_node);
     229       17279 :                   current_elem->set_node(node_id, new_node);
     230             :                   // Add boundary info to the new node
     231       17279 :                   boundary_info.boundary_ids(current_node, node_boundary_ids);
     232       17279 :                   boundary_info.add_node(new_node, node_boundary_ids);
     233             :                 }
     234             : 
     235       17459 :                 multiplicity_counter--; // node created, update multiplicity counter
     236             : 
     237       17459 :                 break; // ones the proper node has been fixed in one element we can break the
     238             :                        // loop
     239             :               }
     240             : 
     241       33695 :             if (should_create_new_node)
     242             :             {
     243      473685 :               for (auto connected_elem_id : connected_elems)
     244             :               {
     245      440170 :                 Elem * connected_elem = mesh->elem_ptr(connected_elem_id);
     246             : 
     247             :                 // Assign the newly added node to other connected elements with the same
     248             :                 // block_id
     249      440170 :                 if (connected_elem->subdomain_id() == current_elem->subdomain_id() &&
     250             :                     connected_elem != current_elem)
     251             :                 {
     252      714780 :                   for (unsigned int node_id = 0; node_id < connected_elem->n_nodes(); ++node_id)
     253      582318 :                     if (connected_elem->node_id(node_id) ==
     254      582318 :                         current_node->id()) // if current node == node on element
     255             :                     {
     256       16236 :                       connected_elem->set_node(node_id, new_node);
     257       16236 :                       break;
     258             :                     }
     259             :                 }
     260             :               }
     261             :             }
     262       33695 :           }
     263             :         }
     264             : 
     265             :         // create blocks pair and assign element side to new interface boundary map
     266       68307 :         for (auto elem_id : connected_elems)
     267             :         {
     268      837912 :           for (auto connected_elem_id : connected_elems)
     269             :           {
     270      778397 :             Elem * current_elem = mesh->elem_ptr(elem_id);
     271      778397 :             Elem * connected_elem = mesh->elem_ptr(connected_elem_id);
     272      778397 :             subdomain_id_type curr_elem_subid = blockRestrictedElementSubdomainID(current_elem);
     273             :             subdomain_id_type connected_elem_subid =
     274      778397 :                 blockRestrictedElementSubdomainID(connected_elem);
     275             : 
     276      778397 :             if (current_elem != connected_elem && curr_elem_subid < connected_elem_subid)
     277             :             {
     278      218609 :               if (current_elem->has_neighbor(connected_elem))
     279             :               {
     280       34181 :                 dof_id_type elem_id = current_elem->id();
     281       34181 :                 dof_id_type connected_elem_id = connected_elem->id();
     282       34181 :                 unsigned int side = current_elem->which_neighbor_am_i(connected_elem);
     283             :                 unsigned int connected_elem_side =
     284       34181 :                     connected_elem->which_neighbor_am_i(current_elem);
     285             : 
     286             :                 // in this case we need to play a game to reorder the sides
     287       34181 :                 if (_block_pairs_restricted || _surrounding_blocks_restricted)
     288             :                 {
     289       18144 :                   connected_elem_subid = connected_elem->subdomain_id();
     290       18144 :                   if (curr_elem_subid > connected_elem_subid) // we need to switch the ids
     291             :                   {
     292        3258 :                     connected_elem_subid = current_elem->subdomain_id();
     293        3258 :                     curr_elem_subid = connected_elem->subdomain_id();
     294             : 
     295        3258 :                     elem_id = connected_elem->id();
     296        3258 :                     side = connected_elem->which_neighbor_am_i(current_elem);
     297             : 
     298        3258 :                     connected_elem_id = current_elem->id();
     299        3258 :                     connected_elem_side = current_elem->which_neighbor_am_i(connected_elem);
     300             :                   }
     301             :                 }
     302             : 
     303             :                 std::pair<subdomain_id_type, subdomain_id_type> blocks_pair =
     304       34181 :                     std::make_pair(curr_elem_subid, connected_elem_subid);
     305             : 
     306             :                 std::pair<subdomain_id_type, subdomain_id_type> blocks_pair2 =
     307       34181 :                     std::make_pair(connected_elem_subid, curr_elem_subid);
     308             : 
     309       34181 :                 if (_block_pairs_restricted)
     310             :                 {
     311        1044 :                   if (findBlockPairs(blockRestrictedElementSubdomainID(current_elem),
     312        1044 :                                      blockRestrictedElementSubdomainID(connected_elem)))
     313             :                   {
     314         864 :                     _new_boundary_sides_map[blocks_pair].insert(std::make_pair(elem_id, side));
     315         864 :                     if (_add_interface_on_two_sides)
     316        1368 :                       _new_boundary_sides_map[blocks_pair2].insert(
     317        1368 :                           std::make_pair(connected_elem_id, connected_elem_side));
     318             :                   }
     319             :                 }
     320             :                 else
     321             :                 {
     322       33137 :                   _new_boundary_sides_map[blocks_pair].insert(std::make_pair(elem_id, side));
     323       33137 :                   if (_add_interface_on_two_sides)
     324           0 :                     _new_boundary_sides_map[blocks_pair2].insert(
     325           0 :                         std::make_pair(connected_elem_id, connected_elem_side));
     326             :                 }
     327             :               }
     328             :             }
     329             :           }
     330             :         }
     331             : 
     332             :       } // end multiplicity check
     333       18169 :     } // end loop over nodes
     334             :   } // end nodeptr check
     335             : 
     336         260 :   addInterfaceBoundary(*mesh);
     337         260 :   Partitioner::set_node_processor_ids(*mesh);
     338         520 :   return dynamic_pointer_cast<MeshBase>(mesh);
     339         260 : }
     340             : 
     341             : void
     342         260 : BreakMeshByBlockGenerator::addInterfaceBoundary(MeshBase & mesh)
     343             : {
     344         260 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     345             : 
     346             :   boundary_id_type boundary_id;
     347         260 :   boundary_id_type boundary_id_interface = Moose::INVALID_BOUNDARY_ID;
     348         260 :   boundary_id_type boundary_id_interface_transition = Moose::INVALID_BOUNDARY_ID;
     349             : 
     350         260 :   BoundaryName boundary_name;
     351             : 
     352             :   // loop over boundary sides
     353        5659 :   for (auto & boundary_side_map : _new_boundary_sides_map)
     354             :   {
     355        5399 :     if (!(_block_pairs_restricted || _surrounding_blocks_restricted) ||
     356        4401 :         ((_block_pairs_restricted || _surrounding_blocks_restricted) && !_add_transition_interface))
     357             :     {
     358             :       // find the appropriate boundary name and id
     359             :       // given primary and secondary block ID
     360        2834 :       if (_split_interface)
     361        1485 :         findBoundaryNameAndInd(mesh,
     362        1485 :                                boundary_side_map.first.first,
     363        1485 :                                boundary_side_map.first.second,
     364             :                                boundary_name,
     365             :                                boundary_id,
     366             :                                boundary_info);
     367             :       else
     368             :       {
     369        1349 :         boundary_name = _interface_name;
     370        2698 :         boundary_id_interface = boundary_id_interface == Moose::INVALID_BOUNDARY_ID
     371        1349 :                                     ? findFreeBoundaryId(mesh)
     372             :                                     : boundary_id_interface;
     373        1349 :         boundary_id = boundary_id_interface;
     374        1349 :         boundary_info.sideset_name(boundary_id_interface) = boundary_name;
     375             :       }
     376             :     }
     377             :     else // block resticted with transition boundary
     378             :     {
     379             : 
     380             :       const bool interior_boundary =
     381        4644 :           _block_set.find(boundary_side_map.first.first) != _block_set.end() &&
     382        2079 :           _block_set.find(boundary_side_map.first.second) != _block_set.end();
     383             : 
     384        2565 :       if ((_split_interface && interior_boundary) ||
     385        1485 :           (_split_transition_interface && !interior_boundary))
     386             :       {
     387        1710 :         findBoundaryNameAndInd(mesh,
     388        1710 :                                boundary_side_map.first.first,
     389        1710 :                                boundary_side_map.first.second,
     390             :                                boundary_name,
     391             :                                boundary_id,
     392             :                                boundary_info);
     393             :       }
     394         855 :       else if (interior_boundary)
     395             :       {
     396         540 :         boundary_name = _interface_name;
     397        1080 :         boundary_id_interface = boundary_id_interface == Moose::INVALID_BOUNDARY_ID
     398         540 :                                     ? findFreeBoundaryId(mesh)
     399             :                                     : boundary_id_interface;
     400             : 
     401         540 :         boundary_id = boundary_id_interface;
     402         540 :         boundary_info.sideset_name(boundary_id_interface) = boundary_name;
     403             :       }
     404             :       else
     405             :       {
     406         315 :         boundary_id_interface_transition =
     407         315 :             boundary_id_interface_transition == Moose::INVALID_BOUNDARY_ID
     408         315 :                 ? findFreeBoundaryId(mesh)
     409             :                 : boundary_id_interface_transition;
     410         315 :         boundary_id = boundary_id_interface_transition;
     411         315 :         boundary_info.sideset_name(boundary_id) = _interface_transition_name;
     412             :       }
     413             :     }
     414             :     // loop over all the side belonging to each pair and add it to the proper interface
     415       15190 :     for (auto & element_side : boundary_side_map.second)
     416        9791 :       boundary_info.add_side(element_side.first, element_side.second, boundary_id);
     417             :   }
     418         260 : }
     419             : 
     420             : subdomain_id_type
     421     1779691 : BreakMeshByBlockGenerator::blockRestrictedElementSubdomainID(const Elem * elem)
     422             : {
     423     1779691 :   subdomain_id_type elem_subdomain_id = elem->subdomain_id();
     424     2083171 :   if ((_block_pairs_restricted || _surrounding_blocks_restricted) &&
     425     2083171 :       (_block_set.find(elem_subdomain_id) == _block_set.end()))
     426       99756 :     elem_subdomain_id = Elem::invalid_subdomain_id;
     427             : 
     428     1779691 :   return elem_subdomain_id;
     429             : }
     430             : 
     431             : bool
     432       27305 : BreakMeshByBlockGenerator::findBlockPairs(subdomain_id_type block_one, subdomain_id_type block_two)
     433             : {
     434       29060 :   for (auto block_pair : _block_pairs)
     435        3015 :     if ((block_pair.first == block_one && block_pair.second == block_two) ||
     436        1755 :         (block_pair.first == block_two && block_pair.second == block_one))
     437        1260 :       return true;
     438       26045 :   return false;
     439             : }

Generated by: LCOV version 1.14