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 "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
88
89 // Declare that this generator has a generateCSG method
91
92 return params;
93}
94
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)))
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))
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)
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");
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");
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
413void
415{
416 // Declare metadata for use in downstream mesh generators
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
459void
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
513std::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
610 subdomain_id_type pin_type = RGMB::MAX_PIN_TYPE_ID - peripheral_idx;
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
644std::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
779void
781 const CSG::CSGLattice & assembly_lattice,
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
805const 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)
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)
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.
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 ...
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...
CSG::CSGRegion getInnerRegion(const std::vector< std::reference_wrapper< const CSG::CSGSurface > > &surfaces, const libMesh::Point &origin)
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 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
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