https://mooseframework.inl.gov
PinMeshGenerator.C
Go to the documentation of this file.
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 
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 
24 
27 {
29 
30  params.addRequiredParam<MeshGeneratorName>(
31  "reactor_params",
32  "The ReactorMeshParams MeshGenerator that is the basis for this component conformal mesh.");
33 
34  params.addRequiredParam<subdomain_id_type>("pin_type",
35  "The integer ID for this pin type definition");
36 
37  params.addRequiredRangeCheckedParam<Real>(
38  "pitch", "pitch>0.0", "The pitch for the outermost boundary polygon");
39 
40  params.addRangeCheckedParam<unsigned int>(
41  "num_sectors", "num_sectors>0", "Number of azimuthal sectors in each quadrant");
42 
43  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  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  params.addRangeCheckedParam<std::vector<unsigned int>>(
54  "mesh_intervals",
55  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  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  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  params.addParam<bool>("extrude",
73  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  params.addParam<bool>(
78  "homogenized", false, "Determines whether homogenized pin mesh should be generated");
79  params.addParam<bool>(
80  "use_as_assembly", false, "Determines whether pin mesh should be used as an assembly mesh");
81 
82  params.addParam<bool>(
83  "quad_center_elements", true, "Whether the center elements are quad or triangular.");
84  params.addParamNamesToGroup("region_ids pin_type", "ID assigment");
85  params.addParamNamesToGroup(
86  "mesh_intervals ring_radii num_sectors pin_type homogenized use_as_assembly",
87  "Pin specifications");
88  params.addParamNamesToGroup("mesh_intervals duct_halfpitch num_sectors", "Duct specifications");
89 
90  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
99 
100  return params;
101 }
102 
104  : ReactorGeometryMeshBuilderBase(parameters),
105  _pin_type(getParam<subdomain_id_type>("pin_type")),
106  _pitch(getParam<Real>("pitch")),
107  _num_sectors(isParamValid("num_sectors") ? getParam<unsigned int>("num_sectors") : 0),
108  _ring_radii(isParamValid("ring_radii") ? getParam<std::vector<Real>>("ring_radii")
109  : std::vector<Real>()),
110  _duct_halfpitch(isParamValid("duct_halfpitch") ? getParam<std::vector<Real>>("duct_halfpitch")
111  : std::vector<Real>()),
112  _intervals(getParam<std::vector<unsigned int>>("mesh_intervals")),
113  _region_ids(isParamValid("region_ids")
114  ? getParam<std::vector<std::vector<subdomain_id_type>>>("region_ids")
115  : std::vector<std::vector<subdomain_id_type>>()),
116  _extrude(getParam<bool>("extrude")),
117  _quad_center(getParam<bool>("quad_center_elements")),
118  _homogenized(getParam<bool>("homogenized")),
119  _is_assembly(getParam<bool>("use_as_assembly"))
120 {
121  // Initialize ReactorMeshParams object
122  initializeReactorMeshParams(getParam<MeshGeneratorName>("reactor_params"));
123 
124  _mesh_dimensions = getReactorParam<unsigned int>(RGMB::mesh_dimensions);
125  _mesh_geometry = getReactorParam<std::string>(RGMB::mesh_geometry);
126 
127  if (_is_assembly)
128  {
129  auto assembly_pitch = getReactorParam<Real>(RGMB::assembly_pitch);
130  if (assembly_pitch != _pitch)
131  mooseError("Pitch defined in PinMeshGenerator must match assembly_pitch defined in "
132  "ReactorMeshParams if use_as_assembly is set to true");
133  }
134 
135  if (_extrude && _mesh_dimensions != 3)
136  paramError("extrude",
137  "In order to extrude this mesh, ReactorMeshParams/dim needs to be set to 3\n");
138  if (_extrude && (!hasReactorParam<boundary_id_type>(RGMB::top_boundary_id) ||
139  !hasReactorParam<boundary_id_type>(RGMB::bottom_boundary_id)))
140  mooseError("Both top_boundary_id and bottom_boundary_id must be provided in ReactorMeshParams "
141  "if using extruded geometry");
142 
143  if (_homogenized)
144  {
145  if (_mesh_geometry == "Square")
146  mooseError("Homogenization in PinMeshGenerator is only supported for hexagonal geometries");
147  const std::vector<std::string> disallowed_parameters = {
148  "num_sectors", "ring_radii", "duct_halfpitch", "mesh_intervals"};
149  for (const auto & parameter : disallowed_parameters)
150  if (parameters.isParamSetByUser(parameter))
151  paramError(parameter,
152  "Parameter " + parameter + " should not be defined for a homogenized pin mesh");
153  }
154  else
155  {
156  if (_num_sectors == 0)
157  mooseError(
158  "Number of sectors must be assigned with parameter num_sectors for non-homogenized pins");
159  if (_intervals.size() != (_ring_radii.size() + _duct_halfpitch.size() + 1))
160  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  if (isParamValid("region_ids"))
167  {
168  unsigned int n_axial_levels =
169  (_mesh_dimensions == 3)
170  ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
171  : 1;
172  if (_region_ids.size() != n_axial_levels)
173  mooseError("The size of region IDs must be equal to the number of axial levels as defined in "
174  "the ReactorMeshParams object");
175  if (_region_ids[0].size() != (_ring_radii.size() + _duct_halfpitch.size() + 1))
176  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  mooseError("Region IDs must be assigned with parameter region_ids");
182  }
183  if (isParamValid("block_names"))
184  {
185  if (getReactorParam<bool>(RGMB::region_id_as_block_name))
186  paramError("block_names",
187  "If ReactorMeshParams/region_id_as_block_name is set, block_names should not be "
188  "specified in PinMeshGenerator");
189  _has_block_names = true;
190  _block_names = getParam<std::vector<std::vector<std::string>>>("block_names");
191  if (_region_ids.size() != _block_names.size())
192  mooseError("The size of block_names must match the size of region_ids");
193  for (const auto i : index_range(_region_ids))
194  if (_region_ids[i].size() != _block_names[i].size())
195  mooseError("The size of block_names must match the size of region_ids");
196  }
197  else
198  _has_block_names = false;
199 
200  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  if (!getReactorParam<bool>(RGMB::bypass_meshgen))
205  {
206  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  bool skip_assembly_generation = _is_assembly && use_flexible_stitching;
212 
213  auto params = _app.getFactory().getValidParams("SimpleHexagonGenerator");
214 
215  params.set<Real>("hexagon_size") = _pitch / 2.0;
216  params.set<boundary_id_type>("external_boundary_id") =
218  const auto boundary_name =
220  std::to_string(_pin_type);
221  params.set<BoundaryName>("external_boundary_name") = boundary_name;
222  params.set<std::vector<subdomain_id_type>>("block_id") = {
224  params.set<MooseEnum>("element_type") = _quad_center ? "QUAD" : "TRI";
225  auto block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R0";
226  if (!_quad_center)
227  block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
228  params.set<std::vector<SubdomainName>>("block_name") = {block_name};
229 
230  if (!skip_assembly_generation)
231  {
232  build_mesh_name = name() + "_2D";
233  addMeshSubgenerator("SimpleHexagonGenerator", build_mesh_name, params);
234  }
235  }
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  for (const auto i : index_range(_intervals))
250  {
251  const auto block_name =
252  RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R" + std::to_string(i);
253  const auto block_id = RGMB::PIN_BLOCK_ID_START + i;
254 
255  if (i < _ring_radii.size())
256  {
257  ring_intervals.push_back(_intervals[i]);
258  ring_blk_ids.push_back(block_id);
259  ring_blk_names.push_back(block_name);
260  }
261  else if (i > _ring_radii.size())
262  {
263  duct_intervals.push_back(_intervals[i]);
264  duct_blk_ids.push_back(block_id);
265  duct_blk_names.push_back(block_name);
266  }
267  else
268  {
269  background_intervals = _intervals[i];
270  background_blk_ids.push_back(block_id);
271  background_blk_names.push_back(block_name);
272  }
273  }
274  if (ring_intervals.size() > 0)
275  {
276  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  if (_quad_center)
282  {
283  ring_blk_ids.insert(ring_blk_ids.begin(), ring_blk_ids.front());
284  ring_blk_names.insert(ring_blk_names.begin(), ring_blk_names.front());
285  }
286  else
287  {
288  const auto block_name = ring_blk_names.front() + RGMB::TRI_BLOCK_NAME_SUFFIX;
289  const auto block_id = RGMB::PIN_BLOCK_ID_TRI;
290  ring_blk_ids.insert(ring_blk_ids.begin(), block_id);
291  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  else if (!_quad_center)
296  {
297  ring_blk_ids[0] = RGMB::PIN_BLOCK_ID_TRI;
298  ring_blk_names[0] += RGMB::TRI_BLOCK_NAME_SUFFIX;
299  }
300  }
301  else
302  {
303  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  if (_quad_center)
309  {
310  background_blk_ids.insert(background_blk_ids.begin(), background_blk_ids.front());
311  background_blk_names.insert(background_blk_names.begin(), background_blk_names.front());
312  }
313  else
314  {
315  const auto block_name = background_blk_names.front() + RGMB::TRI_BLOCK_NAME_SUFFIX;
316  const auto block_id = RGMB::PIN_BLOCK_ID_TRI;
317  background_blk_ids.insert(background_blk_ids.begin(), block_id);
318  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  else if (!_quad_center)
324  {
325  background_blk_ids[0] = RGMB::PIN_BLOCK_ID_TRI;
326  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  _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  auto params = _app.getFactory().getValidParams("PolygonConcentricCircleMeshGenerator");
343  params.set<bool>("preserve_volumes") = true;
344  params.set<bool>("quad_center_elements") = _quad_center;
345  params.set<MooseEnum>("polygon_size_style") = "apothem";
346  params.set<Real>("polygon_size") = _pitch / 2.0;
347  params.set<boundary_id_type>("external_boundary_id") =
349  const auto boundary_name = (_is_assembly ? RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX
351  std::to_string(_pin_type);
352  params.set<BoundaryName>("external_boundary_name") = boundary_name;
353  bool flat_side_up = (_mesh_geometry == "Square");
354  params.set<bool>("flat_side_up") = flat_side_up;
355  params.set<bool>("create_outward_interface_boundaries") = false;
356 
357  const auto num_sides = (_mesh_geometry == "Square") ? 4 : 6;
358  params.set<unsigned int>("num_sides") = num_sides;
359  params.set<std::vector<unsigned int>>("num_sectors_per_side") =
360  std::vector<unsigned int>(num_sides, _num_sectors);
361 
362  if (ring_intervals.size() > 0)
363  {
364  params.set<std::vector<Real>>("ring_radii") = _ring_radii;
365  params.set<std::vector<subdomain_id_type>>("ring_block_ids") = ring_blk_ids;
366  params.set<std::vector<SubdomainName>>("ring_block_names") = ring_blk_names;
367  params.set<std::vector<unsigned int>>("ring_intervals") = ring_intervals;
368  }
369 
370  params.set<std::vector<subdomain_id_type>>("background_block_ids") = background_blk_ids;
371  params.set<std::vector<SubdomainName>>("background_block_names") = background_blk_names;
372  params.set<unsigned int>("background_intervals") = background_intervals;
373 
374  if (duct_intervals.size() > 0)
375  {
376  params.set<MooseEnum>("duct_sizes_style") = "apothem";
377  params.set<std::vector<Real>>("duct_sizes") = _duct_halfpitch;
378  params.set<std::vector<subdomain_id_type>>("duct_block_ids") = duct_blk_ids;
379  params.set<std::vector<SubdomainName>>("duct_block_names") = duct_blk_names;
380  params.set<std::vector<unsigned int>>("duct_intervals") = duct_intervals;
381  }
382 
383  addMeshSubgenerator("PolygonConcentricCircleMeshGenerator", name() + "_2D", params);
384  }
385 
386  // Remove extra sidesets created by PolygonConcentricCircleMeshGenerator
387  {
388  auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
389 
390  params.set<MeshGeneratorName>("input") = name() + "_2D";
391 
392  auto num_sides = (_mesh_geometry == "Square") ? 4 : 6;
393  std::vector<BoundaryName> boundaries_to_delete = {};
394  for (const auto i : make_range(num_sides))
395  boundaries_to_delete.insert(boundaries_to_delete.end(),
396  {std::to_string(10001 + i), std::to_string(15001 + i)});
397  params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
398 
399  build_mesh_name = name() + "_delbds";
400  addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
401  }
402  }
403  }
404 
405  // For pin acting as assembly, modify outermost mesh interval to enable flexible assembly
406  // stitching
407  if (_is_assembly && use_flexible_stitching)
408  {
410  build_mesh_name = name() + "_fpg_delbds";
411  }
412 
413  // Pass mesh meta-data defined in subgenerator constructor to this MeshGenerator
414  if (hasMeshProperty<Real>("pitch_meta", name() + "_2D"))
415  copyMeshProperty<Real>("pitch_meta", name() + "_2D");
416  if (hasMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", name() + "_2D"))
417  copyMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", name() + "_2D");
418  if (hasMeshProperty<Real>("max_radius_meta", name() + "_2D"))
419  copyMeshProperty<Real>("max_radius_meta", name() + "_2D");
420  if (hasMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D"))
421  copyMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D");
422  if (hasMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D"))
423  copyMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D");
424 
425  if (_is_assembly)
426  declareMeshProperty("pattern_pitch_meta", getReactorParam<Real>(RGMB::assembly_pitch));
427  else if (hasMeshProperty<Real>("pattern_pitch_meta", name() + "_2D"))
428  copyMeshProperty<Real>("pattern_pitch_meta", name() + "_2D");
429  declareMeshProperty("is_control_drum_meta", false);
430 
431  if (_extrude)
432  build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
433 
434  // Store final mesh subgenerator
435  _build_mesh = &getMeshByName(build_mesh_name);
436  }
437 
439 }
440 
441 void
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  if (_homogenized || (_intervals.size() == 1))
450  {
451  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  outermost_block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R" +
457  std::to_string(_intervals.size() - 1);
458  has_single_mesh_interval = false;
459 
460  // Invoke BlockDeletionGenerator to delete outermost mesh interval of assembly
461  auto params = _app.getFactory().getValidParams("BlockDeletionGenerator");
462 
463  params.set<std::vector<SubdomainName>>("block") = {outermost_block_name};
464  params.set<MeshGeneratorName>("input") = _homogenized ? name() + "_2D" : name() + "_delbds";
465 
466  addMeshSubgenerator("BlockDeletionGenerator", name() + "_del_outer", params);
467  }
468 
469  {
470  // Invoke FlexiblePatternGenerator to triangulate deleted mesh interval
471  auto params = _app.getFactory().getValidParams("FlexiblePatternGenerator");
472 
473  if (has_single_mesh_interval)
474  params.set<std::vector<MeshGeneratorName>>("inputs") = {};
475  else
476  {
477  params.set<std::vector<MeshGeneratorName>>("inputs") = {name() + "_del_outer"};
478  params.set<std::vector<libMesh::Point>>("extra_positions") = {libMesh::Point(0, 0, 0)};
479  params.set<std::vector<unsigned int>>("extra_positions_mg_indices") = {0};
480  }
481  params.set<bool>("use_auto_area_func") = true;
482  params.set<bool>("verify_holes") = false;
483  params.set<MooseEnum>("boundary_type") = (_mesh_geometry == "Hex") ? "HEXAGON" : "CARTESIAN";
484  params.set<unsigned int>("boundary_sectors") =
485  getReactorParam<unsigned int>(RGMB::num_sectors_flexible_stitching);
486  params.set<Real>("boundary_size") = getReactorParam<Real>(RGMB::assembly_pitch);
487  params.set<boundary_id_type>("external_boundary_id") = RGMB::PIN_BOUNDARY_ID_START + _pin_type;
488  params.set<BoundaryName>("external_boundary_name") =
490  params.set<SubdomainName>("background_subdomain_name") =
491  outermost_block_name + RGMB::TRI_BLOCK_NAME_SUFFIX;
492  params.set<unsigned short>("background_subdomain_id") = RGMB::PIN_BLOCK_ID_TRI_FLEXIBLE;
493 
494  addMeshSubgenerator("FlexiblePatternGenerator", name() + "_fpg", params);
495  }
496  {
497  // Delete extra boundary created by FlexiblePatternGenerator
498  auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
499 
500  params.set<MeshGeneratorName>("input") = name() + "_fpg";
501  std::vector<BoundaryName> boundaries_to_delete = {};
502  if (!has_single_mesh_interval)
503  boundaries_to_delete.push_back(std::to_string(1));
504  params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
505 
506  addMeshSubgenerator("BoundaryDeletionGenerator", name() + "_fpg_delbds", params);
507  }
508 }
509 
510 void
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{
517 
518  // Declare mesh properties that need to be moved up to the assembly level
519  if (_is_assembly)
520  {
522  declareMeshProperty(RGMB::background_block_name, std::vector<std::string>());
523  declareMeshProperty(RGMB::duct_block_names, std::vector<std::vector<std::string>>());
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  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  pin_region_id_map.insert(
532  std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
533  region_id_map.begin()->first, region_id_map.begin()->second));
535  std::map<subdomain_id_type, std::vector<std::vector<std::string>>> pin_block_name_map;
536  pin_block_name_map.insert(std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(
539  }
540  }
541  // Declare mesh properties that are only relevant to pin meshes
542  else
543  {
547  }
548 
549  // Set metadata to describe pin attributes
555 
556  unsigned int n_axial_levels =
557  (_mesh_dimensions == 3)
558  ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
559  : 1;
560  std::vector<std::vector<subdomain_id_type>> ring_region_ids(
561  n_axial_levels, std::vector<subdomain_id_type>(_ring_radii.size()));
562  std::vector<std::vector<subdomain_id_type>> duct_region_ids(
563  n_axial_levels, std::vector<subdomain_id_type>(_duct_halfpitch.size()));
564  std::vector<subdomain_id_type> background_region_ids(n_axial_levels);
565 
566  for (const auto axial_idx : make_range(n_axial_levels))
567  {
568  for (const auto ring_idx : index_range(_ring_radii))
569  ring_region_ids[axial_idx][ring_idx] = _region_ids[axial_idx][ring_idx];
570 
571  background_region_ids[axial_idx] = _region_ids[axial_idx][_ring_radii.size()];
572 
573  for (unsigned int duct_idx = _ring_radii.size() + 1;
574  duct_idx < _duct_halfpitch.size() + _ring_radii.size() + 1;
575  ++duct_idx)
576  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
582  declareMeshProperty(RGMB::background_region_id, background_region_ids);
584 }
585 
586 std::unique_ptr<MeshBase>
588 {
589  // Must be called to free the ReactorMeshParams mesh
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  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  if (hasMeshProperty<Real>("max_radius_meta", name() + "_2D"))
604  {
605  const auto max_radius_meta = getMeshProperty<Real>("max_radius_meta", name() + "_2D");
606  setMeshProperty("max_radius_meta", max_radius_meta);
607  }
608  if (hasMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D"))
609  {
610  const auto background_intervals_meta =
611  getMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D");
612  setMeshProperty("background_intervals_meta", background_intervals_meta);
613  }
614  if (hasMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D"))
615  {
616  const auto node_id_background_meta =
617  getMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D");
618  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  std::string region_id_name = "region_id";
628  std::string pin_type_id_name = "pin_type_id";
629  std::string assembly_type_id_name = "assembly_type_id";
630  std::string plane_id_name = "plane_id";
631  std::string radial_id_name = "radial_id";
632  const std::string default_block_name =
634  std::to_string(_pin_type);
635 
636  auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name);
637  auto radial_id_int = getElemIntegerFromMesh(*(*_build_mesh), radial_id_name);
638  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  if (_extrude)
642  plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
643  if (_is_assembly)
644  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  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  for (auto & elem : (*_build_mesh)->active_element_ptr_range())
654  {
655  const auto base_block_id = elem->subdomain_id();
656  const auto base_block_name = (*_build_mesh)->subdomain_name(base_block_id);
657 
658  // Check if block name has correct prefix
659  std::string prefix = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R";
660  if (!(base_block_name.find(prefix, 0) == 0))
661  continue;
662  // Radial index is integer value of substring after prefix
663  std::string radial_str = base_block_name.substr(prefix.length());
664 
665  // Filter out RGMB::TRI_BLOCK_NAME_SUFFIX if needed
666  const std::string suffix = RGMB::TRI_BLOCK_NAME_SUFFIX;
667  const std::size_t found = radial_str.find(suffix);
668  if (found != std::string::npos)
669  radial_str.replace(found, suffix.length(), "");
670  const unsigned int radial_idx = std::stoi(radial_str);
671 
672  // Region id is inferred from z_id and radial_idx
673  dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
674  const subdomain_id_type elem_region_id = _region_ids[std::size_t(z_id)][radial_idx];
675 
676  // Set element integers
677  elem->set_extra_integer(region_id_int, elem_region_id);
678  elem->set_extra_integer(pin_type_id_int, _pin_type);
679  elem->set_extra_integer(radial_id_int, radial_idx);
680  if (_is_assembly)
681  elem->set_extra_integer(assembly_type_id_int, _pin_type);
682 
683  // Set element block name and block id
684  auto elem_block_name = default_block_name;
685  if (_has_block_names)
686  elem_block_name += "_" + _block_names[std::size_t(z_id)][radial_idx];
687  else if (getReactorParam<bool>(RGMB::region_id_as_block_name))
688  elem_block_name += "_REG" + std::to_string(elem_region_id);
689  if (elem->type() == TRI3 || elem->type() == PRISM6)
690  elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
692  *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
693  }
694 
695  // Mark mesh as not prepared, as block IDs were re-assigned in this method
696  (*_build_mesh)->unset_is_prepared();
697 
698  return std::move(*_build_mesh);
699 }
700 
701 std::unique_ptr<CSG::CSGBase>
703 {
704  // Must be called to free the ReactorMeshParams CSGBase object
706 
707  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  for (const auto & radius : _ring_radii)
714  {
715  const auto surf_name = name() + "_radial_ring_" + std::to_string(radial_index);
716  std::unique_ptr<CSG::CSGSurface> ring_surf_ptr =
717  std::make_unique<CSG::CSGZCylinder>(surf_name, 0, 0, radius);
718  const auto & ring_surf = csg_obj->addSurface(std::move(ring_surf_ptr));
719  surfaces_by_radial_region.push_back({ring_surf});
720  ++radial_index;
721  }
722 
723  // Add surfaces corresponding to pin ducts
724  for (const auto & duct_halfpitch : _duct_halfpitch)
725  {
726  const auto & duct_surfaces =
727  getOuterRadialSurfacesForUnitCell(radial_index, duct_halfpitch, *csg_obj);
728  surfaces_by_radial_region.push_back(duct_surfaces);
729  ++radial_index;
730  }
731 
732  // Add surfaces corresponding to outer pin boundary
733  const auto & duct_surfaces =
734  getOuterRadialSurfacesForUnitCell(radial_index, _pitch / 2., *csg_obj);
735  surfaces_by_radial_region.push_back(duct_surfaces);
736 
737  // Define all radial regions
738  std::vector<CSG::CSGRegion> radial_regions;
739  CSG::CSGRegion inner_region, outer_region;
740  for (const auto i : index_range(surfaces_by_radial_region))
741  {
742  const auto & radial_surfaces = surfaces_by_radial_region[i];
743  CSG::CSGRegion radial_region;
744  bool is_last_radial_region = i == surfaces_by_radial_region.size() - 1;
745  if (inner_region.getRegionType() == CSG::CSGRegion::RegionType::EMPTY)
746  {
747  if (!is_last_radial_region)
748  {
749  // We are in the innermost radial region, the radial region is inner_region
750  inner_region = CSGUtils::getInnerRegion(radial_surfaces, Point(0, 0, 0));
751  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  outer_region = ~inner_region;
759  inner_region = CSGUtils::getInnerRegion(radial_surfaces, Point(0, 0, 0));
760  radial_region = is_last_radial_region ? outer_region : (inner_region & outer_region);
761  }
762  radial_regions.push_back(radial_region);
763  }
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  const auto extruded_pin = _mesh_dimensions == 3;
769  if (extruded_pin)
770  {
771  surfaces_by_axial_region = getAxialPlaneSurfaces(*csg_obj);
772  for (const auto i : make_range(surfaces_by_axial_region.size()))
773  if (i != 0)
774  {
775  CSG::CSGRegion axial_region;
776  const auto & lower_surf = surfaces_by_axial_region[i - 1].get();
777  if (lower_surf != surfaces_by_axial_region.front())
778  axial_region = +lower_surf;
779  const auto & upper_surf = surfaces_by_axial_region[i].get();
780  if (upper_surf != surfaces_by_axial_region.back())
781  {
782  if (axial_region.getRegionType() == CSG::CSGRegion::RegionType::EMPTY)
783  axial_region = -upper_surf;
784  else
785  axial_region &= -upper_surf;
786  }
787  axial_regions.push_back(axial_region);
788  }
789  }
790 
791  // Define all cells within pin domain and add to separate universe
792  const auto & pin_univ = csg_obj->createUniverse(name() + "_univ");
793  for (const auto i : index_range(radial_regions))
794  {
795  for (const auto j : make_range(extruded_pin ? axial_regions.size() : 1))
796  {
797  auto cell_region = radial_regions[i];
798  auto cell_name = name() + "_cell_radial_" + std::to_string(i);
799  const auto region_id = _region_ids[j][i];
800  const auto mat_name = "rgmb_region_" + std::to_string(region_id);
801  if (extruded_pin)
802  {
803  // update name and region with axial info only if extruded
804  const auto axial_region = axial_regions[j];
805  if (axial_region.getRegionType() != CSG::CSGRegion::RegionType::EMPTY)
806  {
807  if (cell_region.getRegionType() != CSG::CSGRegion::RegionType::EMPTY)
808  cell_region &= axial_region;
809  else
810  cell_region = axial_region;
811  }
812  cell_name += "_axial_" + std::to_string(j);
813  }
814  csg_obj->createCell(cell_name, mat_name, cell_region, &pin_univ);
815  }
816  }
817 
818  // Create new cell to bound universe based on pin outer boundaries and add this cell to the root
819  // universe
820  auto pin_region = CSGUtils::getInnerRegion(surfaces_by_radial_region.back(), Point(0, 0, 0));
821  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  auto axial_region = +lowest_axial_surf & -highest_axial_surf;
826  pin_region &= axial_region;
827  }
828  csg_obj->createCell(name() + "_root_cell", pin_univ, pin_region);
829 
830  return csg_obj;
831 }
std::unique_ptr< MeshBase > & getMeshByName(const MeshGeneratorName &mesh_generator_name)
const SubdomainName ASSEMBLY_BLOCK_NAME_PREFIX
const subdomain_id_type _pin_type
The id number for this pin type.
void updateElementBlockNameId(MeshBase &input_mesh, Elem *elem, std::map< std::string, SubdomainID > &name_id_map, std::string elem_block_name, SubdomainID &next_free_id)
Updates the block names and ids of the element in an input mesh according to a map of block name to b...
static const std::string duct_block_names
static const std::string background_region_id
T & setMeshProperty(const std::string &data_name, Args &&... args)
static const std::string assembly_type
PinMeshGenerator(const InputParameters &parameters)
void paramError(const std::string &param, Args... args) const
static const std::string region_id_as_block_name
unsigned int _mesh_dimensions
The number of dimensions the mesh is ultimately going to have (2 or 3, declared in the ReactorMeshPar...
static const std::string is_single_pin
void generateFlexibleAssemblyBoundaries()
std::unique_ptr< CSG::CSGBase > generateCSG() override
const InputParameters & parameters() const
unsigned int getElemIntegerFromMesh(MeshBase &input_mesh, std::string extra_int_name, bool should_exist=false)
Initializes extra element integer from id name for a given mesh and throws an error if it should exis...
T & set(const std::string &name, bool quiet_mode=false)
const subdomain_id_type PIN_BLOCK_ID_START
static const std::string mesh_geometry
void initializeReactorMeshParams(const std::string reactor_param_name)
Initializes and checks validity of ReactorMeshParams mesh generator object.
InputParameters getValidParams(const std::string &name) const
std::vector< std::reference_wrapper< const CSG::CSGSurface > > getAxialPlaneSurfaces(CSG::CSGBase &csg_obj)
Get CSGSurfaces corresponding to axial planes of the extruded RGMB mesh.
Mesh generator for defining a reactor pin with background and duct regions, with the option to be 2-D...
static const std::string background_block_name
static const std::string assembly_pitch
bool _homogenized
Whether the resulting pin mesh should be homogenized.
std::vector< unsigned int > _intervals
The number of mesh intervals in a radial division starting from the center.
static const std::string pin_type
registerMooseObject("ReactorApp", PinMeshGenerator)
const SubdomainName TRI_BLOCK_NAME_SUFFIX
bool _quad_center
Whether the centermost elements in the pin should be quad elements as opposed to tri elements...
Factory & getFactory()
TRI3
bool _has_block_names
Whether block names have been provided by user.
static const std::string pin_region_id_map
const BoundaryName PIN_BOUNDARY_NAME_PREFIX
static const std::string extruded
const T & getReactorParam(const std::string &param_name)
Returns reference of parameter in ReactorMeshParams object.
const std::string & name() const
static const std::string duct_halfpitches
int8_t boundary_id_type
static const std::string mesh_dimensions
void addMeshSubgenerator(const std::string &type, const std::string &name, Ts... extra_input_parameters)
MeshGeneratorName callExtrusionMeshSubgenerators(const MeshGeneratorName input_mesh_name)
Calls mesh subgenerators related to extrusion, renaming of top / bottom boundaries, and defining plane IDs.
static InputParameters validParams()
RegionType getRegionType() const
static const std::string pitch
static constexpr boundary_id_type PIN_BOUNDARY_ID_START
void freeReactorParamsMesh()
Releases the mesh obtained in _reactor_params_mesh.
std::vector< std::vector< std::string > > _block_names
2-D vector (axial outer indexing, radial inner indexing) used to set block names of pin mesh elements...
const unsigned int _num_sectors
The number of azimuthal divisions.
const SubdomainName PIN_BLOCK_NAME_PREFIX
static const std::string top_boundary_id
std::vector< std::reference_wrapper< const CSG::CSGSurface > > getOuterRadialSurfacesForUnitCell(unsigned int radial_index, Real halfpitch, CSG::CSGBase &csg_obj)
Get CSGSurfaces corresponding to hexagonal or square region with given halfpitch and centered around ...
CSG::CSGRegion getInnerRegion(const std::vector< std::reference_wrapper< const CSG::CSGSurface >> &surfaces, const libMesh::Point &origin)
static void setHasGenerateCSG(InputParameters &params)
const bool _extrude
Whether this mesh should be extruded to 3-D, making it the final structure in the reactor mesh...
std::unique_ptr< MeshBase > generate() override
static const std::string is_homogenized
void freeReactorParamsCSG()
Releases the CSG base object obtained in _reactor_params_csg.
std::vector< Real > _ring_radii
The outer radii of concentric rings in the pin.
static const std::string is_control_drum
bool isParamSetByUser(const std::string &name) const
static const std::string pin_region_ids
const double radius
static const std::string flexible_assembly_stitching
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Real _pitch
The face-to-face size of this pin.
static const std::string ring_radii
MooseApp & _app
const subdomain_id_type PIN_BLOCK_ID_TRI_FLEXIBLE
static const std::string pin_block_names
const subdomain_id_type PIN_BLOCK_ID_TRI
static const std::string axial_mesh_intervals
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
A base class that contains common members for Reactor Geometry Mesh Builder mesh generators.
const BoundaryName ASSEMBLY_BOUNDARY_NAME_PREFIX
T & declareMeshProperty(const std::string &data_name, Args &&... args)
std::string _mesh_geometry
The type of geometry that is being described (Square or Hex, declared in the ReactorMeshParams object...
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
bool isParamValid(const std::string &name) const
static const std::string bypass_meshgen
bool _is_assembly
Whether the resulting pin mesh should also be used as an assembly mesh.
PRISM6
SubdomainID getNextFreeSubdomainID(MeshBase &input_mesh)
T & copyMeshProperty(const std::string &target_data_name, const std::string &source_data_name, const std::string &source_mesh)
static const std::string num_sectors_flexible_stitching
bool hasMeshProperty(const std::string &data_name, const std::string &prefix) const
static const std::string duct_region_ids
void ErrorVector unsigned int
auto index_range(const T &sizable)
static const std::string bottom_boundary_id
std::unique_ptr< MeshBase > * _build_mesh
The final mesh that is generated by the subgenerators; This mesh is generated by the subgenerators wi...
static const std::string pin_block_name_map
const std::vector< Real > _duct_halfpitch
The inner apothem of any surrounding ducts in the pin.
std::vector< std::vector< subdomain_id_type > > _region_ids
2-D vector (axial outer indexing, radial inner indexing) used to set "region_id" extra-element intege...
static const std::string ring_region_ids
uint8_t dof_id_type