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

Generated by: LCOV version 1.14