LCOV - code coverage report
Current view: top level - src/meshgenerators - LowerDBlockFromSidesetGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 97 98 99.0 %
Date: 2025-07-17 01:28:37 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 "LowerDBlockFromSidesetGenerator.h"
      11             : #include "InputParameters.h"
      12             : #include "MooseTypes.h"
      13             : #include "CastUniquePointer.h"
      14             : #include "MooseMeshUtils.h"
      15             : 
      16             : #include "libmesh/distributed_mesh.h"
      17             : #include "libmesh/elem.h"
      18             : #include "libmesh/parallel_elem.h"
      19             : #include "libmesh/parallel_node.h"
      20             : #include "libmesh/compare_elems_by_level.h"
      21             : #include "libmesh/mesh_communication.h"
      22             : 
      23             : #include "timpi/parallel_sync.h"
      24             : 
      25             : #include <set>
      26             : #include <typeinfo>
      27             : 
      28             : registerMooseObject("MooseApp", LowerDBlockFromSidesetGenerator);
      29             : 
      30             : InputParameters
      31       18503 : LowerDBlockFromSidesetGenerator::validParams()
      32             : {
      33       18503 :   InputParameters params = MeshGenerator::validParams();
      34             : 
      35       18503 :   params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
      36       18503 :   params.addParam<SubdomainID>("new_block_id", "The lower dimensional block id to create");
      37       18503 :   params.addParam<SubdomainName>("new_block_name",
      38             :                                  "The lower dimensional block name to create (optional)");
      39       18503 :   params.addRequiredParam<std::vector<BoundaryName>>(
      40             :       "sidesets", "The sidesets from which to create the new block");
      41             : 
      42       18503 :   params.addClassDescription("Adds lower dimensional elements on the specified sidesets.");
      43             : 
      44       18503 :   return params;
      45           0 : }
      46             : 
      47        2119 : LowerDBlockFromSidesetGenerator::LowerDBlockFromSidesetGenerator(const InputParameters & parameters)
      48             :   : MeshGenerator(parameters),
      49        2119 :     _input(getMesh("input")),
      50        4238 :     _sideset_names(getParam<std::vector<BoundaryName>>("sidesets"))
      51             : {
      52        2119 : }
      53             : 
      54             : // Used to temporarily store information about which lower-dimensional
      55             : // sides to add and what subdomain id to use for the added sides.
      56             : struct ElemSideDouble
      57             : {
      58       19110 :   ElemSideDouble(Elem * elem_in, unsigned short int side_in) : elem(elem_in), side(side_in) {}
      59             : 
      60             :   Elem * elem;
      61             :   unsigned short int side;
      62             : };
      63             : 
      64             : std::unique_ptr<MeshBase>
      65        1986 : LowerDBlockFromSidesetGenerator::generate()
      66             : {
      67        1986 :   std::unique_ptr<MeshBase> mesh = std::move(_input);
      68             : 
      69             :   // Generate a new block id if one isn't supplied.
      70        3972 :   SubdomainID new_block_id = isParamValid("new_block_id")
      71        3972 :                                  ? getParam<SubdomainID>("new_block_id")
      72        1986 :                                  : MooseMeshUtils::getNextFreeSubdomainID(*mesh);
      73             : 
      74             :   // Make sure our boundary info and parallel counts are setup
      75        1986 :   if (!mesh->is_prepared())
      76             :   {
      77         384 :     const bool allow_remote_element_removal = mesh->allow_remote_element_removal();
      78             :     // We want all of our boundary elements available, so avoid removing them if they haven't
      79             :     // already been so
      80         384 :     mesh->allow_remote_element_removal(false);
      81         384 :     mesh->prepare_for_use();
      82         384 :     mesh->allow_remote_element_removal(allow_remote_element_removal);
      83             :   }
      84             : 
      85             :   // Check that the sidesets are present in the mesh
      86        4132 :   for (const auto & sideset : _sideset_names)
      87        2150 :     if (!MooseMeshUtils::hasBoundaryName(*mesh, sideset))
      88           4 :       paramError("sidesets", "The sideset '", sideset, "' was not found within the mesh");
      89             : 
      90        1982 :   auto sideset_ids = MooseMeshUtils::getBoundaryIDs(*mesh, _sideset_names, true);
      91        1982 :   std::set<boundary_id_type> sidesets(sideset_ids.begin(), sideset_ids.end());
      92        1982 :   auto side_list = mesh->get_boundary_info().build_side_list();
      93        1982 :   if (!mesh->is_serial() && mesh->comm().size() > 1)
      94             :   {
      95          10 :     std::vector<Elem *> elements_to_send;
      96          10 :     unsigned short i_need_boundary_elems = 0;
      97         172 :     for (const auto & [elem_id, side, bc_id] : side_list)
      98             :     {
      99         162 :       libmesh_ignore(side);
     100         162 :       if (sidesets.count(bc_id))
     101             :       {
     102             :         // Whether we have this boundary information through our locally owned element or a ghosted
     103             :         // element, we'll need the boundary elements for parallel consistent addition
     104          92 :         i_need_boundary_elems = 1;
     105          92 :         auto * elem = mesh->elem_ptr(elem_id);
     106          92 :         if (elem->processor_id() == mesh->processor_id())
     107          80 :           elements_to_send.push_back(elem);
     108             :       }
     109             :     }
     110             : 
     111             :     std::set<const Elem *, libMesh::CompareElemIdsByLevel> connected_elements(
     112          10 :         elements_to_send.begin(), elements_to_send.end());
     113          10 :     std::set<const Node *> connected_nodes;
     114          10 :     reconnect_nodes(connected_elements, connected_nodes);
     115          10 :     std::set<dof_id_type> connected_node_ids;
     116         178 :     for (auto * nd : connected_nodes)
     117         168 :       connected_node_ids.insert(nd->id());
     118             : 
     119          10 :     std::vector<unsigned short> need_boundary_elems(mesh->comm().size());
     120          10 :     mesh->comm().allgather(i_need_boundary_elems, need_boundary_elems);
     121          10 :     std::unordered_map<processor_id_type, decltype(elements_to_send)> push_element_data;
     122          10 :     std::unordered_map<processor_id_type, decltype(connected_nodes)> push_node_data;
     123             : 
     124          30 :     for (const auto pid : index_range(mesh->comm()))
     125             :       // Don't need to send to self
     126          20 :       if (pid != mesh->processor_id() && need_boundary_elems[pid])
     127             :       {
     128          10 :         if (elements_to_send.size())
     129          10 :           push_element_data[pid] = elements_to_send;
     130          10 :         if (connected_nodes.size())
     131          10 :           push_node_data[pid] = connected_nodes;
     132             :       }
     133             : 
     134          10 :     auto node_action_functor = [](processor_id_type, const auto &)
     135             :     {
     136             :       // Node packing specialization already has unpacked node into mesh, so nothing to do
     137          10 :     };
     138          20 :     Parallel::push_parallel_packed_range(
     139          10 :         mesh->comm(), push_node_data, mesh.get(), node_action_functor);
     140          10 :     auto elem_action_functor = [](processor_id_type, const auto &)
     141             :     {
     142             :       // Elem packing specialization already has unpacked elem into mesh, so nothing to do
     143          10 :     };
     144          20 :     TIMPI::push_parallel_packed_range(
     145          10 :         mesh->comm(), push_element_data, mesh.get(), elem_action_functor);
     146             : 
     147             :     // now that we've gathered everything, we need to rebuild the side list
     148          10 :     side_list = mesh->get_boundary_info().build_side_list();
     149          10 :   }
     150             : 
     151        1982 :   std::vector<std::pair<dof_id_type, ElemSideDouble>> element_sides_on_boundary;
     152        1982 :   dof_id_type counter = 0;
     153      127604 :   for (const auto & triple : side_list)
     154      125626 :     if (sidesets.count(std::get<2>(triple)))
     155             :     {
     156       19114 :       if (auto elem = mesh->query_elem_ptr(std::get<0>(triple)))
     157             :       {
     158       19114 :         if (!elem->active())
     159           4 :           mooseError(
     160             :               "Only active, level 0 elements can be made interior parents of new level 0 lower-d "
     161             :               "elements. Make sure that ",
     162           4 :               type(),
     163             :               "s are run before any refinement generators");
     164       19110 :         element_sides_on_boundary.push_back(
     165       38220 :             std::make_pair(counter, ElemSideDouble(elem, std::get<1>(triple))));
     166             :       }
     167       19110 :       ++counter;
     168             :     }
     169             : 
     170        1978 :   dof_id_type max_elem_id = mesh->max_elem_id();
     171        1978 :   unique_id_type max_unique_id = mesh->parallel_max_unique_id();
     172             : 
     173             :   // Making an important assumption that at least our boundary elements are the same on all
     174             :   // processes even in distributed mesh mode (this is reliant on the correct ghosting functors
     175             :   // existing on the mesh)
     176       21088 :   for (auto & [i, elem_side] : element_sides_on_boundary)
     177             :   {
     178       19110 :     Elem * elem = elem_side.elem;
     179             : 
     180       19110 :     const auto side = elem_side.side;
     181             : 
     182             :     // Build a non-proxy element from this side.
     183       19110 :     std::unique_ptr<Elem> side_elem(elem->build_side_ptr(side));
     184             : 
     185             :     // The side will be added with the same processor id as the parent.
     186       19110 :     side_elem->processor_id() = elem->processor_id();
     187             : 
     188             :     // Add subdomain ID
     189       19110 :     side_elem->subdomain_id() = new_block_id;
     190             : 
     191             :     // Also assign the side's interior parent, so it is always
     192             :     // easy to figure out the Elem we came from.
     193       19110 :     side_elem->set_interior_parent(elem);
     194             : 
     195             :     // Add id
     196       19110 :     side_elem->set_id(max_elem_id + i);
     197       19110 :     side_elem->set_unique_id(max_unique_id + i);
     198             : 
     199             :     // Finally, add the lower-dimensional element to the Mesh.
     200       19110 :     mesh->add_elem(side_elem.release());
     201       19110 :   };
     202             : 
     203             :   // Assign block name, if provided
     204        1978 :   if (isParamValid("new_block_name"))
     205        1339 :     mesh->subdomain_name(new_block_id) = getParam<SubdomainName>("new_block_name");
     206             : 
     207        1978 :   const bool skip_partitioning_old = mesh->skip_partitioning();
     208        1978 :   mesh->skip_partitioning(true);
     209        1978 :   mesh->prepare_for_use();
     210        1978 :   mesh->skip_partitioning(skip_partitioning_old);
     211             : 
     212        3956 :   return mesh;
     213        1978 : }

Generated by: LCOV version 1.14