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