LCOV - code coverage report
Current view: top level - src/meshgenerators - PinMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose reactor: #31405 (292dce) with base fef103 Lines: 321 335 95.8 %
Date: 2025-09-04 07:56:24 Functions: 5 5 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 "PinMeshGenerator.h"
      11             : 
      12             : #include "ReactorGeometryMeshBuilderBase.h"
      13             : #include <cmath>
      14             : #include "MooseApp.h"
      15             : #include "MooseMeshUtils.h"
      16             : #include "Factory.h"
      17             : #include "libmesh/elem.h"
      18             : 
      19             : registerMooseObject("ReactorApp", PinMeshGenerator);
      20             : 
      21             : InputParameters
      22        1946 : PinMeshGenerator::validParams()
      23             : {
      24        1946 :   auto params = ReactorGeometryMeshBuilderBase::validParams();
      25             : 
      26        3892 :   params.addRequiredParam<MeshGeneratorName>(
      27             :       "reactor_params",
      28             :       "The ReactorMeshParams MeshGenerator that is the basis for this component conformal mesh.");
      29             : 
      30        3892 :   params.addRequiredParam<subdomain_id_type>("pin_type",
      31             :                                              "The integer ID for this pin type definition");
      32             : 
      33        3892 :   params.addRequiredRangeCheckedParam<Real>(
      34             :       "pitch", "pitch>0.0", "The pitch for the outermost boundary polygon");
      35             : 
      36        3892 :   params.addRangeCheckedParam<unsigned int>(
      37             :       "num_sectors", "num_sectors>0", "Number of azimuthal sectors in each quadrant");
      38             : 
      39        3892 :   params.addRangeCheckedParam<std::vector<Real>>(
      40             :       "ring_radii",
      41             :       "ring_radii>0.0",
      42             :       "Radii of major concentric circles of the pin. If unspecified, no pin is present.");
      43             : 
      44        3892 :   params.addRangeCheckedParam<std::vector<Real>>(
      45             :       "duct_halfpitch",
      46             :       "duct_halfpitch>0.0",
      47             :       "Apothem of the ducts. If unspecified, no duct is present.");
      48             : 
      49        5838 :   params.addRangeCheckedParam<std::vector<unsigned int>>(
      50             :       "mesh_intervals",
      51        3892 :       std::vector<unsigned int>{1},
      52             :       "mesh_intervals>0",
      53             :       "The number of meshing intervals for each region starting at the center. Parameter should be "
      54             :       "size:"
      55             :       "((length(ring_radii) + length(duct_halfpitch) + 1");
      56             : 
      57        3892 :   params.addParam<std::vector<std::vector<std::string>>>(
      58             :       "block_names",
      59             :       "Block names for each radial and axial zone. "
      60             :       "Inner indexing is radial zones (pin/background/duct), outer indexing is axial");
      61             : 
      62        3892 :   params.addParam<std::vector<std::vector<subdomain_id_type>>>(
      63             :       "region_ids",
      64             :       "IDs for each radial and axial zone for assignment of region_id extra element "
      65             :       "id. "
      66             :       "Inner indexing is radial zones (pin/background/duct), outer indexing is axial");
      67             : 
      68        3892 :   params.addParam<bool>("extrude",
      69        3892 :                         false,
      70             :                         "Determines if this is the final step in the geometry construction"
      71             :                         " and extrudes the 2D geometry to 3D. If this is true then this mesh "
      72             :                         "cannot be used in further mesh building in the Reactor workflow");
      73        3892 :   params.addParam<bool>(
      74        3892 :       "homogenized", false, "Determines whether homogenized pin mesh should be generated");
      75        3892 :   params.addParam<bool>(
      76        3892 :       "use_as_assembly", false, "Determines whether pin mesh should be used as an assembly mesh");
      77             : 
      78        3892 :   params.addParam<bool>(
      79        3892 :       "quad_center_elements", true, "Whether the center elements are quad or triangular.");
      80        3892 :   params.addParamNamesToGroup("region_ids pin_type", "ID assigment");
      81        3892 :   params.addParamNamesToGroup(
      82             :       "mesh_intervals ring_radii num_sectors pin_type homogenized use_as_assembly",
      83             :       "Pin specifications");
      84        3892 :   params.addParamNamesToGroup("mesh_intervals duct_halfpitch num_sectors", "Duct specifications");
      85             : 
      86        1946 :   params.addClassDescription("This PinMeshGenerator object is designed to generate pin-like "
      87             :                              "structures, with IDs, from a reactor geometry. "
      88             :                              "Whether it be a square or hexagonal pin, they are divided into three "
      89             :                              "substructures - the innermost "
      90             :                              "radial pin regions, the single bridging background region, and the "
      91             :                              "square or hexagonal ducts regions.");
      92             : 
      93        1946 :   return params;
      94           0 : }
      95             : 
      96         970 : PinMeshGenerator::PinMeshGenerator(const InputParameters & parameters)
      97             :   : ReactorGeometryMeshBuilderBase(parameters),
      98         970 :     _pin_type(getParam<subdomain_id_type>("pin_type")),
      99        1940 :     _pitch(getParam<Real>("pitch")),
     100        3694 :     _num_sectors(isParamValid("num_sectors") ? getParam<unsigned int>("num_sectors") : 0),
     101        3517 :     _ring_radii(isParamValid("ring_radii") ? getParam<std::vector<Real>>("ring_radii")
     102             :                                            : std::vector<Real>()),
     103        3197 :     _duct_halfpitch(isParamValid("duct_halfpitch") ? getParam<std::vector<Real>>("duct_halfpitch")
     104             :                                                    : std::vector<Real>()),
     105        1940 :     _intervals(getParam<std::vector<unsigned int>>("mesh_intervals")),
     106        3880 :     _region_ids(isParamValid("region_ids")
     107         970 :                     ? getParam<std::vector<std::vector<subdomain_id_type>>>("region_ids")
     108             :                     : std::vector<std::vector<subdomain_id_type>>()),
     109        1940 :     _extrude(getParam<bool>("extrude")),
     110        1940 :     _quad_center(getParam<bool>("quad_center_elements")),
     111        1940 :     _homogenized(getParam<bool>("homogenized")),
     112        2910 :     _is_assembly(getParam<bool>("use_as_assembly"))
     113             : {
     114             :   // Initialize ReactorMeshParams object
     115        2910 :   initializeReactorMeshParams(getParam<MeshGeneratorName>("reactor_params"));
     116             : 
     117         970 :   _mesh_dimensions = getReactorParam<unsigned int>(RGMB::mesh_dimensions);
     118         970 :   _mesh_geometry = getReactorParam<std::string>(RGMB::mesh_geometry);
     119             : 
     120         970 :   if (_is_assembly)
     121             :   {
     122         145 :     auto assembly_pitch = getReactorParam<Real>(RGMB::assembly_pitch);
     123         145 :     if (assembly_pitch != _pitch)
     124           0 :       mooseError("Pitch defined in PinMeshGenerator must match assembly_pitch defined in "
     125             :                  "ReactorMeshParams if use_as_assembly is set to true");
     126             :   }
     127             : 
     128         970 :   if (_extrude && _mesh_dimensions != 3)
     129           0 :     paramError("extrude",
     130             :                "In order to extrude this mesh, ReactorMeshParams/dim needs to be set to 3\n");
     131        1976 :   if (_extrude && (!hasReactorParam<boundary_id_type>(RGMB::top_boundary_id) ||
     132        1006 :                    !hasReactorParam<boundary_id_type>(RGMB::bottom_boundary_id)))
     133           0 :     mooseError("Both top_boundary_id and bottom_boundary_id must be provided in ReactorMeshParams "
     134             :                "if using extruded geometry");
     135             : 
     136         970 :   if (_homogenized)
     137             :   {
     138          93 :     if (_mesh_geometry == "Square")
     139           0 :       mooseError("Homogenization in PinMeshGenerator is only supported for hexagonal geometries");
     140             :     const std::vector<std::string> disallowed_parameters = {
     141          93 :         "num_sectors", "ring_radii", "duct_halfpitch", "mesh_intervals"};
     142         465 :     for (const auto & parameter : disallowed_parameters)
     143         372 :       if (parameters.isParamSetByUser(parameter))
     144           0 :         paramError(parameter,
     145           0 :                    "Parameter " + parameter + " should not be defined for a homogenized pin mesh");
     146          93 :   }
     147             :   else
     148             :   {
     149         877 :     if (_num_sectors == 0)
     150           0 :       mooseError(
     151             :           "Number of sectors must be assigned with parameter num_sectors for non-homogenized pins");
     152         877 :     if (_intervals.size() != (_ring_radii.size() + _duct_halfpitch.size() + 1))
     153           0 :       mooseError(
     154             :           "The number of mesh intervals must be equal to the number of annular regions + the "
     155             :           "number of duct regions + 1"
     156             :           " for the region between the rings and ducts\n");
     157             :   }
     158             : 
     159        1940 :   if (isParamValid("region_ids"))
     160             :   {
     161             :     unsigned int n_axial_levels =
     162         970 :         (_mesh_dimensions == 3)
     163             :             ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
     164        1775 :             : 1;
     165         970 :     if (_region_ids.size() != n_axial_levels)
     166           0 :       mooseError("The size of region IDs must be equal to the number of axial levels as defined in "
     167             :                  "the ReactorMeshParams object");
     168         970 :     if (_region_ids[0].size() != (_ring_radii.size() + _duct_halfpitch.size() + 1))
     169           0 :       mooseError("The number of region IDs given needs to be one more than the number of "
     170             :                  "ring_radii + the number of duct_radii\n");
     171             :   }
     172             :   else
     173             :   {
     174           0 :     mooseError("Region IDs must be assigned with parameter region_ids");
     175             :   }
     176        1940 :   if (isParamValid("block_names"))
     177             :   {
     178         173 :     if (getReactorParam<bool>(RGMB::region_id_as_block_name))
     179           2 :       paramError("block_names",
     180             :                  "If ReactorMeshParams/region_id_as_block_name is set, block_names should not be "
     181             :                  "specified in PinMeshGenerator");
     182         171 :     _has_block_names = true;
     183         513 :     _block_names = getParam<std::vector<std::vector<std::string>>>("block_names");
     184         171 :     if (_region_ids.size() != _block_names.size())
     185           0 :       mooseError("The size of block_names must match the size of region_ids");
     186         513 :     for (const auto i : index_range(_region_ids))
     187         342 :       if (_region_ids[i].size() != _block_names[i].size())
     188           0 :         mooseError("The size of block_names must match the size of region_ids");
     189             :   }
     190             :   else
     191         797 :     _has_block_names = false;
     192             : 
     193         968 :   const auto use_flexible_stitching = getReactorParam<bool>(RGMB::flexible_assembly_stitching);
     194             :   std::string build_mesh_name;
     195             : 
     196             :   // No subgenerators will be called if option to bypass mesh generators is enabled
     197         968 :   if (!getReactorParam<bool>(RGMB::bypass_meshgen))
     198             :   {
     199         854 :     if (_homogenized)
     200             :     {
     201             :       // If flexible assembly stitching is invoked and this is a homogeneous assembly mesh, do not
     202             :       // call mesh subgenerators here. The homogeneous assembly mesh should be created entirely in
     203             :       // generateFlexibleAssemblyBoundaries()
     204          84 :       bool skip_assembly_generation = _is_assembly && use_flexible_stitching;
     205             : 
     206          84 :       auto params = _app.getFactory().getValidParams("SimpleHexagonGenerator");
     207             : 
     208          84 :       params.set<Real>("hexagon_size") = _pitch / 2.0;
     209          84 :       params.set<boundary_id_type>("external_boundary_id") =
     210          84 :           RGMB::PIN_BOUNDARY_ID_START + _pin_type;
     211             :       const auto boundary_name =
     212          84 :           (_is_assembly ? RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX : RGMB::PIN_BOUNDARY_NAME_PREFIX) +
     213         252 :           std::to_string(_pin_type);
     214         168 :       params.set<BoundaryName>("external_boundary_name") = boundary_name;
     215          84 :       params.set<std::vector<subdomain_id_type>>("block_id") = {
     216         216 :           _quad_center ? RGMB::PIN_BLOCK_ID_START : RGMB::PIN_BLOCK_ID_TRI};
     217         216 :       params.set<MooseEnum>("element_type") = _quad_center ? "QUAD" : "TRI";
     218          84 :       auto block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R0";
     219          84 :       if (!_quad_center)
     220             :         block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
     221         252 :       params.set<std::vector<SubdomainName>>("block_name") = {block_name};
     222             : 
     223          84 :       if (!skip_assembly_generation)
     224             :       {
     225          54 :         build_mesh_name = name() + "_2D";
     226         108 :         addMeshSubgenerator("SimpleHexagonGenerator", build_mesh_name, params);
     227             :       }
     228          84 :     }
     229             :     else
     230             :     {
     231             :       // Define all id variables used in the pin
     232             :       std::vector<unsigned int> ring_intervals;
     233             :       std::vector<subdomain_id_type> ring_blk_ids;
     234             :       std::vector<SubdomainName> ring_blk_names;
     235             :       unsigned int background_intervals = 1;
     236             :       std::vector<subdomain_id_type> background_blk_ids;
     237             :       std::vector<SubdomainName> background_blk_names;
     238             :       std::vector<unsigned int> duct_intervals;
     239             :       std::vector<subdomain_id_type> duct_blk_ids;
     240             :       std::vector<SubdomainName> duct_blk_names;
     241             : 
     242        2363 :       for (const auto i : index_range(_intervals))
     243             :       {
     244             :         const auto block_name =
     245        4779 :             RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R" + std::to_string(i);
     246        1593 :         const auto block_id = RGMB::PIN_BLOCK_ID_START + i;
     247             : 
     248        1593 :         if (i < _ring_radii.size())
     249             :         {
     250         565 :           ring_intervals.push_back(_intervals[i]);
     251         565 :           ring_blk_ids.push_back(block_id);
     252         565 :           ring_blk_names.push_back(block_name);
     253             :         }
     254        1028 :         else if (i > _ring_radii.size())
     255             :         {
     256         258 :           duct_intervals.push_back(_intervals[i]);
     257         258 :           duct_blk_ids.push_back(block_id);
     258         258 :           duct_blk_names.push_back(block_name);
     259             :         }
     260             :         else
     261             :         {
     262         770 :           background_intervals = _intervals[i];
     263         770 :           background_blk_ids.push_back(block_id);
     264         770 :           background_blk_names.push_back(block_name);
     265             :         }
     266             :       }
     267         770 :       if (ring_intervals.size() > 0)
     268             :       {
     269         529 :         if (ring_intervals.front() != 1)
     270             :         {
     271             :           // If quad center elements, copy element at beginning of block names and
     272             :           // block ids. Otherwise add RGMB::TRI_BLOCK_NAME_SUFFIX to block names and generate new
     273             :           // block id
     274          18 :           if (_quad_center)
     275             :           {
     276           9 :             ring_blk_ids.insert(ring_blk_ids.begin(), ring_blk_ids.front());
     277           9 :             ring_blk_names.insert(ring_blk_names.begin(), ring_blk_names.front());
     278             :           }
     279             :           else
     280             :           {
     281           9 :             const auto block_name = ring_blk_names.front() + RGMB::TRI_BLOCK_NAME_SUFFIX;
     282           9 :             const auto block_id = RGMB::PIN_BLOCK_ID_TRI;
     283           9 :             ring_blk_ids.insert(ring_blk_ids.begin(), block_id);
     284           9 :             ring_blk_names.insert(ring_blk_names.begin(), block_name);
     285             :           }
     286             :         }
     287             :         // Add RGMB::TRI_BLOCK_NAME_SUFFIX if only one radial region and tri center elements
     288         511 :         else if (!_quad_center)
     289             :         {
     290         276 :           ring_blk_ids[0] = RGMB::PIN_BLOCK_ID_TRI;
     291         276 :           ring_blk_names[0] += RGMB::TRI_BLOCK_NAME_SUFFIX;
     292             :         }
     293             :       }
     294             :       else
     295             :       {
     296         241 :         if (background_intervals > 1)
     297             :         {
     298             :           // If quad center elements, copy element at beginning of block names and
     299             :           // block ids. Otherwise add RGMB::TRI_BLOCK_NAME_SUFFIX to block names and generate new
     300             :           // block id
     301         168 :           if (_quad_center)
     302             :           {
     303         159 :             background_blk_ids.insert(background_blk_ids.begin(), background_blk_ids.front());
     304         159 :             background_blk_names.insert(background_blk_names.begin(), background_blk_names.front());
     305             :           }
     306             :           else
     307             :           {
     308           9 :             const auto block_name = background_blk_names.front() + RGMB::TRI_BLOCK_NAME_SUFFIX;
     309           9 :             const auto block_id = RGMB::PIN_BLOCK_ID_TRI;
     310           9 :             background_blk_ids.insert(background_blk_ids.begin(), block_id);
     311           9 :             background_blk_names.insert(background_blk_names.begin(), block_name);
     312             :           }
     313             :         }
     314             :         // Add RGMB::TRI_BLOCK_NAME_SUFFIX if only one background region and tri center elements
     315             :         // and no ring regions
     316          73 :         else if (!_quad_center)
     317             :         {
     318          64 :           background_blk_ids[0] = RGMB::PIN_BLOCK_ID_TRI;
     319          64 :           background_blk_names[0] += RGMB::TRI_BLOCK_NAME_SUFFIX;
     320             :         }
     321             :       }
     322             : 
     323             :       // If flexible assembly stitching is invoked and this is an assembly mesh with only a
     324             :       // background region, do not call mesh subgenerators here. This assembly mesh should be
     325             :       // created entirely in generateFlexibleAssemblyBoundaries()
     326             :       bool skip_assembly_generation =
     327         770 :           _is_assembly && use_flexible_stitching && _intervals.size() == 1;
     328             : 
     329             :       if (!skip_assembly_generation)
     330             :       {
     331             :         // Generate Cartesian/hex pin using PolygonConcentricCircleMeshGenerator
     332             :         {
     333             :           // Get and assign parameters for the main geometry feature of the Pin
     334             :           // which is created with a PolygonConcentricCircleMeshGenerator subgenerator
     335         760 :           auto params = _app.getFactory().getValidParams("PolygonConcentricCircleMeshGenerator");
     336         760 :           params.set<bool>("preserve_volumes") = true;
     337         760 :           params.set<bool>("quad_center_elements") = _quad_center;
     338        1520 :           params.set<MooseEnum>("polygon_size_style") = "apothem";
     339         760 :           params.set<Real>("polygon_size") = _pitch / 2.0;
     340         760 :           params.set<boundary_id_type>("external_boundary_id") =
     341         760 :               RGMB::PIN_BOUNDARY_ID_START + _pin_type;
     342         760 :           const auto boundary_name = (_is_assembly ? RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX
     343             :                                                    : RGMB::PIN_BOUNDARY_NAME_PREFIX) +
     344        2280 :                                      std::to_string(_pin_type);
     345        1520 :           params.set<BoundaryName>("external_boundary_name") = boundary_name;
     346         760 :           bool flat_side_up = (_mesh_geometry == "Square");
     347         760 :           params.set<bool>("flat_side_up") = flat_side_up;
     348         760 :           params.set<bool>("create_outward_interface_boundaries") = false;
     349             : 
     350         760 :           const auto num_sides = (_mesh_geometry == "Square") ? 4 : 6;
     351         760 :           params.set<unsigned int>("num_sides") = num_sides;
     352         760 :           params.set<std::vector<unsigned int>>("num_sectors_per_side") =
     353        1520 :               std::vector<unsigned int>(num_sides, _num_sectors);
     354             : 
     355         760 :           if (ring_intervals.size() > 0)
     356             :           {
     357         529 :             params.set<std::vector<Real>>("ring_radii") = _ring_radii;
     358         529 :             params.set<std::vector<subdomain_id_type>>("ring_block_ids") = ring_blk_ids;
     359         529 :             params.set<std::vector<SubdomainName>>("ring_block_names") = ring_blk_names;
     360        1058 :             params.set<std::vector<unsigned int>>("ring_intervals") = ring_intervals;
     361             :           }
     362             : 
     363         760 :           params.set<std::vector<subdomain_id_type>>("background_block_ids") = background_blk_ids;
     364         760 :           params.set<std::vector<SubdomainName>>("background_block_names") = background_blk_names;
     365         760 :           params.set<unsigned int>("background_intervals") = background_intervals;
     366             : 
     367         760 :           if (duct_intervals.size() > 0)
     368             :           {
     369         516 :             params.set<MooseEnum>("duct_sizes_style") = "apothem";
     370         258 :             params.set<std::vector<Real>>("duct_sizes") = _duct_halfpitch;
     371         258 :             params.set<std::vector<subdomain_id_type>>("duct_block_ids") = duct_blk_ids;
     372         258 :             params.set<std::vector<SubdomainName>>("duct_block_names") = duct_blk_names;
     373         516 :             params.set<std::vector<unsigned int>>("duct_intervals") = duct_intervals;
     374             :           }
     375             : 
     376        1520 :           addMeshSubgenerator("PolygonConcentricCircleMeshGenerator", name() + "_2D", params);
     377         760 :         }
     378             : 
     379             :         // Remove extra sidesets created by PolygonConcentricCircleMeshGenerator
     380             :         {
     381        1520 :           auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
     382             : 
     383        2280 :           params.set<MeshGeneratorName>("input") = name() + "_2D";
     384             : 
     385         760 :           auto num_sides = (_mesh_geometry == "Square") ? 4 : 6;
     386             :           std::vector<BoundaryName> boundaries_to_delete = {};
     387        4680 :           for (const auto i : make_range(num_sides))
     388       11760 :             boundaries_to_delete.insert(boundaries_to_delete.end(),
     389       11760 :                                         {std::to_string(10001 + i), std::to_string(15001 + i)});
     390        1520 :           params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
     391             : 
     392         760 :           build_mesh_name = name() + "_delbds";
     393        1520 :           addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
     394         760 :         }
     395             :       }
     396         770 :     }
     397             : 
     398             :     // For pin acting as assembly, modify outermost mesh interval to enable flexible assembly
     399             :     // stitching
     400         854 :     if (_is_assembly && use_flexible_stitching)
     401             :     {
     402          55 :       generateFlexibleAssemblyBoundaries();
     403         110 :       build_mesh_name = name() + "_fpg_delbds";
     404             :     }
     405             : 
     406             :     // Pass mesh meta-data defined in subgenerator constructor to this MeshGenerator
     407        1708 :     if (hasMeshProperty<Real>("pitch_meta", name() + "_2D"))
     408        1628 :       copyMeshProperty<Real>("pitch_meta", name() + "_2D");
     409        1708 :     if (hasMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", name() + "_2D"))
     410        1628 :       copyMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", name() + "_2D");
     411        1708 :     if (hasMeshProperty<Real>("max_radius_meta", name() + "_2D"))
     412        1628 :       copyMeshProperty<Real>("max_radius_meta", name() + "_2D");
     413        1708 :     if (hasMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D"))
     414        1628 :       copyMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D");
     415        1708 :     if (hasMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D"))
     416        1628 :       copyMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D");
     417             : 
     418         854 :     if (_is_assembly)
     419         272 :       declareMeshProperty("pattern_pitch_meta", getReactorParam<Real>(RGMB::assembly_pitch));
     420        1436 :     else if (hasMeshProperty<Real>("pattern_pitch_meta", name() + "_2D"))
     421        1382 :       copyMeshProperty<Real>("pattern_pitch_meta", name() + "_2D");
     422         854 :     declareMeshProperty("is_control_drum_meta", false);
     423             : 
     424         854 :     if (_extrude && _mesh_dimensions == 3)
     425          36 :       build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
     426             : 
     427             :     // Store final mesh subgenerator
     428         854 :     _build_mesh = &getMeshByName(build_mesh_name);
     429             :   }
     430             : 
     431         968 :   generateMetadata();
     432         968 : }
     433             : 
     434             : void
     435          55 : PinMeshGenerator::generateFlexibleAssemblyBoundaries()
     436             : {
     437             :   SubdomainName outermost_block_name;
     438             :   bool has_single_mesh_interval;
     439             : 
     440             :   // Assemblies that invoke this method are either homogenized or have a single pin. First check if
     441             :   // the assembly only has a single region. Otherwise, determine the outermost region for deletion
     442          55 :   if (_homogenized || (_intervals.size() == 1))
     443             :   {
     444          80 :     outermost_block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R0";
     445             :     has_single_mesh_interval = true;
     446             :   }
     447             :   else
     448             :   {
     449          45 :     outermost_block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R" +
     450          15 :                            std::to_string(_intervals.size() - 1);
     451             :     has_single_mesh_interval = false;
     452             : 
     453             :     // Invoke BlockDeletionGenerator to delete outermost mesh interval of assembly
     454          30 :     auto params = _app.getFactory().getValidParams("BlockDeletionGenerator");
     455             : 
     456          45 :     params.set<std::vector<SubdomainName>>("block") = {outermost_block_name};
     457          45 :     params.set<MeshGeneratorName>("input") = _homogenized ? name() + "_2D" : name() + "_delbds";
     458             : 
     459          30 :     addMeshSubgenerator("BlockDeletionGenerator", name() + "_del_outer", params);
     460          15 :   }
     461             : 
     462             :   {
     463             :     // Invoke FlexiblePatternGenerator to triangulate deleted mesh interval
     464          55 :     auto params = _app.getFactory().getValidParams("FlexiblePatternGenerator");
     465             : 
     466          55 :     if (has_single_mesh_interval)
     467          80 :       params.set<std::vector<MeshGeneratorName>>("inputs") = {};
     468             :     else
     469             :     {
     470          75 :       params.set<std::vector<MeshGeneratorName>>("inputs") = {name() + "_del_outer"};
     471          15 :       params.set<std::vector<libMesh::Point>>("extra_positions") = {libMesh::Point(0, 0, 0)};
     472          30 :       params.set<std::vector<unsigned int>>("extra_positions_mg_indices") = {0};
     473             :     }
     474          55 :     params.set<bool>("use_auto_area_func") = true;
     475          55 :     params.set<bool>("verify_holes") = false;
     476         125 :     params.set<MooseEnum>("boundary_type") = (_mesh_geometry == "Hex") ? "HEXAGON" : "CARTESIAN";
     477          55 :     params.set<unsigned int>("boundary_sectors") =
     478          55 :         getReactorParam<unsigned int>(RGMB::num_sectors_flexible_stitching);
     479          55 :     params.set<Real>("boundary_size") = getReactorParam<Real>(RGMB::assembly_pitch);
     480          55 :     params.set<boundary_id_type>("external_boundary_id") = RGMB::PIN_BOUNDARY_ID_START + _pin_type;
     481         110 :     params.set<BoundaryName>("external_boundary_name") =
     482          55 :         RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX + std::to_string(_pin_type);
     483         110 :     params.set<SubdomainName>("background_subdomain_name") =
     484          55 :         outermost_block_name + RGMB::TRI_BLOCK_NAME_SUFFIX;
     485          55 :     params.set<unsigned short>("background_subdomain_id") = RGMB::PIN_BLOCK_ID_TRI_FLEXIBLE;
     486             : 
     487         110 :     addMeshSubgenerator("FlexiblePatternGenerator", name() + "_fpg", params);
     488          55 :   }
     489             :   {
     490             :     // Delete extra boundary created by FlexiblePatternGenerator
     491         110 :     auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
     492             : 
     493         220 :     params.set<MeshGeneratorName>("input") = name() + "_fpg";
     494             :     std::vector<BoundaryName> boundaries_to_delete = {};
     495          55 :     if (!has_single_mesh_interval)
     496          30 :       boundaries_to_delete.push_back(std::to_string(1));
     497          55 :     params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
     498             : 
     499         110 :     addMeshSubgenerator("BoundaryDeletionGenerator", name() + "_fpg_delbds", params);
     500          55 :   }
     501          55 : }
     502             : 
     503             : void
     504         968 : PinMeshGenerator::generateMetadata()
     505             : {
     506             :   // Store pin region ids and block names for id swap after extrusion if needed
     507             :   // by future mesh generators
     508             :   std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>> region_id_map{
     509        1936 :       {_pin_type, _region_ids}};
     510             : 
     511             :   // Declare mesh properties that need to be moved up to the assembly level
     512         968 :   if (_is_assembly)
     513             :   {
     514         145 :     declareMeshProperty(RGMB::assembly_type, _pin_type);
     515         145 :     declareMeshProperty(RGMB::background_block_name, std::vector<std::string>());
     516         145 :     declareMeshProperty(RGMB::duct_block_names, std::vector<std::vector<std::string>>());
     517         145 :     declareMeshProperty(RGMB::is_single_pin, _is_assembly);
     518         145 :     declareMeshProperty(RGMB::is_control_drum, false);
     519             :     // Following metadata is only relevant if an output mesh is generated by RGMB
     520             :     // because it pertains to region & block ids of elements in the output mesh
     521         145 :     if (!getReactorParam<bool>(RGMB::bypass_meshgen))
     522             :     {
     523             :       std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>> pin_region_id_map;
     524         136 :       pin_region_id_map.insert(
     525         136 :           std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
     526         136 :               region_id_map.begin()->first, region_id_map.begin()->second));
     527             :       declareMeshProperty(RGMB::pin_region_id_map, pin_region_id_map);
     528             :       std::map<subdomain_id_type, std::vector<std::vector<std::string>>> pin_block_name_map;
     529         272 :       pin_block_name_map.insert(std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(
     530         136 :           _pin_type, _block_names));
     531             :       declareMeshProperty(RGMB::pin_block_name_map, pin_block_name_map);
     532             :     }
     533             :   }
     534             :   // Declare mesh properties that are only relevant to pin meshes
     535             :   else
     536             :   {
     537         823 :     declareMeshProperty(RGMB::pin_type, _pin_type);
     538             :     declareMeshProperty(RGMB::pin_region_ids, region_id_map);
     539         823 :     declareMeshProperty(RGMB::pin_block_names, _block_names);
     540             :   }
     541             : 
     542             :   // Set metadata to describe pin attributes
     543         968 :   declareMeshProperty(RGMB::pitch, _pitch);
     544         968 :   declareMeshProperty(RGMB::is_homogenized, _homogenized);
     545         968 :   declareMeshProperty(RGMB::ring_radii, _ring_radii);
     546         968 :   declareMeshProperty(RGMB::duct_halfpitches, _duct_halfpitch);
     547        1918 :   declareMeshProperty(RGMB::extruded, _extrude && _mesh_dimensions == 3);
     548             : 
     549             :   unsigned int n_axial_levels =
     550         968 :       (_mesh_dimensions == 3)
     551             :           ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
     552        1773 :           : 1;
     553             :   std::vector<std::vector<subdomain_id_type>> ring_region_ids(
     554         968 :       n_axial_levels, std::vector<subdomain_id_type>(_ring_radii.size()));
     555             :   std::vector<std::vector<subdomain_id_type>> duct_region_ids(
     556         968 :       n_axial_levels, std::vector<subdomain_id_type>(_duct_halfpitch.size()));
     557         968 :   std::vector<subdomain_id_type> background_region_ids(n_axial_levels);
     558             : 
     559        2241 :   for (const auto axial_idx : make_range(n_axial_levels))
     560             :   {
     561        2098 :     for (const auto ring_idx : index_range(_ring_radii))
     562         825 :       ring_region_ids[axial_idx][ring_idx] = _region_ids[axial_idx][ring_idx];
     563             : 
     564        1273 :     background_region_ids[axial_idx] = _region_ids[axial_idx][_ring_radii.size()];
     565             : 
     566        1273 :     for (unsigned int duct_idx = _ring_radii.size() + 1;
     567        1690 :          duct_idx < _duct_halfpitch.size() + _ring_radii.size() + 1;
     568             :          ++duct_idx)
     569         417 :       duct_region_ids[axial_idx][duct_idx - _ring_radii.size() - 1] =
     570             :           _region_ids[axial_idx][duct_idx];
     571             :   }
     572             : 
     573             :   // Define mesh properties related to region ids
     574             :   declareMeshProperty(RGMB::ring_region_ids, ring_region_ids);
     575             :   declareMeshProperty(RGMB::background_region_id, background_region_ids);
     576             :   declareMeshProperty(RGMB::duct_region_ids, duct_region_ids);
     577        1936 : }
     578             : 
     579             : std::unique_ptr<MeshBase>
     580         828 : PinMeshGenerator::generate()
     581             : {
     582             :   // Must be called to free the ReactorMeshParams mesh
     583         828 :   freeReactorMeshParams();
     584             : 
     585             :   // If bypass_mesh is true, return a null mesh. In this mode, an output mesh is not
     586             :   // generated and only metadata is defined on the generator, so logic related to
     587             :   // generation of output mesh will not be called
     588         828 :   if (getReactorParam<bool>(RGMB::bypass_meshgen))
     589             :   {
     590             :     auto null_mesh = nullptr;
     591             :     return null_mesh;
     592             :   }
     593             : 
     594             :   // Update metadata at this point since values for these metadata only get set by PCCMG
     595             :   // at generate() stage
     596        1656 :   if (hasMeshProperty<Real>("max_radius_meta", name() + "_2D"))
     597             :   {
     598         788 :     const auto max_radius_meta = getMeshProperty<Real>("max_radius_meta", name() + "_2D");
     599        1576 :     setMeshProperty("max_radius_meta", max_radius_meta);
     600             :   }
     601        1656 :   if (hasMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D"))
     602             :   {
     603             :     const auto background_intervals_meta =
     604         788 :         getMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D");
     605        1576 :     setMeshProperty("background_intervals_meta", background_intervals_meta);
     606             :   }
     607        1656 :   if (hasMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D"))
     608             :   {
     609             :     const auto node_id_background_meta =
     610         788 :         getMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D");
     611        1576 :     setMeshProperty("node_id_background_meta", node_id_background_meta);
     612             :   }
     613             : 
     614             :   // This generate() method will be called once the subgenerators that we depend on
     615             :   // have been called. This is where we reassign subdomain ids/names according to what
     616             :   // the user has provided, and also where we set region_id, pin_type_id, and radial_id
     617             :   // extra element integers
     618             : 
     619             :   // Add region id, pin type id, and radial id to the mesh as element integers
     620         828 :   std::string region_id_name = "region_id";
     621         828 :   std::string pin_type_id_name = "pin_type_id";
     622         828 :   std::string assembly_type_id_name = "assembly_type_id";
     623         828 :   std::string plane_id_name = "plane_id";
     624         828 :   std::string radial_id_name = "radial_id";
     625             :   const std::string default_block_name =
     626         828 :       (_is_assembly ? RGMB::ASSEMBLY_BLOCK_NAME_PREFIX : RGMB::PIN_BLOCK_NAME_PREFIX) +
     627        1656 :       std::to_string(_pin_type);
     628             : 
     629         828 :   auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name);
     630         828 :   auto radial_id_int = getElemIntegerFromMesh(*(*_build_mesh), radial_id_name);
     631         828 :   auto pin_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), pin_type_id_name);
     632             :   unsigned int plane_id_int = 0;
     633             :   unsigned int assembly_type_id_int = 0;
     634         828 :   if (_extrude)
     635          36 :     plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
     636         828 :   if (_is_assembly)
     637         272 :     assembly_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), assembly_type_id_name);
     638             : 
     639             :   // Get next free block ID in mesh in case subdomain ids need to be remapped
     640         828 :   auto next_block_id = MooseMeshUtils::getNextFreeSubdomainID(*(*(_build_mesh)));
     641             :   std::map<std::string, SubdomainID> rgmb_name_id_map;
     642             : 
     643             :   // Loop through all elements and set regions ids, pin type id, and radial idx.
     644             :   // These element integers are also used to infer the block name for the region,
     645             :   // and block IDs/names will be reassigned on the pin mesh if necessary
     646       64208 :   for (auto & elem : (*_build_mesh)->active_element_ptr_range())
     647             :   {
     648       31276 :     const auto base_block_id = elem->subdomain_id();
     649       31276 :     const auto base_block_name = (*_build_mesh)->subdomain_name(base_block_id);
     650             : 
     651             :     // Check if block name has correct prefix
     652       62552 :     std::string prefix = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R";
     653       31276 :     if (!(base_block_name.find(prefix, 0) == 0))
     654             :       continue;
     655             :     // Radial index is integer value of substring after prefix
     656       31276 :     std::string radial_str = base_block_name.substr(prefix.length());
     657             : 
     658             :     // Filter out RGMB::TRI_BLOCK_NAME_SUFFIX if needed
     659       31276 :     const std::string suffix = RGMB::TRI_BLOCK_NAME_SUFFIX;
     660             :     const std::size_t found = radial_str.find(suffix);
     661       31276 :     if (found != std::string::npos)
     662       14984 :       radial_str.replace(found, suffix.length(), "");
     663       31276 :     const unsigned int radial_idx = std::stoi(radial_str);
     664             : 
     665             :     // Region id is inferred from z_id and radial_idx
     666       31276 :     dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
     667       31276 :     const subdomain_id_type elem_region_id = _region_ids[std::size_t(z_id)][radial_idx];
     668             : 
     669             :     // Set element integers
     670       31276 :     elem->set_extra_integer(region_id_int, elem_region_id);
     671       31276 :     elem->set_extra_integer(pin_type_id_int, _pin_type);
     672       31276 :     elem->set_extra_integer(radial_id_int, radial_idx);
     673       31276 :     if (_is_assembly)
     674       12462 :       elem->set_extra_integer(assembly_type_id_int, _pin_type);
     675             : 
     676             :     // Set element block name and block id
     677       31276 :     auto elem_block_name = default_block_name;
     678       31276 :     if (_has_block_names)
     679        7020 :       elem_block_name += "_" + _block_names[std::size_t(z_id)][radial_idx];
     680       27766 :     else if (getReactorParam<bool>(RGMB::region_id_as_block_name))
     681       29720 :       elem_block_name += "_REG" + std::to_string(elem_region_id);
     682       31276 :     if (elem->type() == TRI3 || elem->type() == PRISM6)
     683             :       elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
     684       62552 :     updateElementBlockNameId(
     685       31276 :         *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
     686         828 :   }
     687             : 
     688             :   // Mark mesh as not prepared, as block IDs were re-assigned in this method
     689         828 :   (*_build_mesh)->set_isnt_prepared();
     690             : 
     691             :   return std::move(*_build_mesh);
     692             : }

Generated by: LCOV version 1.14