LCOV - code coverage report
Current view: top level - src/meshgenerators - AssemblyMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose reactor: #31405 (292dce) with base fef103 Lines: 289 312 92.6 %
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 "AssemblyMeshGenerator.h"
      11             : 
      12             : #include "ReactorGeometryMeshBuilderBase.h"
      13             : #include "MooseApp.h"
      14             : #include "Factory.h"
      15             : #include "libmesh/elem.h"
      16             : #include "MooseMeshUtils.h"
      17             : 
      18             : registerMooseObject("ReactorApp", AssemblyMeshGenerator);
      19             : 
      20             : InputParameters
      21        1018 : AssemblyMeshGenerator::validParams()
      22             : {
      23        1018 :   auto params = ReactorGeometryMeshBuilderBase::validParams();
      24             : 
      25        2036 :   params.addRequiredParam<std::vector<MeshGeneratorName>>(
      26             :       "inputs", "The PinMeshGenerators that form the components of the assembly.");
      27             : 
      28        2036 :   params.addRequiredParam<subdomain_id_type>("assembly_type",
      29             :                                              "The integer ID for this assembly type definition");
      30             : 
      31        2036 :   params.addRequiredParam<std::vector<std::vector<unsigned int>>>(
      32             :       "pattern",
      33             :       "A double-indexed array starting with the upper-left corner where the index"
      34             :       "represents the layout of input pins in the assembly lattice.");
      35             : 
      36        2036 :   params.addRangeCheckedParam<std::vector<Real>>(
      37             :       "duct_halfpitch",
      38             :       "duct_halfpitch>0.0",
      39             :       "Distance(s) from center to duct(s) inner boundaries.");
      40             : 
      41        2036 :   params.addRangeCheckedParam<unsigned int>("background_intervals",
      42             :                                             "background_intervals>0",
      43             :                                             "Radial intervals in the assembly peripheral region.");
      44             : 
      45        2036 :   params.addRangeCheckedParam<std::vector<unsigned int>>(
      46             :       "duct_intervals", "duct_intervals>0", "Number of meshing intervals in each enclosing duct.");
      47             : 
      48        2036 :   params.addParam<std::vector<subdomain_id_type>>(
      49             :       "background_region_id",
      50             :       "The region id for the background area between the pins and the ducts to set region_id "
      51             :       "extra-element integer");
      52             : 
      53        2036 :   params.addParam<std::vector<std::vector<subdomain_id_type>>>(
      54             :       "duct_region_ids",
      55             :       "The region id for the ducts from innermost to outermost, to set region_id "
      56             :       "extra-element integer.");
      57             : 
      58        2036 :   params.addParam<std::vector<std::string>>("background_block_name",
      59             :                                             "The block names for the assembly background regions");
      60             : 
      61        2036 :   params.addParam<std::vector<std::vector<std::string>>>(
      62             :       "duct_block_names",
      63             :       "The block names for the assembly duct regions from innermost to outermost");
      64             : 
      65        2036 :   params.addParam<bool>("extrude",
      66        2036 :                         false,
      67             :                         "Determines if this is the final step in the geometry construction"
      68             :                         " and extrudes the 2D geometry to 3D. If this is true then this mesh "
      69             :                         "cannot be used in further mesh building in the Reactor workflow");
      70        2036 :   params.addParamNamesToGroup("background_region_id duct_region_ids assembly_type", "ID assigment");
      71        2036 :   params.addParamNamesToGroup("background_intervals background_region_id",
      72             :                               "Background specifications");
      73        2036 :   params.addParamNamesToGroup("duct_intervals duct_region_ids duct_halfpitch",
      74             :                               "Duct specifications");
      75             : 
      76        1018 :   params.addClassDescription("This AssemblyMeshGenerator object is designed to generate "
      77             :                              "assembly-like structures, with IDs, from a reactor geometry. "
      78             :                              "The assembly-like structures must consist of a full pattern of equal "
      79             :                              "sized pins from PinMeshGenerator. "
      80             :                              "A hexagonal assembly will be placed inside of a bounding hexagon "
      81             :                              "consisting of a background region and, optionally,"
      82             :                              " duct regions.");
      83             :   // depletion id generation params are added
      84        1018 :   addDepletionIDParams(params);
      85             : 
      86        1018 :   return params;
      87           0 : }
      88             : 
      89         509 : AssemblyMeshGenerator::AssemblyMeshGenerator(const InputParameters & parameters)
      90             :   : ReactorGeometryMeshBuilderBase(parameters),
      91         509 :     _inputs(getParam<std::vector<MeshGeneratorName>>("inputs")),
      92        1018 :     _assembly_type(getParam<subdomain_id_type>("assembly_type")),
      93        1018 :     _pattern(getParam<std::vector<std::vector<unsigned int>>>("pattern")),
      94        1755 :     _duct_sizes(isParamValid("duct_halfpitch") ? getParam<std::vector<Real>>("duct_halfpitch")
      95             :                                                : std::vector<Real>()),
      96         509 :     _background_intervals(
      97        1720 :         isParamValid("background_intervals") ? getParam<unsigned int>("background_intervals") : 0),
      98        1474 :     _duct_intervals(isParamValid("duct_intervals")
      99         509 :                         ? getParam<std::vector<unsigned int>>("duct_intervals")
     100             :                         : std::vector<unsigned int>()),
     101        1720 :     _background_region_id(isParamValid("background_region_id")
     102         509 :                               ? getParam<std::vector<subdomain_id_type>>("background_region_id")
     103             :                               : std::vector<subdomain_id_type>()),
     104        1474 :     _duct_region_ids(isParamValid("duct_region_ids")
     105         509 :                          ? getParam<std::vector<std::vector<subdomain_id_type>>>("duct_region_ids")
     106             :                          : std::vector<std::vector<subdomain_id_type>>()),
     107        1527 :     _extrude(getParam<bool>("extrude"))
     108             : {
     109             :   MeshGeneratorName reactor_params =
     110         509 :       MeshGeneratorName(getMeshProperty<std::string>(RGMB::reactor_params_name, _inputs[0]));
     111             :   // Check that MG name for reactor params is consistent across all assemblies
     112         809 :   for (unsigned int i = 1; i < _inputs.size(); i++)
     113         300 :     if (getMeshProperty<std::string>(RGMB::reactor_params_name, _inputs[i]) != reactor_params)
     114           0 :       mooseError("The name of all reactor_params objects should be identical across all input pins "
     115             :                  "in the assembly.\n");
     116             : 
     117             :   // Initialize ReactorMeshParams object stored in pin input
     118        1018 :   initializeReactorMeshParams(reactor_params);
     119             : 
     120         509 :   _geom_type = getReactorParam<std::string>(RGMB::mesh_geometry);
     121         509 :   _mesh_dimensions = getReactorParam<unsigned int>(RGMB::mesh_dimensions);
     122             : 
     123         509 :   if (_extrude && _mesh_dimensions != 3)
     124           0 :     paramError("extrude",
     125             :                "In order to extrude this mesh, ReactorMeshParams/dim needs to be set to 3\n");
     126        1152 :   if (_extrude && (!hasReactorParam<boundary_id_type>(RGMB::top_boundary_id) ||
     127         643 :                    !hasReactorParam<boundary_id_type>(RGMB::bottom_boundary_id)))
     128           0 :     mooseError("Both top_boundary_id and bottom_boundary_id must be provided in ReactorMeshParams "
     129             :                "if using extruded geometry");
     130             : 
     131             :   Real base_pitch = 0.0;
     132             : 
     133             :   // Check constitutent pins do not have shared pin_type ids
     134             :   std::map<subdomain_id_type, std::string> pin_map_type_to_name;
     135        1316 :   for (const auto i : index_range(_inputs))
     136             :   {
     137             :     auto pin = _inputs[i];
     138         809 :     if (i == 0)
     139         509 :       base_pitch = getMeshProperty<Real>(RGMB::pitch, pin);
     140             :     else
     141             :     {
     142         300 :       auto pitch = getMeshProperty<Real>(RGMB::pitch, pin);
     143         300 :       if (!MooseUtils::absoluteFuzzyEqual(pitch, base_pitch))
     144           0 :         mooseError("All pins within an assembly must have the same pitch");
     145             :     }
     146         809 :     if (getMeshProperty<bool>(RGMB::extruded, pin))
     147           0 :       mooseError("Pins that have already been extruded cannot be used in AssemblyMeshGenerator "
     148             :                  "definition.\n");
     149         809 :     const auto pin_type = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pin);
     150         811 :     if (pin_map_type_to_name.find(pin_type) != pin_map_type_to_name.end() &&
     151           2 :         pin_map_type_to_name[pin_type] != pin)
     152           2 :       mooseError("Constituent pins have shared pin_type ids but different names. Each uniquely "
     153             :                  "defined pin in PinMeshGenerator must have its own pin_type id.");
     154         807 :     pin_map_type_to_name[pin_type] = pin;
     155             :   }
     156         507 :   auto assembly_pitch = getReactorParam<Real>(RGMB::assembly_pitch);
     157             : 
     158             :   unsigned int n_axial_levels =
     159         507 :       (_mesh_dimensions == 3)
     160             :           ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
     161         964 :           : 1;
     162         507 :   if (_geom_type == "Square")
     163             :   {
     164             :     const auto ny = _pattern.size();
     165             :     const auto nx = _pattern[0].size();
     166         194 :     if (_background_region_id.size() == 0)
     167             :     {
     168         156 :       if ((!MooseUtils::absoluteFuzzyEqual(base_pitch * ny, assembly_pitch)) ||
     169         156 :           (!MooseUtils::absoluteFuzzyEqual(base_pitch * nx, assembly_pitch)))
     170           0 :         mooseError(
     171             :             "Assembly pitch must be equal to lattice dimension times pin pitch for Cartesian "
     172             :             "assemblies with no background region");
     173         156 :       if (_background_intervals > 0)
     174           0 :         mooseError("\"background_region_id\" must be defined if \"background_intervals\" is "
     175             :                    "greater than 0");
     176             :     }
     177             :     else
     178             :     {
     179          38 :       if ((base_pitch * ny > assembly_pitch) || (base_pitch * nx > assembly_pitch))
     180           0 :         mooseError(
     181             :             "Assembly pitch must be larger than lattice dimension times pin pitch for Cartesian "
     182             :             "assemblies with background region");
     183          38 :       if (_background_intervals == 0)
     184           0 :         mooseError("\"background_intervals\" must be greater than 0 if \"background_region_id\" is "
     185             :                    "defined");
     186          38 :       if (_background_region_id.size() != n_axial_levels)
     187           0 :         mooseError(
     188             :             "The size of background_region_id must be equal to the number of axial levels as "
     189             :             "defined in the ReactorMeshParams object");
     190             :     }
     191             :   }
     192             :   else
     193             :   {
     194         313 :     if ((_background_region_id.size() == 0) || _background_intervals == 0)
     195           0 :       mooseError("Hexagonal assemblies must have a background region defined");
     196         313 :     if (assembly_pitch / std::sin(M_PI / 3.0) < _pattern.size() * base_pitch)
     197           0 :       mooseError("Hexagonal diameter of assembly must be larger than the number of assembly rows "
     198             :                  "times the pin pitch");
     199             :     // Check size of background region id matches number of axial levels
     200         313 :     if (_background_region_id.size() != n_axial_levels)
     201           0 :       mooseError("The size of background_region_id must be equal to the number of axial levels as "
     202             :                  "defined in the ReactorMeshParams object");
     203             :   }
     204             : 
     205         507 :   if (_duct_sizes.size() != _duct_intervals.size())
     206           0 :     mooseError("If ducts are defined then \"duct_intervals\" and \"duct_region_ids\" must also be "
     207             :                "defined and of equal size.");
     208             : 
     209         507 :   if (_duct_sizes.size() != 0)
     210             :   {
     211             :     // Check size of duct region id matches number of axial levels
     212         228 :     if (_duct_region_ids.size() != n_axial_levels)
     213           0 :       mooseError("The size of duct_region_id must be equal to the number of axial levels as "
     214             :                  "defined in the ReactorMeshParams object");
     215         228 :     if (_duct_region_ids[0].size() != _duct_sizes.size())
     216           0 :       paramError("duct_halfpitch",
     217             :                  "If ducts are defined, then \"duct_intervals\" and \"duct_region_ids\" "
     218             :                  "must also be defined and of equal size.");
     219             :   }
     220             : 
     221             :   // Check whether block names are defined properly
     222        1014 :   if (isParamValid("background_block_name"))
     223             :   {
     224         128 :     if (getReactorParam<bool>(RGMB::region_id_as_block_name))
     225           2 :       paramError("background_block_name",
     226             :                  "If ReactorMeshParams/region_id_as_block_name is set, background_block_name "
     227             :                  "should not be specified in AssemblyMeshGenerator");
     228         126 :     _has_background_block_name = true;
     229         378 :     _background_block_name = getParam<std::vector<std::string>>("background_block_name");
     230         126 :     if (_background_region_id.size() != _background_block_name.size())
     231           0 :       mooseError("The size of background_block_name must match the size of background_region_id");
     232             :   }
     233             :   else
     234         379 :     _has_background_block_name = false;
     235             : 
     236        1010 :   if (isParamValid("duct_block_names"))
     237             :   {
     238          63 :     if (getReactorParam<bool>(RGMB::region_id_as_block_name))
     239           0 :       paramError("duct_block_names",
     240             :                  "If ReactorMeshParams/region_id_as_block_name is set, duct_block_names should not "
     241             :                  "be specified in AssemblyMeshGenerator");
     242          63 :     _has_duct_block_names = true;
     243         189 :     _duct_block_names = getParam<std::vector<std::vector<std::string>>>("duct_block_names");
     244          63 :     if (_duct_region_ids.size() != _duct_block_names.size())
     245           0 :       mooseError("The size of duct_block_names must match the size of duct_region_ids");
     246         189 :     for (const auto i : index_range(_duct_region_ids))
     247         126 :       if (_duct_region_ids[i].size() != _duct_block_names[i].size())
     248           0 :         mooseError("The size of duct_block_names must match the size of duct_region_ids");
     249             :   }
     250             :   else
     251         442 :     _has_duct_block_names = false;
     252             : 
     253             :   // No subgenerators will be called if option to bypass mesh generators is enabled
     254         505 :   if (!getReactorParam<bool>(RGMB::bypass_meshgen))
     255             :   {
     256             :     // Declare dependency of inputs to sub generator calls. If mesh generation
     257         429 :     declareMeshesForSub("inputs");
     258             : 
     259         429 :     _assembly_boundary_id = RGMB::ASSEMBLY_BOUNDARY_ID_START + _assembly_type;
     260         429 :     _assembly_boundary_name = RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX + std::to_string(_assembly_type);
     261             : 
     262             :     // Call PatternedHexMeshGenerator or PatternedCartesianMeshGenerator to stitch assembly
     263             :     {
     264             :       const auto patterned_mg_name =
     265         429 :           _geom_type == "Hex" ? "PatternedHexMeshGenerator" : "PatternedCartesianMeshGenerator";
     266         429 :       auto params = _app.getFactory().getValidParams(patterned_mg_name);
     267             : 
     268         429 :       if (_geom_type == "Hex")
     269             :       {
     270         275 :         params.set<Real>("hexagon_size") = getReactorParam<Real>(RGMB::assembly_pitch) / 2.0;
     271         550 :         params.set<MooseEnum>("hexagon_size_style") = "apothem";
     272             :       }
     273             :       else
     274             :       {
     275         154 :         if (_background_region_id.size() == 0)
     276         232 :           params.set<MooseEnum>("pattern_boundary") = "none";
     277             :         else
     278             :         {
     279          76 :           params.set<MooseEnum>("pattern_boundary") = "expanded";
     280          38 :           params.set<Real>("square_size") = getReactorParam<Real>(RGMB::assembly_pitch);
     281          38 :           params.set<bool>("uniform_mesh_on_sides") = true;
     282             :         }
     283             :       }
     284             : 
     285        1287 :       params.set<std::vector<std::string>>("id_name") = {"pin_id"};
     286         858 :       params.set<std::vector<MooseEnum>>("assign_type") = {
     287        1716 :           MooseEnum("cell", "cell")}; // give elems IDs relative to position in assembly
     288         429 :       params.set<std::vector<MeshGeneratorName>>("inputs") = _inputs;
     289         429 :       params.set<std::vector<std::vector<unsigned int>>>("pattern") = _pattern;
     290         429 :       params.set<bool>("create_outward_interface_boundaries") = false;
     291             : 
     292         429 :       if (_background_intervals > 0)
     293             :       {
     294         313 :         params.set<unsigned int>("background_intervals") = _background_intervals;
     295             :         // Initial block id used to define peripheral regions of assembly
     296             : 
     297             :         const auto background_block_name =
     298         313 :             RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R0";
     299             :         const auto background_block_id = RGMB::ASSEMBLY_BLOCK_ID_START;
     300         313 :         params.set<subdomain_id_type>("background_block_id") = background_block_id;
     301         626 :         params.set<SubdomainName>("background_block_name") = background_block_name;
     302             :       }
     303             : 
     304         429 :       if (_duct_sizes.size() > 0)
     305             :       {
     306             :         std::vector<subdomain_id_type> duct_block_ids;
     307             :         std::vector<SubdomainName> duct_block_names;
     308         398 :         for (const auto duct_it : index_range(_duct_region_ids[0]))
     309             :         {
     310         199 :           const auto duct_block_name = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX +
     311         398 :                                        std::to_string(_assembly_type) + "_R" +
     312         398 :                                        std::to_string(duct_it + 1);
     313         199 :           const auto duct_block_id = RGMB::ASSEMBLY_BLOCK_ID_START + duct_it + 1;
     314         199 :           duct_block_ids.push_back(duct_block_id);
     315         199 :           duct_block_names.push_back(duct_block_name);
     316             :         }
     317             : 
     318         199 :         params.set<std::vector<Real>>("duct_sizes") = _duct_sizes;
     319         199 :         params.set<std::vector<subdomain_id_type>>("duct_block_ids") = duct_block_ids;
     320         199 :         params.set<std::vector<SubdomainName>>("duct_block_names") = duct_block_names;
     321         199 :         params.set<std::vector<unsigned int>>("duct_intervals") = _duct_intervals;
     322         199 :       }
     323             : 
     324         429 :       params.set<boundary_id_type>("external_boundary_id") = _assembly_boundary_id;
     325         429 :       params.set<BoundaryName>("external_boundary_name") = _assembly_boundary_name;
     326             : 
     327         858 :       addMeshSubgenerator(patterned_mg_name, name() + "_pattern", params);
     328             : 
     329             :       // Pass mesh meta-data defined in subgenerator constructor to this MeshGenerator
     330         858 :       copyMeshProperty<bool>("is_control_drum_meta", name() + "_pattern");
     331         858 :       copyMeshProperty<std::vector<Point>>("control_drum_positions", name() + "_pattern");
     332         858 :       copyMeshProperty<std::vector<Real>>("control_drum_angles", name() + "_pattern");
     333         858 :       copyMeshProperty<std::vector<std::vector<Real>>>("control_drums_azimuthal_meta",
     334         858 :                                                        name() + "_pattern");
     335         858 :       copyMeshProperty<std::string>("position_file_name", name() + "_pattern");
     336         858 :       copyMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern");
     337         429 :     }
     338             : 
     339         429 :     std::string build_mesh_name = name() + "_delbds";
     340             : 
     341             :     // Remove outer pin sidesets created by PolygonConcentricCircleMeshGenerator
     342             :     {
     343             :       // Get outer boundaries of all constituent pins based on pin_type
     344             :       std::vector<BoundaryName> boundaries_to_delete = {};
     345        1587 :       for (const auto & pattern_x : _pattern)
     346             :       {
     347        3844 :         for (const auto & pattern_idx : pattern_x)
     348             :         {
     349        2686 :           const auto pin_name = _inputs[pattern_idx];
     350        2686 :           const auto pin_id = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pin_name);
     351             :           const BoundaryName boundary_name =
     352        5372 :               RGMB::PIN_BOUNDARY_NAME_PREFIX + std::to_string(pin_id);
     353        2686 :           if (!std::count(boundaries_to_delete.begin(), boundaries_to_delete.end(), boundary_name))
     354         676 :             boundaries_to_delete.push_back(boundary_name);
     355             :         }
     356             :       }
     357         858 :       auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
     358             : 
     359        1287 :       params.set<MeshGeneratorName>("input") = name() + "_pattern";
     360         429 :       params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
     361             : 
     362         858 :       addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
     363         429 :     }
     364             : 
     365             :     // Modify outermost mesh interval to enable flexible assembly stitching
     366         429 :     const auto use_flexible_stitching = getReactorParam<bool>(RGMB::flexible_assembly_stitching);
     367         429 :     if (use_flexible_stitching)
     368             :     {
     369          75 :       generateFlexibleAssemblyBoundaries();
     370         150 :       build_mesh_name = name() + "_fpg_delbds";
     371             :     }
     372             : 
     373        1105 :     for (auto pinMG : _inputs)
     374             :     {
     375             :       std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>> region_id_map =
     376             :           getMeshProperty<std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>>(
     377         676 :               RGMB::pin_region_ids, pinMG);
     378        1352 :       _pin_region_id_map.insert(
     379           0 :           std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
     380         676 :               region_id_map.begin()->first, region_id_map.begin()->second));
     381         676 :       subdomain_id_type pin_type_id = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pinMG);
     382             :       std::vector<std::vector<std::string>> pin_block_names =
     383         676 :           getMeshProperty<std::vector<std::vector<std::string>>>(RGMB::pin_block_names, pinMG);
     384        1352 :       _pin_block_name_map.insert(
     385         676 :           std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(pin_type_id,
     386             :                                                                               pin_block_names));
     387         676 :     }
     388             : 
     389         429 :     if (_extrude && _mesh_dimensions == 3)
     390         126 :       build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
     391             : 
     392             :     // Store final mesh subgenerator
     393         429 :     _build_mesh = &getMeshByName(build_mesh_name);
     394             :   }
     395             :   // If mesh generation should be bypassed, call getMeshes to resolve MeshGeneratorSystem
     396             :   // dependencies
     397             :   else
     398         152 :     auto input_meshes = getMeshes("inputs");
     399             : 
     400         505 :   generateMetadata();
     401        1792 : }
     402             : 
     403             : void
     404         505 : AssemblyMeshGenerator::generateMetadata()
     405             : {
     406             :   // Declare metadata for use in downstream mesh generators
     407         505 :   declareMeshProperty(RGMB::assembly_type, _assembly_type);
     408             :   declareMeshProperty(RGMB::pitch, getReactorParam<Real>(RGMB::assembly_pitch));
     409         505 :   declareMeshProperty(RGMB::background_region_id, _background_region_id);
     410         505 :   declareMeshProperty(RGMB::background_block_name, _background_block_name);
     411         505 :   declareMeshProperty(RGMB::duct_halfpitches, _duct_sizes);
     412         505 :   declareMeshProperty(RGMB::duct_region_ids, _duct_region_ids);
     413         505 :   declareMeshProperty(RGMB::duct_block_names, _duct_block_names);
     414         505 :   declareMeshProperty(RGMB::is_homogenized, false);
     415         505 :   declareMeshProperty(RGMB::is_single_pin, false);
     416         947 :   declareMeshProperty(RGMB::extruded, _extrude && _mesh_dimensions == 3);
     417         505 :   declareMeshProperty(RGMB::is_control_drum, false);
     418             :   // Following metadata is only relevant if an output mesh is generated by RGMB
     419         505 :   if (!getReactorParam<bool>(RGMB::bypass_meshgen))
     420             :   {
     421         429 :     declareMeshProperty(RGMB::pin_region_id_map, _pin_region_id_map);
     422         429 :     declareMeshProperty(RGMB::pin_block_name_map, _pin_block_name_map);
     423             :   }
     424             : 
     425             :   // Determine constituent pin names and define lattice as metadata
     426             :   std::vector<std::vector<int>> pin_name_lattice;
     427             :   std::vector<std::string> input_pin_names;
     428        1851 :   for (const auto i : index_range(_pattern))
     429             :   {
     430        1346 :     std::vector<int> pin_name_idx(_pattern[i].size());
     431        4444 :     for (const auto j : index_range(_pattern[i]))
     432             :     {
     433        3098 :       const auto input_pin_name = _inputs[_pattern[i][j]];
     434        3098 :       const auto it = std::find(input_pin_names.begin(), input_pin_names.end(), input_pin_name);
     435        3098 :       if (it == input_pin_names.end())
     436             :       {
     437         801 :         pin_name_idx[j] = input_pin_names.size();
     438         801 :         input_pin_names.push_back(input_pin_name);
     439             :       }
     440             :       else
     441        2297 :         pin_name_idx[j] = it - input_pin_names.begin();
     442             :     }
     443        1346 :     pin_name_lattice.push_back(pin_name_idx);
     444        1346 :   }
     445             :   declareMeshProperty(RGMB::pin_names, input_pin_names);
     446             :   declareMeshProperty(RGMB::pin_lattice, pin_name_lattice);
     447         505 : }
     448             : 
     449             : void
     450          75 : AssemblyMeshGenerator::generateFlexibleAssemblyBoundaries()
     451             : {
     452             :   // Assemblies that invoke this method have constituent pin lattice, delete outermost background or
     453             :   // duct region (if present)
     454             :   SubdomainName block_to_delete = "";
     455          75 :   if (_background_region_id.size() == 0)
     456           0 :     mooseError("Attempting to use flexible stitching on assembly " + name() +
     457             :                " that does not have a background region. This is not yet supported.");
     458          75 :   const auto radial_index = _duct_region_ids.size() == 0 ? 0 : _duct_region_ids[0].size();
     459         225 :   block_to_delete = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R" +
     460          75 :                     std::to_string(radial_index);
     461             : 
     462             :   {
     463             :     // Invoke BlockDeletionGenerator to delete outermost mesh interval of assembly
     464         150 :     auto params = _app.getFactory().getValidParams("BlockDeletionGenerator");
     465             : 
     466         225 :     params.set<std::vector<SubdomainName>>("block") = {block_to_delete};
     467         225 :     params.set<MeshGeneratorName>("input") = name() + "_delbds";
     468             : 
     469         150 :     addMeshSubgenerator("BlockDeletionGenerator", name() + "_del_outer", params);
     470          75 :   }
     471             :   {
     472             :     // Invoke FlexiblePatternGenerator to triangulate deleted mesh region
     473         150 :     auto params = _app.getFactory().getValidParams("FlexiblePatternGenerator");
     474             : 
     475         375 :     params.set<std::vector<MeshGeneratorName>>("inputs") = {name() + "_del_outer"};
     476          75 :     params.set<std::vector<libMesh::Point>>("extra_positions") = {libMesh::Point(0, 0, 0)};
     477          75 :     params.set<std::vector<unsigned int>>("extra_positions_mg_indices") = {0};
     478          75 :     params.set<bool>("use_auto_area_func") = true;
     479         170 :     params.set<MooseEnum>("boundary_type") = (_geom_type == "Hex") ? "HEXAGON" : "CARTESIAN";
     480          75 :     params.set<unsigned int>("boundary_sectors") =
     481          75 :         getReactorParam<unsigned int>(RGMB::num_sectors_flexible_stitching);
     482          75 :     params.set<Real>("boundary_size") = getReactorParam<Real>(RGMB::assembly_pitch);
     483          75 :     params.set<boundary_id_type>("external_boundary_id") = _assembly_boundary_id;
     484          75 :     params.set<BoundaryName>("external_boundary_name") = _assembly_boundary_name;
     485         150 :     params.set<SubdomainName>("background_subdomain_name") =
     486          75 :         block_to_delete + RGMB::TRI_BLOCK_NAME_SUFFIX;
     487          75 :     params.set<bool>("verify_holes") = false;
     488          75 :     params.set<unsigned short>("background_subdomain_id") = RGMB::ASSEMBLY_BLOCK_ID_TRI_FLEXIBLE;
     489             : 
     490         150 :     addMeshSubgenerator("FlexiblePatternGenerator", name() + "_fpg", params);
     491          75 :   }
     492             :   {
     493             :     // Delete extra boundary created by FlexiblePatternGenerator
     494         150 :     auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
     495             : 
     496         225 :     params.set<MeshGeneratorName>("input") = name() + "_fpg";
     497         300 :     params.set<std::vector<BoundaryName>>("boundary_names") = {std::to_string(1)};
     498             : 
     499         150 :     addMeshSubgenerator("BoundaryDeletionGenerator", name() + "_fpg_delbds", params);
     500          75 :   }
     501          75 : }
     502             : 
     503             : std::unique_ptr<MeshBase>
     504         417 : AssemblyMeshGenerator::generate()
     505             : {
     506             :   // Must be called to free the ReactorMeshParams mesh
     507         417 :   freeReactorMeshParams();
     508             : 
     509             :   // If bypass_mesh is true, return a null mesh. In this mode, an output mesh is not
     510             :   // generated and only metadata is defined on the generator, so logic related to
     511             :   // generation of output mesh will not be called
     512         417 :   if (getReactorParam<bool>(RGMB::bypass_meshgen))
     513             :   {
     514             :     auto null_mesh = nullptr;
     515             :     return null_mesh;
     516             :   }
     517             : 
     518             :   // Update metadata at this point since values for these metadata only get set by PCCMG
     519             :   // at generate() stage
     520         834 :   if (hasMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern"))
     521             :   {
     522             :     const auto pattern_pitch_meta =
     523         417 :         getMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern");
     524         834 :     setMeshProperty("pattern_pitch_meta", pattern_pitch_meta);
     525             :   }
     526             : 
     527             :   // This generate() method will be called once the subgenerators that we depend on are
     528             :   // called. This is where we reassign subdomain ids/name in case they were merged when
     529             :   // stitching pins into an assembly. This is also where we set region_id and
     530             :   // assembly_type_id element integers.
     531             : 
     532             :   // Define all extra element names and integers
     533         417 :   std::string plane_id_name = "plane_id";
     534         417 :   std::string region_id_name = "region_id";
     535         417 :   std::string pin_type_id_name = "pin_type_id";
     536         417 :   std::string assembly_type_id_name = "assembly_type_id";
     537         417 :   std::string radial_id_name = "radial_id";
     538             :   const std::string default_block_name =
     539         417 :       RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type);
     540             : 
     541         417 :   auto pin_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), pin_type_id_name, true);
     542         417 :   auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name, true);
     543         417 :   auto radial_id_int = getElemIntegerFromMesh(*(*_build_mesh), radial_id_name, true);
     544             : 
     545         417 :   auto assembly_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), assembly_type_id_name);
     546             : 
     547             :   unsigned int plane_id_int = 0;
     548         417 :   if (_extrude)
     549         126 :     plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
     550             : 
     551             :   // Get next free block ID in mesh in case subdomain ids need to be remapped
     552         417 :   auto next_block_id = MooseMeshUtils::getNextFreeSubdomainID(*(*(_build_mesh)));
     553             :   std::map<std::string, SubdomainID> rgmb_name_id_map;
     554             : 
     555             :   // Loop through all mesh elements and set region ids and reassign block IDs/names
     556             :   // if they were merged during pin stitching
     557      311398 :   for (auto & elem : (*_build_mesh)->active_element_ptr_range())
     558             :   {
     559      155282 :     elem->set_extra_integer(assembly_type_id_int, _assembly_type);
     560      155282 :     const dof_id_type pin_type_id = elem->get_extra_integer(pin_type_id_int);
     561      155282 :     const dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
     562             : 
     563             :     // Element is part of a pin mesh
     564      155282 :     if (_pin_region_id_map.find(pin_type_id) != _pin_region_id_map.end())
     565             :     {
     566             :       // Get region ID from pin_type, z_id, and radial_idx
     567       81050 :       const dof_id_type radial_idx = elem->get_extra_integer(radial_id_int);
     568       81050 :       const auto elem_rid = _pin_region_id_map[pin_type_id][z_id][radial_idx];
     569       81050 :       elem->set_extra_integer(region_id_int, elem_rid);
     570             : 
     571             :       // Set element block name and block id
     572       81050 :       bool has_block_names = !_pin_block_name_map[pin_type_id].empty();
     573       81050 :       auto elem_block_name = default_block_name;
     574       81050 :       if (has_block_names)
     575       39960 :         elem_block_name += "_" + _pin_block_name_map[pin_type_id][z_id][radial_idx];
     576       61070 :       else if (getReactorParam<bool>(RGMB::region_id_as_block_name))
     577       43300 :         elem_block_name += "_REG" + std::to_string(elem_rid);
     578       81050 :       if (elem->type() == TRI3 || elem->type() == PRISM6)
     579             :         elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
     580      162100 :       updateElementBlockNameId(
     581       81050 :           *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
     582             :     }
     583             :     else
     584             :     {
     585             :       // Assembly peripheral element (background / duct), set subdomains according
     586             :       // to user preferences and set pin type id to RGMB::MAX_PIN_TYPE_ID - peripheral index
     587             :       // Region id is inferred from z_id and peripheral_idx
     588       74232 :       const auto base_block_id = elem->subdomain_id();
     589       74232 :       const auto base_block_name = (*_build_mesh)->subdomain_name(base_block_id);
     590             : 
     591             :       // Check if block name has correct prefix
     592      148464 :       std::string prefix = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R";
     593       74232 :       if (!(base_block_name.find(prefix, 0) == 0))
     594             :         continue;
     595             :       // Peripheral index is integer value of substring after prefix
     596      148464 :       const unsigned int peripheral_idx = std::stoi(base_block_name.substr(prefix.length()));
     597             : 
     598             :       bool is_background_region = peripheral_idx == 0;
     599             : 
     600       74232 :       subdomain_id_type pin_type = RGMB::MAX_PIN_TYPE_ID - peripheral_idx;
     601       74232 :       elem->set_extra_integer(pin_type_id_int, pin_type);
     602             : 
     603       74232 :       const auto elem_rid = (is_background_region ? _background_region_id[z_id]
     604       22510 :                                                   : _duct_region_ids[z_id][peripheral_idx - 1]);
     605       74232 :       elem->set_extra_integer(region_id_int, elem_rid);
     606             : 
     607             :       // Set element block name and block id
     608       74232 :       auto elem_block_name = default_block_name;
     609       74232 :       if (getReactorParam<bool>(RGMB::region_id_as_block_name))
     610      118152 :         elem_block_name += "_REG" + std::to_string(elem_rid);
     611       15156 :       else if (is_background_region && _has_background_block_name)
     612        8424 :         elem_block_name += "_" + _background_block_name[z_id];
     613        5976 :       else if (!is_background_region && _has_duct_block_names)
     614        3888 :         elem_block_name += "_" + _duct_block_names[z_id][peripheral_idx - 1];
     615       74232 :       if (elem->type() == TRI3 || elem->type() == PRISM6)
     616             :         elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
     617      148464 :       updateElementBlockNameId(
     618       74232 :           *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
     619             :     }
     620         417 :   }
     621             : 
     622         834 :   if (getParam<bool>("generate_depletion_id"))
     623             :   {
     624          36 :     const MooseEnum option = getParam<MooseEnum>("depletion_id_type");
     625          18 :     addDepletionId(*(*_build_mesh), option, DepletionIDGenerationLevel::Assembly, _extrude);
     626          18 :   }
     627             : 
     628             :   // Mark mesh as not prepared, as block IDs were re-assigned in this method
     629         417 :   (*_build_mesh)->set_isnt_prepared();
     630             : 
     631             :   return std::move(*_build_mesh);
     632             : }

Generated by: LCOV version 1.14