https://mooseframework.inl.gov
AssemblyMeshGenerator.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 "AssemblyMeshGenerator.h"
11 
13 #include "MooseApp.h"
14 #include "Factory.h"
15 #include "libmesh/elem.h"
16 #include "MooseMeshUtils.h"
17 #include "CSGCartesianLattice.h"
18 #include "CSGHexagonalLattice.h"
19 #include "CSGUtils.h"
20 
22 
25 {
27 
28  params.addRequiredParam<std::vector<MeshGeneratorName>>(
29  "inputs", "The PinMeshGenerators that form the components of the assembly.");
30 
31  params.addRequiredParam<subdomain_id_type>("assembly_type",
32  "The integer ID for this assembly type definition");
33 
34  params.addRequiredParam<std::vector<std::vector<unsigned int>>>(
35  "pattern",
36  "A double-indexed array starting with the upper-left corner where the index"
37  "represents the layout of input pins in the assembly lattice.");
38 
39  params.addRangeCheckedParam<std::vector<Real>>(
40  "duct_halfpitch",
41  "duct_halfpitch>0.0",
42  "Distance(s) from center to duct(s) inner boundaries.");
43 
44  params.addRangeCheckedParam<unsigned int>("background_intervals",
45  "background_intervals>0",
46  "Radial intervals in the assembly peripheral region.");
47 
48  params.addRangeCheckedParam<std::vector<unsigned int>>(
49  "duct_intervals", "duct_intervals>0", "Number of meshing intervals in each enclosing duct.");
50 
51  params.addParam<std::vector<subdomain_id_type>>(
52  "background_region_id",
53  "The region id for the background area between the pins and the ducts to set region_id "
54  "extra-element integer");
55 
56  params.addParam<std::vector<std::vector<subdomain_id_type>>>(
57  "duct_region_ids",
58  "The region id for the ducts from innermost to outermost, to set region_id "
59  "extra-element integer.");
60 
61  params.addParam<std::vector<std::string>>("background_block_name",
62  "The block names for the assembly background regions");
63 
64  params.addParam<std::vector<std::vector<std::string>>>(
65  "duct_block_names",
66  "The block names for the assembly duct regions from innermost to outermost");
67 
68  params.addParam<bool>("extrude",
69  false,
70  "Determines if this is the final step in the geometry construction"
71  " and extrudes the 2D geometry to 3D. If this is true then this mesh "
72  "cannot be used in further mesh building in the Reactor workflow");
73  params.addParamNamesToGroup("background_region_id duct_region_ids assembly_type", "ID assigment");
74  params.addParamNamesToGroup("background_intervals background_region_id",
75  "Background specifications");
76  params.addParamNamesToGroup("duct_intervals duct_region_ids duct_halfpitch",
77  "Duct specifications");
78 
79  params.addClassDescription("This AssemblyMeshGenerator object is designed to generate "
80  "assembly-like structures, with IDs, from a reactor geometry. "
81  "The assembly-like structures must consist of a full pattern of equal "
82  "sized pins from PinMeshGenerator. "
83  "A hexagonal assembly will be placed inside of a bounding hexagon "
84  "consisting of a background region and, optionally,"
85  " duct regions.");
86  // depletion id generation params are added
87  addDepletionIDParams(params);
88 
89  // Declare that this generator has a generateCSG method
91 
92  return params;
93 }
94 
96  : ReactorGeometryMeshBuilderBase(parameters),
97  _inputs(getParam<std::vector<MeshGeneratorName>>("inputs")),
98  _assembly_type(getParam<subdomain_id_type>("assembly_type")),
99  _pattern(getParam<std::vector<std::vector<unsigned int>>>("pattern")),
100  _duct_sizes(isParamValid("duct_halfpitch") ? getParam<std::vector<Real>>("duct_halfpitch")
101  : std::vector<Real>()),
102  _background_intervals(
103  isParamValid("background_intervals") ? getParam<unsigned int>("background_intervals") : 0),
104  _duct_intervals(isParamValid("duct_intervals")
105  ? getParam<std::vector<unsigned int>>("duct_intervals")
106  : std::vector<unsigned int>()),
107  _background_region_id(isParamValid("background_region_id")
108  ? getParam<std::vector<subdomain_id_type>>("background_region_id")
109  : std::vector<subdomain_id_type>()),
110  _duct_region_ids(isParamValid("duct_region_ids")
111  ? getParam<std::vector<std::vector<subdomain_id_type>>>("duct_region_ids")
112  : std::vector<std::vector<subdomain_id_type>>()),
113  _extrude(getParam<bool>("extrude"))
114 {
115  MeshGeneratorName reactor_params =
116  MeshGeneratorName(getMeshProperty<std::string>(RGMB::reactor_params_name, _inputs[0]));
117  // Check that MG name for reactor params is consistent across all assemblies
118  for (unsigned int i = 1; i < _inputs.size(); i++)
119  if (getMeshProperty<std::string>(RGMB::reactor_params_name, _inputs[i]) != reactor_params)
120  mooseError("The name of all reactor_params objects should be identical across all input pins "
121  "in the assembly.\n");
122 
123  // Initialize ReactorMeshParams object stored in pin input
124  initializeReactorMeshParams(reactor_params);
125 
126  _geom_type = getReactorParam<std::string>(RGMB::mesh_geometry);
127  _mesh_dimensions = getReactorParam<unsigned int>(RGMB::mesh_dimensions);
128 
129  if (_extrude && _mesh_dimensions != 3)
130  paramError("extrude",
131  "In order to extrude this mesh, ReactorMeshParams/dim needs to be set to 3\n");
132  if (_extrude && (!hasReactorParam<boundary_id_type>(RGMB::top_boundary_id) ||
133  !hasReactorParam<boundary_id_type>(RGMB::bottom_boundary_id)))
134  mooseError("Both top_boundary_id and bottom_boundary_id must be provided in ReactorMeshParams "
135  "if using extruded geometry");
136 
137  Real base_pitch = 0.0;
138 
139  // Check constitutent pins do not have shared pin_type ids
140  std::map<subdomain_id_type, std::string> pin_map_type_to_name;
141  for (const auto i : index_range(_inputs))
142  {
143  auto pin = _inputs[i];
144  if (i == 0)
145  base_pitch = getMeshProperty<Real>(RGMB::pitch, pin);
146  else
147  {
148  auto pitch = getMeshProperty<Real>(RGMB::pitch, pin);
149  if (!MooseUtils::absoluteFuzzyEqual(pitch, base_pitch))
150  mooseError("All pins within an assembly must have the same pitch");
151  }
152  if (getMeshProperty<bool>(RGMB::extruded, pin))
153  mooseError("Pins that have already been extruded cannot be used in AssemblyMeshGenerator "
154  "definition.\n");
155  const auto pin_type = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pin);
156  if (pin_map_type_to_name.find(pin_type) != pin_map_type_to_name.end() &&
157  pin_map_type_to_name[pin_type] != pin)
158  mooseError("Constituent pins have shared pin_type ids but different names. Each uniquely "
159  "defined pin in PinMeshGenerator must have its own pin_type id.");
160  pin_map_type_to_name[pin_type] = pin;
161  }
162  auto assembly_pitch = getReactorParam<Real>(RGMB::assembly_pitch);
163 
164  unsigned int n_axial_levels =
165  (_mesh_dimensions == 3)
166  ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
167  : 1;
168  if (_geom_type == "Square")
169  {
170  const auto ny = _pattern.size();
171  const auto nx = _pattern[0].size();
172  if (_background_region_id.size() == 0)
173  {
174  if ((!MooseUtils::absoluteFuzzyEqual(base_pitch * ny, assembly_pitch)) ||
175  (!MooseUtils::absoluteFuzzyEqual(base_pitch * nx, assembly_pitch)))
176  mooseError(
177  "Assembly pitch must be equal to lattice dimension times pin pitch for Cartesian "
178  "assemblies with no background region");
179  if (_background_intervals > 0)
180  mooseError("\"background_region_id\" must be defined if \"background_intervals\" is "
181  "greater than 0");
182  }
183  else
184  {
185  if ((base_pitch * ny > assembly_pitch) || (base_pitch * nx > assembly_pitch))
186  mooseError(
187  "Assembly pitch must be larger than lattice dimension times pin pitch for Cartesian "
188  "assemblies with background region");
189  if (_background_intervals == 0)
190  mooseError("\"background_intervals\" must be greater than 0 if \"background_region_id\" is "
191  "defined");
192  if (_background_region_id.size() != n_axial_levels)
193  mooseError(
194  "The size of background_region_id must be equal to the number of axial levels as "
195  "defined in the ReactorMeshParams object");
196  }
197  }
198  else
199  {
200  if ((_background_region_id.size() == 0) || _background_intervals == 0)
201  mooseError("Hexagonal assemblies must have a background region defined");
202  if (assembly_pitch / std::sin(M_PI / 3.0) < _pattern.size() * base_pitch)
203  mooseError("Hexagonal diameter of assembly must be larger than the number of assembly rows "
204  "times the pin pitch");
205  // Check size of background region id matches number of axial levels
206  if (_background_region_id.size() != n_axial_levels)
207  mooseError("The size of background_region_id must be equal to the number of axial levels as "
208  "defined in the ReactorMeshParams object");
209  }
210 
211  if (_duct_sizes.size() != _duct_intervals.size())
212  mooseError("If ducts are defined then \"duct_intervals\" and \"duct_region_ids\" must also be "
213  "defined and of equal size.");
214 
215  if (_duct_sizes.size() != 0)
216  {
217  // Check size of duct region id matches number of axial levels
218  if (_duct_region_ids.size() != n_axial_levels)
219  mooseError("The size of duct_region_id must be equal to the number of axial levels as "
220  "defined in the ReactorMeshParams object");
221  if (_duct_region_ids[0].size() != _duct_sizes.size())
222  paramError("duct_halfpitch",
223  "If ducts are defined, then \"duct_intervals\" and \"duct_region_ids\" "
224  "must also be defined and of equal size.");
225  }
226 
227  // Check whether block names are defined properly
228  if (isParamValid("background_block_name"))
229  {
230  if (getReactorParam<bool>(RGMB::region_id_as_block_name))
231  paramError("background_block_name",
232  "If ReactorMeshParams/region_id_as_block_name is set, background_block_name "
233  "should not be specified in AssemblyMeshGenerator");
235  _background_block_name = getParam<std::vector<std::string>>("background_block_name");
236  if (_background_region_id.size() != _background_block_name.size())
237  mooseError("The size of background_block_name must match the size of background_region_id");
238  }
239  else
241 
242  if (isParamValid("duct_block_names"))
243  {
244  if (getReactorParam<bool>(RGMB::region_id_as_block_name))
245  paramError("duct_block_names",
246  "If ReactorMeshParams/region_id_as_block_name is set, duct_block_names should not "
247  "be specified in AssemblyMeshGenerator");
248  _has_duct_block_names = true;
249  _duct_block_names = getParam<std::vector<std::vector<std::string>>>("duct_block_names");
250  if (_duct_region_ids.size() != _duct_block_names.size())
251  mooseError("The size of duct_block_names must match the size of duct_region_ids");
252  for (const auto i : index_range(_duct_region_ids))
253  if (_duct_region_ids[i].size() != _duct_block_names[i].size())
254  mooseError("The size of duct_block_names must match the size of duct_region_ids");
255  }
256  else
257  _has_duct_block_names = false;
258 
259  // No subgenerators will be called if option to bypass mesh generators is enabled
260  if (!getReactorParam<bool>(RGMB::bypass_meshgen))
261  {
262  // Declare dependency of inputs to sub generator calls. If mesh generation
263  declareMeshesForSub("inputs");
264 
267 
268  // Call PatternedHexMeshGenerator or PatternedCartesianMeshGenerator to stitch assembly
269  {
270  const auto patterned_mg_name =
271  _geom_type == "Hex" ? "PatternedHexMeshGenerator" : "PatternedCartesianMeshGenerator";
272  auto params = _app.getFactory().getValidParams(patterned_mg_name);
273 
274  if (_geom_type == "Hex")
275  {
276  params.set<Real>("hexagon_size") = getReactorParam<Real>(RGMB::assembly_pitch) / 2.0;
277  params.set<MooseEnum>("hexagon_size_style") = "apothem";
278  }
279  else
280  {
281  if (_background_region_id.size() == 0)
282  params.set<MooseEnum>("pattern_boundary") = "none";
283  else
284  {
285  params.set<MooseEnum>("pattern_boundary") = "expanded";
286  params.set<Real>("square_size") = getReactorParam<Real>(RGMB::assembly_pitch);
287  params.set<bool>("uniform_mesh_on_sides") = true;
288  }
289  }
290 
291  params.set<std::vector<std::string>>("id_name") = {"pin_id"};
292  params.set<std::vector<MooseEnum>>("assign_type") = {
293  MooseEnum("cell", "cell")}; // give elems IDs relative to position in assembly
294  params.set<std::vector<MeshGeneratorName>>("inputs") = _inputs;
295  params.set<std::vector<std::vector<unsigned int>>>("pattern") = _pattern;
296  params.set<bool>("create_outward_interface_boundaries") = false;
297 
298  if (_background_intervals > 0)
299  {
300  params.set<unsigned int>("background_intervals") = _background_intervals;
301  // Initial block id used to define peripheral regions of assembly
302 
303  const auto background_block_name =
304  RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R0";
305  const auto background_block_id = RGMB::ASSEMBLY_BLOCK_ID_START;
306  params.set<subdomain_id_type>("background_block_id") = background_block_id;
307  params.set<SubdomainName>("background_block_name") = background_block_name;
308  }
309 
310  if (_duct_sizes.size() > 0)
311  {
312  std::vector<subdomain_id_type> duct_block_ids;
313  std::vector<SubdomainName> duct_block_names;
314  for (const auto duct_it : index_range(_duct_region_ids[0]))
315  {
316  const auto duct_block_name = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX +
317  std::to_string(_assembly_type) + "_R" +
318  std::to_string(duct_it + 1);
319  const auto duct_block_id = RGMB::ASSEMBLY_BLOCK_ID_START + duct_it + 1;
320  duct_block_ids.push_back(duct_block_id);
321  duct_block_names.push_back(duct_block_name);
322  }
323 
324  params.set<std::vector<Real>>("duct_sizes") = _duct_sizes;
325  params.set<std::vector<subdomain_id_type>>("duct_block_ids") = duct_block_ids;
326  params.set<std::vector<SubdomainName>>("duct_block_names") = duct_block_names;
327  params.set<std::vector<unsigned int>>("duct_intervals") = _duct_intervals;
328  }
329 
330  params.set<boundary_id_type>("external_boundary_id") = _assembly_boundary_id;
331  params.set<BoundaryName>("external_boundary_name") = _assembly_boundary_name;
332 
333  addMeshSubgenerator(patterned_mg_name, name() + "_pattern", params);
334 
335  // Pass mesh meta-data defined in subgenerator constructor to this MeshGenerator
336  copyMeshProperty<bool>("is_control_drum_meta", name() + "_pattern");
337  copyMeshProperty<std::vector<Point>>("control_drum_positions", name() + "_pattern");
338  copyMeshProperty<std::vector<Real>>("control_drum_angles", name() + "_pattern");
339  copyMeshProperty<std::vector<std::vector<Real>>>("control_drums_azimuthal_meta",
340  name() + "_pattern");
341  copyMeshProperty<std::string>("position_file_name", name() + "_pattern");
342  copyMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern");
343  }
344 
345  std::string build_mesh_name = name() + "_delbds";
346 
347  // Remove outer pin sidesets created by PolygonConcentricCircleMeshGenerator
348  {
349  // Get outer boundaries of all constituent pins based on pin_type
350  std::vector<BoundaryName> boundaries_to_delete = {};
351  for (const auto & pattern_x : _pattern)
352  {
353  for (const auto & pattern_idx : pattern_x)
354  {
355  const auto pin_name = _inputs[pattern_idx];
356  const auto pin_id = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pin_name);
357  const BoundaryName boundary_name =
358  RGMB::PIN_BOUNDARY_NAME_PREFIX + std::to_string(pin_id);
359  if (!std::count(boundaries_to_delete.begin(), boundaries_to_delete.end(), boundary_name))
360  boundaries_to_delete.push_back(boundary_name);
361  }
362  }
363  auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
364 
365  params.set<MeshGeneratorName>("input") = name() + "_pattern";
366  params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
367 
368  addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
369  }
370 
371  // Modify outermost mesh interval to enable flexible assembly stitching
372  const auto use_flexible_stitching = getReactorParam<bool>(RGMB::flexible_assembly_stitching);
373  if (use_flexible_stitching)
374  {
376  build_mesh_name = name() + "_fpg_delbds";
377  }
378 
379  for (auto pinMG : _inputs)
380  {
381  std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>> region_id_map =
382  getMeshProperty<std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>>(
383  RGMB::pin_region_ids, pinMG);
384  _pin_region_id_map.insert(
385  std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
386  region_id_map.begin()->first, region_id_map.begin()->second));
387  subdomain_id_type pin_type_id = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pinMG);
388  std::vector<std::vector<std::string>> pin_block_names =
389  getMeshProperty<std::vector<std::vector<std::string>>>(RGMB::pin_block_names, pinMG);
390  _pin_block_name_map.insert(
391  std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(pin_type_id,
392  pin_block_names));
393  }
394 
395  if (_extrude && _mesh_dimensions == 3)
396  build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
397 
398  // Store final mesh subgenerator
399  _build_mesh = &getMeshByName(build_mesh_name);
400  }
401  // If mesh generation should be bypassed, call getMeshes to resolve MeshGeneratorSystem
402  // dependencies
403  else
404  auto input_meshes = getMeshes("inputs");
405 
406  // If we are in CSG only mode, store the CSGBase objects associated with input MG's
408  _input_csg_bases = getCSGBases("inputs");
409 
411 }
412 
413 void
415 {
416  // Declare metadata for use in downstream mesh generators
418  declareMeshProperty(RGMB::pitch, getReactorParam<Real>(RGMB::assembly_pitch));
428  // Following metadata is only relevant if an output mesh is generated by RGMB
429  if (!getReactorParam<bool>(RGMB::bypass_meshgen))
430  {
433  }
434 
435  // Determine constituent pin names and define lattice as metadata
436  std::vector<std::vector<int>> pin_name_lattice;
437  std::vector<std::string> input_pin_names;
438  for (const auto i : index_range(_pattern))
439  {
440  std::vector<int> pin_name_idx(_pattern[i].size());
441  for (const auto j : index_range(_pattern[i]))
442  {
443  const auto input_pin_name = _inputs[_pattern[i][j]];
444  const auto it = std::find(input_pin_names.begin(), input_pin_names.end(), input_pin_name);
445  if (it == input_pin_names.end())
446  {
447  pin_name_idx[j] = input_pin_names.size();
448  input_pin_names.push_back(input_pin_name);
449  }
450  else
451  pin_name_idx[j] = it - input_pin_names.begin();
452  }
453  pin_name_lattice.push_back(pin_name_idx);
454  }
455  declareMeshProperty(RGMB::pin_names, input_pin_names);
456  declareMeshProperty(RGMB::pin_lattice, pin_name_lattice);
457 }
458 
459 void
461 {
462  // Assemblies that invoke this method have constituent pin lattice, delete outermost background or
463  // duct region (if present)
464  SubdomainName block_to_delete = "";
465  if (_background_region_id.size() == 0)
466  mooseError("Attempting to use flexible stitching on assembly " + name() +
467  " that does not have a background region. This is not yet supported.");
468  const auto radial_index = _duct_region_ids.size() == 0 ? 0 : _duct_region_ids[0].size();
469  block_to_delete = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R" +
470  std::to_string(radial_index);
471 
472  {
473  // Invoke BlockDeletionGenerator to delete outermost mesh interval of assembly
474  auto params = _app.getFactory().getValidParams("BlockDeletionGenerator");
475 
476  params.set<std::vector<SubdomainName>>("block") = {block_to_delete};
477  params.set<MeshGeneratorName>("input") = name() + "_delbds";
478 
479  addMeshSubgenerator("BlockDeletionGenerator", name() + "_del_outer", params);
480  }
481  {
482  // Invoke FlexiblePatternGenerator to triangulate deleted mesh region
483  auto params = _app.getFactory().getValidParams("FlexiblePatternGenerator");
484 
485  params.set<std::vector<MeshGeneratorName>>("inputs") = {name() + "_del_outer"};
486  params.set<std::vector<libMesh::Point>>("extra_positions") = {libMesh::Point(0, 0, 0)};
487  params.set<std::vector<unsigned int>>("extra_positions_mg_indices") = {0};
488  params.set<bool>("use_auto_area_func") = true;
489  params.set<MooseEnum>("boundary_type") = (_geom_type == "Hex") ? "HEXAGON" : "CARTESIAN";
490  params.set<unsigned int>("boundary_sectors") =
491  getReactorParam<unsigned int>(RGMB::num_sectors_flexible_stitching);
492  params.set<Real>("boundary_size") = getReactorParam<Real>(RGMB::assembly_pitch);
493  params.set<boundary_id_type>("external_boundary_id") = _assembly_boundary_id;
494  params.set<BoundaryName>("external_boundary_name") = _assembly_boundary_name;
495  params.set<SubdomainName>("background_subdomain_name") =
496  block_to_delete + RGMB::TRI_BLOCK_NAME_SUFFIX;
497  params.set<bool>("verify_holes") = false;
498  params.set<unsigned short>("background_subdomain_id") = RGMB::ASSEMBLY_BLOCK_ID_TRI_FLEXIBLE;
499 
500  addMeshSubgenerator("FlexiblePatternGenerator", name() + "_fpg", params);
501  }
502  {
503  // Delete extra boundary created by FlexiblePatternGenerator
504  auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
505 
506  params.set<MeshGeneratorName>("input") = name() + "_fpg";
507  params.set<std::vector<BoundaryName>>("boundary_names") = {std::to_string(1)};
508 
509  addMeshSubgenerator("BoundaryDeletionGenerator", name() + "_fpg_delbds", params);
510  }
511 }
512 
513 std::unique_ptr<MeshBase>
515 {
516  // Must be called to free the ReactorMeshParams mesh
518 
519  // If bypass_mesh is true, return a null mesh. In this mode, an output mesh is not
520  // generated and only metadata is defined on the generator, so logic related to
521  // generation of output mesh will not be called
522  if (getReactorParam<bool>(RGMB::bypass_meshgen))
523  {
524  auto null_mesh = nullptr;
525  return null_mesh;
526  }
527 
528  // Update metadata at this point since values for these metadata only get set by PCCMG
529  // at generate() stage
530  if (hasMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern"))
531  {
532  const auto pattern_pitch_meta =
533  getMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern");
534  setMeshProperty("pattern_pitch_meta", pattern_pitch_meta);
535  }
536 
537  // This generate() method will be called once the subgenerators that we depend on are
538  // called. This is where we reassign subdomain ids/name in case they were merged when
539  // stitching pins into an assembly. This is also where we set region_id and
540  // assembly_type_id element integers.
541 
542  // Define all extra element names and integers
543  std::string plane_id_name = "plane_id";
544  std::string region_id_name = "region_id";
545  std::string pin_type_id_name = "pin_type_id";
546  std::string assembly_type_id_name = "assembly_type_id";
547  std::string radial_id_name = "radial_id";
548  const std::string default_block_name =
550 
551  auto pin_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), pin_type_id_name, true);
552  auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name, true);
553  auto radial_id_int = getElemIntegerFromMesh(*(*_build_mesh), radial_id_name, true);
554 
555  auto assembly_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), assembly_type_id_name);
556 
557  unsigned int plane_id_int = 0;
558  if (_extrude)
559  plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
560 
561  // Get next free block ID in mesh in case subdomain ids need to be remapped
562  auto next_block_id = MooseMeshUtils::getNextFreeSubdomainID(*(*(_build_mesh)));
563  std::map<std::string, SubdomainID> rgmb_name_id_map;
564 
565  // Loop through all mesh elements and set region ids and reassign block IDs/names
566  // if they were merged during pin stitching
567  for (auto & elem : (*_build_mesh)->active_element_ptr_range())
568  {
569  elem->set_extra_integer(assembly_type_id_int, _assembly_type);
570  const dof_id_type pin_type_id = elem->get_extra_integer(pin_type_id_int);
571  const dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
572 
573  // Element is part of a pin mesh
574  if (_pin_region_id_map.find(pin_type_id) != _pin_region_id_map.end())
575  {
576  // Get region ID from pin_type, z_id, and radial_idx
577  const dof_id_type radial_idx = elem->get_extra_integer(radial_id_int);
578  const auto elem_rid = _pin_region_id_map[pin_type_id][z_id][radial_idx];
579  elem->set_extra_integer(region_id_int, elem_rid);
580 
581  // Set element block name and block id
582  bool has_block_names = !_pin_block_name_map[pin_type_id].empty();
583  auto elem_block_name = default_block_name;
584  if (has_block_names)
585  elem_block_name += "_" + _pin_block_name_map[pin_type_id][z_id][radial_idx];
586  else if (getReactorParam<bool>(RGMB::region_id_as_block_name))
587  elem_block_name += "_REG" + std::to_string(elem_rid);
588  if (elem->type() == TRI3 || elem->type() == PRISM6)
589  elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
591  *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
592  }
593  else
594  {
595  // Assembly peripheral element (background / duct), set subdomains according
596  // to user preferences and set pin type id to RGMB::MAX_PIN_TYPE_ID - peripheral index
597  // Region id is inferred from z_id and peripheral_idx
598  const auto base_block_id = elem->subdomain_id();
599  const auto base_block_name = (*_build_mesh)->subdomain_name(base_block_id);
600 
601  // Check if block name has correct prefix
602  std::string prefix = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R";
603  if (!(base_block_name.find(prefix, 0) == 0))
604  continue;
605  // Peripheral index is integer value of substring after prefix
606  const unsigned int peripheral_idx = std::stoi(base_block_name.substr(prefix.length()));
607 
608  bool is_background_region = peripheral_idx == 0;
609 
611  elem->set_extra_integer(pin_type_id_int, pin_type);
612 
613  const auto elem_rid = (is_background_region ? _background_region_id[z_id]
614  : _duct_region_ids[z_id][peripheral_idx - 1]);
615  elem->set_extra_integer(region_id_int, elem_rid);
616 
617  // Set element block name and block id
618  auto elem_block_name = default_block_name;
619  if (getReactorParam<bool>(RGMB::region_id_as_block_name))
620  elem_block_name += "_REG" + std::to_string(elem_rid);
621  else if (is_background_region && _has_background_block_name)
622  elem_block_name += "_" + _background_block_name[z_id];
623  else if (!is_background_region && _has_duct_block_names)
624  elem_block_name += "_" + _duct_block_names[z_id][peripheral_idx - 1];
625  if (elem->type() == TRI3 || elem->type() == PRISM6)
626  elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
628  *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
629  }
630  }
631 
632  if (getParam<bool>("generate_depletion_id"))
633  {
634  const MooseEnum option = getParam<MooseEnum>("depletion_id_type");
636  }
637 
638  // Mark mesh as not prepared, as block IDs were re-assigned in this method
639  (*_build_mesh)->unset_is_prepared();
640 
641  return std::move(*_build_mesh);
642 }
643 
644 std::unique_ptr<CSG::CSGBase>
646 {
647  // Must be called to free the ReactorMeshParams CSGBase object
649 
650  auto csg_obj = std::make_unique<CSG::CSGBase>();
651 
652  // Combine all bases from PinMG inputs into this base. Root universes from
653  // inputs are renamed to a new universe name. These universes and their
654  // cells will be discarded, so that only the infinite pin universe is retained
655  std::unordered_map<unsigned int, std::string> univ_id_names;
656  std::vector<std::string> univs_to_discard;
657  for (const auto i : index_range(_inputs))
658  {
659  const auto input_univ_name_discard = _inputs[i] + "_root_univ";
660  const auto input_univ_name = _inputs[i] + "_univ";
661  csg_obj->joinOtherBase(std::move(*_input_csg_bases[i]), true, input_univ_name_discard);
662  univs_to_discard.push_back(input_univ_name_discard);
663  univ_id_names[i] = input_univ_name;
664  }
665 
666  // Discard root universes of the input pins and their cells
667  for (const auto & univ_name : univs_to_discard)
668  {
669  const auto & universe_to_delete = csg_obj->getUniverseByName(univ_name);
670  const auto cells_to_delete = universe_to_delete.getAllCells();
671  csg_obj->deleteUniverse(universe_to_delete);
672  for (const auto & cell : cells_to_delete)
673  csg_obj->deleteCell(cell.get());
674  }
675 
676  // Build the universe pattern for the assembly lattice from the input pattern
677  std::vector<std::vector<std::reference_wrapper<const CSG::CSGUniverse>>> universe_pattern;
678  for (const auto & row : _pattern)
679  {
680  std::vector<std::reference_wrapper<const CSG::CSGUniverse>> universe_row;
681  for (const auto & univ_id : row)
682  {
683  const auto & lattice_univ = csg_obj->getUniverseByName(univ_id_names[univ_id]);
684  universe_row.push_back(lattice_univ);
685  }
686  universe_pattern.push_back(universe_row);
687  }
688 
689  // Define axial boundaries for problem
690  std::vector<std::reference_wrapper<const CSG::CSGSurface>> surfaces_by_axial_region;
691  CSG::CSGRegion axial_extent;
692  const auto extruded_assembly = _mesh_dimensions == 3;
693  if (extruded_assembly)
694  {
695  surfaces_by_axial_region = getAxialPlaneSurfaces(*csg_obj);
696  const auto & lowest_axial_surf = surfaces_by_axial_region.front().get();
697  const auto & highest_axial_surf = surfaces_by_axial_region.back().get();
698  axial_extent = +lowest_axial_surf & -highest_axial_surf;
699  }
700 
701  // Define all duct boundaries and create the appropriate cell to fill each duct region.
702  // Add these cells to a separate universe
703  std::vector<Real> duct_boundaries = _duct_sizes;
704  duct_boundaries.push_back(getReactorParam<Real>(RGMB::assembly_pitch) / 2.);
705  CSG::CSGRegion inner_region;
706  const auto & assembly_univ = csg_obj->createUniverse(name() + "_univ");
707  for (const auto i : index_range(duct_boundaries))
708  {
709  bool is_last_radial_region = i == duct_boundaries.size() - 1;
710  if (i == 0)
711  {
712  // For innermost duct region, we create a lattice cell as the fill
713  const auto pin_pitch = getMeshProperty<Real>(RGMB::pitch, _inputs[0]);
714  auto & assembly_lattice = createRGMBLattice(pin_pitch, universe_pattern, *csg_obj);
715  setAssemblyLatticeOuter(assembly_lattice, surfaces_by_axial_region, *csg_obj);
716 
717  // Define lattice cell
718  std::string lat_cell_name = name() + "_lattice_cell";
719  if (!is_last_radial_region)
720  {
721  const auto & duct_surfaces =
722  getOuterRadialSurfacesForUnitCell(i, duct_boundaries[i], *csg_obj);
723  inner_region = CSGUtils::getInnerRegion(duct_surfaces, Point(0, 0, 0));
724  }
725  if (_geom_type == "Hex")
726  {
727  // For hex lattices, apply a 90 degree rotation to the lattice to match orientation
728  // of FEM mesh
729  csg_obj->applyAxisRotation(assembly_lattice, CSG::RotationAxisType::Z, 90.);
730  }
731  csg_obj->createCell(lat_cell_name, assembly_lattice, inner_region, &assembly_univ);
732  }
733  else
734  {
735  // Update ducted region
736  CSG::CSGRegion radial_region = ~inner_region;
737  if (!is_last_radial_region)
738  {
739  const auto & duct_surfaces =
740  getOuterRadialSurfacesForUnitCell(i, duct_boundaries[i], *csg_obj);
741  inner_region = CSGUtils::getInnerRegion(duct_surfaces, Point(0, 0, 0));
742  radial_region &= inner_region;
743  }
744 
745  // Define cell fill of lattice
746  std::string duct_cell_name = name() + "_duct_cell_radial_" + std::to_string(i - 1);
747  if (!extruded_assembly)
748  {
749  // In 2D, the cell fill will be a material
750  std::string region_name = "rgmb_region_" + std::to_string(_duct_region_ids[0][i - 1]);
751  csg_obj->createCell(duct_cell_name, region_name, radial_region, &assembly_univ);
752  }
753  else
754  {
755  // In 3D, the cell fill will be a universe
756  const auto & name_prefix = name() + "_duct_radial_" + std::to_string(i - 1);
757  std::vector<subdomain_id_type> duct_region_ids;
758  for (const auto j : make_range(_duct_region_ids.size()))
759  duct_region_ids.push_back(_duct_region_ids[j][i - 1]);
760  auto & fill_univ = createDuctFillUniverse(
761  name_prefix, surfaces_by_axial_region, duct_region_ids, *csg_obj);
762  csg_obj->createCell(duct_cell_name, fill_univ, radial_region, &assembly_univ);
763  }
764  }
765  }
766 
767  // Create new cell to bound universe based on assembly outer boundaries, and add this cell
768  // to the root universe
769  const auto & duct_surfaces = getOuterRadialSurfacesForUnitCell(
770  duct_boundaries.size() - 1, duct_boundaries.back(), *csg_obj);
771  auto assembly_region = CSGUtils::getInnerRegion(duct_surfaces, Point(0, 0, 0));
772  if (extruded_assembly)
773  assembly_region &= axial_extent;
774  csg_obj->createCell(name() + "_root_cell", assembly_univ, assembly_region);
775 
776  return csg_obj;
777 }
778 
779 void
782  const std::vector<std::reference_wrapper<const CSG::CSGSurface>> & surfaces_by_axial_region,
783  CSG::CSGBase & csg_obj)
784 {
785  if (_background_region_id.size() == 0)
786  // Cartesian lattices may not have a background region. In this case, the outer is left as void
787  return;
788 
789  // Define outer fill of lattice
790  if (_mesh_dimensions == 2)
791  {
792  // In 2D, the outer fill will be a material fill
793  std::string region_name = "rgmb_region_" + std::to_string(_background_region_id[0]);
794  csg_obj.setLatticeOuter(assembly_lattice, region_name);
795  }
796  else // _mesh_dimension == 3
797  {
798  // In 3D, we define the outer fill as a universe
799  auto & outer_univ = createDuctFillUniverse(
800  name() + "_lattice_outer", surfaces_by_axial_region, _background_region_id, csg_obj);
801  csg_obj.setLatticeOuter(assembly_lattice, outer_univ);
802  }
803 }
804 
805 const CSG::CSGUniverse &
807  const std::string & name_prefix,
808  const std::vector<std::reference_wrapper<const CSG::CSGSurface>> & surfaces_by_axial_region,
809  const std::vector<subdomain_id_type> & region_ids,
810  CSG::CSGBase & csg_obj)
811 {
812  mooseAssert(surfaces_by_axial_region.size() - region_ids.size() == 1,
813  "Incorrect length of axial data vectors");
814  auto & fill_univ = csg_obj.createUniverse(name_prefix + "_univ");
815  for (const auto i : make_range(surfaces_by_axial_region.size() - 1))
816  {
817  CSG::CSGRegion axial_region;
818  const auto & lower_surf = surfaces_by_axial_region[i].get();
819  if (lower_surf != surfaces_by_axial_region.front())
820  axial_region = +lower_surf;
821  const auto & upper_surf = surfaces_by_axial_region[i + 1].get();
822  if (upper_surf != surfaces_by_axial_region.back())
823  {
824  if (axial_region.getRegionType() == CSG::CSGRegion::RegionType::EMPTY)
825  axial_region = -upper_surf;
826  else
827  axial_region &= -upper_surf;
828  }
829  auto cell_name = name_prefix + "_axial_" + std::to_string(i);
830  const auto mat_name = "rgmb_region_" + std::to_string(region_ids[i]);
831  csg_obj.createCell(cell_name, mat_name, axial_region, &fill_univ);
832  }
833 
834  return fill_univ;
835 }
registerMooseObject("ReactorApp", AssemblyMeshGenerator)
std::unique_ptr< MeshBase > & getMeshByName(const MeshGeneratorName &mesh_generator_name)
static void addDepletionIDParams(InputParameters &parameters)
const SubdomainName ASSEMBLY_BLOCK_NAME_PREFIX
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...
bool _has_duct_block_names
Whether block names for assembly duct elements have been provided by user.
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
const subdomain_id_type ASSEMBLY_BLOCK_ID_START
void paramError(const std::string &param, Args... args) const
std::vector< std::unique_ptr< CSG::CSGBase > *> getCSGBases(const std::string &param_name)
static const std::string region_id_as_block_name
const unsigned int _background_intervals
The number of divisions in the mesh outside of the pins and inside of the ducts.
static const std::string assembly_lattice
static const std::string is_single_pin
const subdomain_id_type _assembly_type
The id number for the type of the assembly.
std::unique_ptr< MeshBase > generate() override
const CSGCell & createCell(const std::string &name, const std::string &mat_name, const CSGRegion &region, const CSGUniverse *add_to_univ=nullptr)
const std::vector< Real > _duct_sizes
The inner apothem of any ducts around the assembly.
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...
void setLatticeOuter(const CSGLattice &lattice, const std::string &outer_name)
bool _has_background_block_name
Whether block names for assembly background elements have been provided by user.
T & set(const std::string &name, bool quiet_mode=false)
static const std::string reactor_params_name
static constexpr boundary_id_type ASSEMBLY_BOUNDARY_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.
std::vector< std::vector< subdomain_id_type > > _duct_region_ids
2-D vector (axial outer indexing, radial inner indexing) used to set the "region_id" extra-element in...
static const std::string background_block_name
std::unique_ptr< MeshBase > * _build_mesh
The final mesh that is generated by the subgenerators; This mesh is generated by the subgenerators wi...
void addDepletionId(MeshBase &input_mesh, const MooseEnum &option, const DepletionIDGenerationLevel generation_level, const bool extrude)
add depletion IDs
static const std::string assembly_pitch
static const std::string pin_type
std::map< subdomain_id_type, std::vector< std::vector< subdomain_id_type > > > _pin_region_id_map
A mapping from pin-type IDs to region IDs used when assigning region IDs during the pin stitching sta...
const SubdomainName TRI_BLOCK_NAME_SUFFIX
Factory & getFactory()
TRI3
static InputParameters validParams()
static const std::string pin_region_id_map
const BoundaryName PIN_BOUNDARY_NAME_PREFIX
std::vector< subdomain_id_type > _background_region_id
Vector used to set the "region_id" extra-element integer of the assembly background elements...
static const std::string extruded
const T & getReactorParam(const std::string &param_name)
Returns reference of parameter in ReactorMeshParams object.
std::vector< std::unique_ptr< CSG::CSGBase > * > _input_csg_bases
List of pointers to all CSG bases created by input mesh generators.
const std::string & name() const
static const std::string duct_halfpitches
std::unique_ptr< CSG::CSGBase > generateCSG() override
int8_t boundary_id_type
static const std::string mesh_dimensions
const subdomain_id_type ASSEMBLY_BLOCK_ID_TRI_FLEXIBLE
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.
RegionType getRegionType() const
static const std::string pitch
void freeReactorParamsMesh()
Releases the mesh obtained in _reactor_params_mesh.
const std::vector< unsigned int > _duct_intervals
The number of divisions in the meshes of the ducts.
bool getCSGOnly() const
static const std::string top_boundary_id
std::vector< std::unique_ptr< MeshBase > *> getMeshes(const std::string &param_name)
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)
std::vector< std::vector< std::string > > _duct_block_names
Optional 2-D vector (axial outer indexing, radial inner indexing) used to set the block names of the ...
static void setHasGenerateCSG(InputParameters &params)
static const std::string is_homogenized
void freeReactorParamsCSG()
Releases the CSG base object obtained in _reactor_params_csg.
const CSG::CSGUniverse & createDuctFillUniverse(const std::string &name_prefix, const std::vector< std::reference_wrapper< const CSG::CSGSurface >> &surfaces_by_axial_region, const std::vector< subdomain_id_type > &region_ids, CSG::CSGBase &csg_obj)
Create fill universe for ducted regions.
static const std::string is_control_drum
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 pin_region_ids
static const std::string flexible_assembly_stitching
static const std::string pin_lattice
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
MooseApp & _app
const std::vector< std::vector< unsigned int > > _pattern
The 2D pin-by-pin layout of the assembly mapping indices into _inputs.
std::string _geom_type
The type of geometry that is being described (Square or Hex, declared in the ReactorMeshParams object...
static const std::string pin_block_names
AssemblyMeshGenerator(const InputParameters &parameters)
const CSG::CSGLattice & createRGMBLattice(const Real pitch, const std::vector< std::vector< std::reference_wrapper< const CSG::CSGUniverse >>> pattern, CSG::CSGBase &csg_obj)
Create CSG lattice for assembly and core lattices.
boundary_id_type _assembly_boundary_id
The ID of the assembly outer boundary, equal to the assembly type ID + 2000.
static const std::string axial_mesh_intervals
IntRange< T > make_range(T beg, T end)
const std::vector< MeshGeneratorName > _inputs
The names of the pins that compose the Assembly.
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
void setAssemblyLatticeOuter(const CSG::CSGLattice &assembly_lattice, const std::vector< std::reference_wrapper< const CSG::CSGSurface >> &surfaces_by_axial_region, CSG::CSGBase &csg_obj)
Create CSG cell with lattice fill for region within first duct boundary.
T & declareMeshProperty(const std::string &data_name, Args &&... args)
std::vector< std::string > _background_block_name
Optional vector used to set the block names of the assembly background elements.
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
static const std::string pin_names
const CSGUniverse & createUniverse(const std::string &name)
MeshGeneratorSystem & getMeshGeneratorSystem()
PRISM6
std::map< subdomain_id_type, std::vector< std::vector< std::string > > > _pin_block_name_map
A mapping from pin-type IDs to block names used when assigning block names during the pin stitching s...
void declareMeshesForSub(const std::string &param_name)
SubdomainID getNextFreeSubdomainID(MeshBase &input_mesh)
const bool _extrude
Whether this mesh should be extruded to 3-D, making it the final structure in the reactor mesh...
static const std::string num_sectors_flexible_stitching
static const std::string duct_region_ids
BoundaryName _assembly_boundary_name
The name of the assembly outer boundary, equal to the concatenation of "outer_assembly_" and the asse...
void ErrorVector unsigned int
const subdomain_id_type MAX_PIN_TYPE_ID
auto index_range(const T &sizable)
static const std::string bottom_boundary_id
static const std::string pin_block_name_map
uint8_t dof_id_type
Mesh generator for defining a reactor assembly using a Cartesian or hexagonal lattice with the option...