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 auto smallest_duct = _duct_sizes.size() ? _duct_sizes.back() * 2. : assembly_pitch;
204 if (smallest_duct / std::sin(M_PI / 3.0) < _pattern.size() * base_pitch)
205 mooseError("Hexagonal diameter of assembly ducts and outer boundary must be larger than the "
206 "number of assembly rows "
207 "times the pin pitch");
208 // Check size of background region id matches number of axial levels
209 if (_background_region_id.size() != n_axial_levels)
210 mooseError("The size of background_region_id must be equal to the number of axial levels as "
211 "defined in the ReactorMeshParams object");
212 }
213
214 if (_duct_sizes.size() != _duct_intervals.size())
215 mooseError("If ducts are defined then \"duct_intervals\" and \"duct_region_ids\" must also be "
216 "defined and of equal size.");
217
218 if (_duct_sizes.size() != 0)
219 {
220 // Check size of duct region id matches number of axial levels
221 if (_duct_region_ids.size() != n_axial_levels)
222 mooseError("The size of duct_region_id must be equal to the number of axial levels as "
223 "defined in the ReactorMeshParams object");
224 if (_duct_region_ids[0].size() != _duct_sizes.size())
225 paramError("duct_halfpitch",
226 "If ducts are defined, then \"duct_intervals\" and \"duct_region_ids\" "
227 "must also be defined and of equal size.");
228 }
229
230 // Check whether block names are defined properly
231 if (isParamValid("background_block_name"))
232 {
233 if (getReactorParam<bool>(RGMB::region_id_as_block_name))
234 paramError("background_block_name",
235 "If ReactorMeshParams/region_id_as_block_name is set, background_block_name "
236 "should not be specified in AssemblyMeshGenerator");
238 _background_block_name = getParam<std::vector<std::string>>("background_block_name");
240 mooseError("The size of background_block_name must match the size of background_region_id");
241 }
242 else
244
245 if (isParamValid("duct_block_names"))
246 {
247 if (getReactorParam<bool>(RGMB::region_id_as_block_name))
248 paramError("duct_block_names",
249 "If ReactorMeshParams/region_id_as_block_name is set, duct_block_names should not "
250 "be specified in AssemblyMeshGenerator");
252 _duct_block_names = getParam<std::vector<std::vector<std::string>>>("duct_block_names");
253 if (_duct_region_ids.size() != _duct_block_names.size())
254 mooseError("The size of duct_block_names must match the size of duct_region_ids");
255 for (const auto i : index_range(_duct_region_ids))
256 if (_duct_region_ids[i].size() != _duct_block_names[i].size())
257 mooseError("The size of duct_block_names must match the size of duct_region_ids");
258 }
259 else
260 _has_duct_block_names = false;
261
262 // No subgenerators will be called if option to bypass mesh generators is enabled
263 if (!getReactorParam<bool>(RGMB::bypass_meshgen))
264 {
265 // Declare dependency of inputs to sub generator calls. If mesh generation
266 declareMeshesForSub("inputs");
267
270
271 // Call PatternedHexMeshGenerator or PatternedCartesianMeshGenerator to stitch assembly
272 {
273 const auto patterned_mg_name =
274 _geom_type == "Hex" ? "PatternedHexMeshGenerator" : "PatternedCartesianMeshGenerator";
275 auto params = _app.getFactory().getValidParams(patterned_mg_name);
276
277 if (_geom_type == "Hex")
278 {
279 params.set<Real>("hexagon_size") = getReactorParam<Real>(RGMB::assembly_pitch) / 2.0;
280 params.set<MooseEnum>("hexagon_size_style") = "apothem";
281 }
282 else
283 {
284 if (_background_region_id.size() == 0)
285 params.set<MooseEnum>("pattern_boundary") = "none";
286 else
287 {
288 params.set<MooseEnum>("pattern_boundary") = "expanded";
289 params.set<Real>("square_size") = getReactorParam<Real>(RGMB::assembly_pitch);
290 params.set<bool>("uniform_mesh_on_sides") = true;
291 }
292 }
293
294 params.set<std::vector<std::string>>("id_name") = {"pin_id"};
295 params.set<std::vector<MooseEnum>>("assign_type") = {
296 MooseEnum("cell", "cell")}; // give elems IDs relative to position in assembly
297 params.set<std::vector<MeshGeneratorName>>("inputs") = _inputs;
298 params.set<std::vector<std::vector<unsigned int>>>("pattern") = _pattern;
299 params.set<bool>("create_outward_interface_boundaries") = false;
300
301 if (_background_intervals > 0)
302 {
303 params.set<unsigned int>("background_intervals") = _background_intervals;
304 // Initial block id used to define peripheral regions of assembly
305
306 const auto background_block_name =
307 RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R0";
308 const auto background_block_id = RGMB::ASSEMBLY_BLOCK_ID_START;
309 params.set<subdomain_id_type>("background_block_id") = background_block_id;
310 params.set<SubdomainName>("background_block_name") = background_block_name;
311 }
312
313 if (_duct_sizes.size() > 0)
314 {
315 std::vector<subdomain_id_type> duct_block_ids;
316 std::vector<SubdomainName> duct_block_names;
317 for (const auto duct_it : index_range(_duct_region_ids[0]))
318 {
319 const auto duct_block_name = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX +
320 std::to_string(_assembly_type) + "_R" +
321 std::to_string(duct_it + 1);
322 const auto duct_block_id = RGMB::ASSEMBLY_BLOCK_ID_START + duct_it + 1;
323 duct_block_ids.push_back(duct_block_id);
324 duct_block_names.push_back(duct_block_name);
325 }
326
327 params.set<std::vector<Real>>("duct_sizes") = _duct_sizes;
328 params.set<std::vector<subdomain_id_type>>("duct_block_ids") = duct_block_ids;
329 params.set<std::vector<SubdomainName>>("duct_block_names") = duct_block_names;
330 params.set<std::vector<unsigned int>>("duct_intervals") = _duct_intervals;
331 }
332
333 params.set<boundary_id_type>("external_boundary_id") = _assembly_boundary_id;
334 params.set<BoundaryName>("external_boundary_name") = _assembly_boundary_name;
335
336 addMeshSubgenerator(patterned_mg_name, name() + "_pattern", params);
337
338 // Pass mesh meta-data defined in subgenerator constructor to this MeshGenerator
339 copyMeshProperty<bool>("is_control_drum_meta", name() + "_pattern");
340 copyMeshProperty<std::vector<Point>>("control_drum_positions", name() + "_pattern");
341 copyMeshProperty<std::vector<Real>>("control_drum_angles", name() + "_pattern");
342 copyMeshProperty<std::vector<std::vector<Real>>>("control_drums_azimuthal_meta",
343 name() + "_pattern");
344 copyMeshProperty<std::string>("position_file_name", name() + "_pattern");
345 copyMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern");
346 }
347
348 std::string build_mesh_name = name() + "_delbds";
349
350 // Remove outer pin sidesets created by PolygonConcentricCircleMeshGenerator
351 {
352 // Get outer boundaries of all constituent pins based on pin_type
353 std::vector<BoundaryName> boundaries_to_delete = {};
354 for (const auto & pattern_x : _pattern)
355 {
356 for (const auto & pattern_idx : pattern_x)
357 {
358 const auto pin_name = _inputs[pattern_idx];
359 const auto pin_id = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pin_name);
360 const BoundaryName boundary_name =
361 RGMB::PIN_BOUNDARY_NAME_PREFIX + std::to_string(pin_id);
362 if (!std::count(boundaries_to_delete.begin(), boundaries_to_delete.end(), boundary_name))
363 boundaries_to_delete.push_back(boundary_name);
364 }
365 }
366 auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
367
368 params.set<MeshGeneratorName>("input") = name() + "_pattern";
369 params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
370
371 addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
372 }
373
374 // Modify outermost mesh interval to enable flexible assembly stitching
375 const auto use_flexible_stitching = getReactorParam<bool>(RGMB::flexible_assembly_stitching);
376 if (use_flexible_stitching)
377 {
379 build_mesh_name = name() + "_fpg_delbds";
380 }
381
382 for (auto pinMG : _inputs)
383 {
384 std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>> region_id_map =
385 getMeshProperty<std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>>(
386 RGMB::pin_region_ids, pinMG);
387 _pin_region_id_map.insert(
388 std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
389 region_id_map.begin()->first, region_id_map.begin()->second));
390 subdomain_id_type pin_type_id = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pinMG);
391 std::vector<std::vector<std::string>> pin_block_names =
392 getMeshProperty<std::vector<std::vector<std::string>>>(RGMB::pin_block_names, pinMG);
393 _pin_block_name_map.insert(
394 std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(pin_type_id,
395 pin_block_names));
396 }
397
398 if (_extrude && _mesh_dimensions == 3)
399 build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
400
401 // Store final mesh subgenerator
402 _build_mesh = &getMeshByName(build_mesh_name);
403 }
404 else
405 {
406 // If mesh generation should be bypassed, call getMeshes to resolve MeshGeneratorSystem
407 // dependencies
408 auto input_meshes = getMeshes("inputs");
409
410 // Since subgenerators are not called, error checking on input parameters needs to happen here
412 paramError("pattern",
413 "Assembly lattice contains an index that exceeds the number of input pins");
414
415 if (_duct_sizes.size())
416 {
418 paramError("duct_halfpitch", "This parameter must be strictly ascending.");
419 if (_duct_sizes.back() > assembly_pitch / 2.)
420 paramError("duct_halfpitch",
421 "Duct halfpitches of assembly must not exceed half the assembly pitch");
422 }
423 }
424
425 // If we are in CSG only mode, store the CSGBase objects associated with input MG's
427 _input_csg_bases = getCSGBases("inputs");
428
430}
431
432void
434{
435 // Declare metadata for use in downstream mesh generators
447 // Following metadata is only relevant if an output mesh is generated by RGMB
448 if (!getReactorParam<bool>(RGMB::bypass_meshgen))
449 {
452 }
453
454 // Determine constituent pin names and define lattice as metadata
455 std::vector<std::vector<int>> pin_name_lattice;
456 std::vector<std::string> input_pin_names;
457 for (const auto i : index_range(_pattern))
458 {
459 std::vector<int> pin_name_idx(_pattern[i].size());
460 for (const auto j : index_range(_pattern[i]))
461 {
462 const auto input_pin_name = _inputs[_pattern[i][j]];
463 const auto it = std::find(input_pin_names.begin(), input_pin_names.end(), input_pin_name);
464 if (it == input_pin_names.end())
465 {
466 pin_name_idx[j] = input_pin_names.size();
467 input_pin_names.push_back(input_pin_name);
468 }
469 else
470 pin_name_idx[j] = it - input_pin_names.begin();
471 }
472 pin_name_lattice.push_back(pin_name_idx);
473 }
474 declareMeshProperty(RGMB::pin_names, input_pin_names);
475 declareMeshProperty(RGMB::pin_lattice, pin_name_lattice);
476}
477
478void
480{
481 // Assemblies that invoke this method have constituent pin lattice, delete outermost background or
482 // duct region (if present)
483 SubdomainName block_to_delete = "";
484 if (_background_region_id.size() == 0)
485 mooseError("Attempting to use flexible stitching on assembly " + name() +
486 " that does not have a background region. This is not yet supported.");
487 const auto radial_index = _duct_region_ids.size() == 0 ? 0 : _duct_region_ids[0].size();
488 block_to_delete = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R" +
489 std::to_string(radial_index);
490
491 {
492 // Invoke BlockDeletionGenerator to delete outermost mesh interval of assembly
493 auto params = _app.getFactory().getValidParams("BlockDeletionGenerator");
494
495 params.set<std::vector<SubdomainName>>("block") = {block_to_delete};
496 params.set<MeshGeneratorName>("input") = name() + "_delbds";
497
498 addMeshSubgenerator("BlockDeletionGenerator", name() + "_del_outer", params);
499 }
500 {
501 // Invoke FlexiblePatternGenerator to triangulate deleted mesh region
502 auto params = _app.getFactory().getValidParams("FlexiblePatternGenerator");
503
504 params.set<std::vector<MeshGeneratorName>>("inputs") = {name() + "_del_outer"};
505 params.set<std::vector<libMesh::Point>>("extra_positions") = {libMesh::Point(0, 0, 0)};
506 params.set<std::vector<unsigned int>>("extra_positions_mg_indices") = {0};
507 params.set<bool>("use_auto_area_func") = true;
508 params.set<MooseEnum>("boundary_type") = (_geom_type == "Hex") ? "HEXAGON" : "CARTESIAN";
509 params.set<unsigned int>("boundary_sectors") =
510 getReactorParam<unsigned int>(RGMB::num_sectors_flexible_stitching);
511 params.set<Real>("boundary_size") = getReactorParam<Real>(RGMB::assembly_pitch);
512 params.set<boundary_id_type>("external_boundary_id") = _assembly_boundary_id;
513 params.set<BoundaryName>("external_boundary_name") = _assembly_boundary_name;
514 params.set<SubdomainName>("background_subdomain_name") =
515 block_to_delete + RGMB::TRI_BLOCK_NAME_SUFFIX;
516 params.set<bool>("verify_holes") = false;
517 params.set<unsigned short>("background_subdomain_id") = RGMB::ASSEMBLY_BLOCK_ID_TRI_FLEXIBLE;
518
519 addMeshSubgenerator("FlexiblePatternGenerator", name() + "_fpg", params);
520 }
521 {
522 // Delete extra boundary created by FlexiblePatternGenerator
523 auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
524
525 params.set<MeshGeneratorName>("input") = name() + "_fpg";
526 params.set<std::vector<BoundaryName>>("boundary_names") = {std::to_string(1)};
527
528 addMeshSubgenerator("BoundaryDeletionGenerator", name() + "_fpg_delbds", params);
529 }
530}
531
532std::unique_ptr<MeshBase>
534{
535 // Must be called to free the ReactorMeshParams mesh
537
538 // If bypass_mesh is true, return a null mesh. In this mode, an output mesh is not
539 // generated and only metadata is defined on the generator, so logic related to
540 // generation of output mesh will not be called
541 if (getReactorParam<bool>(RGMB::bypass_meshgen))
542 {
543 auto null_mesh = nullptr;
544 return null_mesh;
545 }
546
547 // Update metadata at this point since values for these metadata only get set by PCCMG
548 // at generate() stage
549 if (hasMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern"))
550 {
551 const auto pattern_pitch_meta =
552 getMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern");
553 setMeshProperty("pattern_pitch_meta", pattern_pitch_meta);
554 }
555
556 // This generate() method will be called once the subgenerators that we depend on are
557 // called. This is where we reassign subdomain ids/name in case they were merged when
558 // stitching pins into an assembly. This is also where we set region_id and
559 // assembly_type_id element integers.
560
561 // Define all extra element names and integers
562 std::string plane_id_name = "plane_id";
563 std::string region_id_name = "region_id";
564 std::string pin_type_id_name = "pin_type_id";
565 std::string assembly_type_id_name = "assembly_type_id";
566 std::string radial_id_name = "radial_id";
567 const std::string default_block_name =
569
570 auto pin_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), pin_type_id_name, true);
571 auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name, true);
572 auto radial_id_int = getElemIntegerFromMesh(*(*_build_mesh), radial_id_name, true);
573
574 auto assembly_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), assembly_type_id_name);
575
576 unsigned int plane_id_int = 0;
577 if (_extrude)
578 plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
579
580 // Get next free block ID in mesh in case subdomain ids need to be remapped
581 auto next_block_id = MooseMeshUtils::getNextFreeSubdomainID(*(*(_build_mesh)));
582 std::map<std::string, SubdomainID> rgmb_name_id_map;
583
584 // Loop through all mesh elements and set region ids and reassign block IDs/names
585 // if they were merged during pin stitching
586 for (auto & elem : (*_build_mesh)->active_element_ptr_range())
587 {
588 elem->set_extra_integer(assembly_type_id_int, _assembly_type);
589 const dof_id_type pin_type_id = elem->get_extra_integer(pin_type_id_int);
590 const dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
591
592 // Element is part of a pin mesh
593 if (_pin_region_id_map.find(pin_type_id) != _pin_region_id_map.end())
594 {
595 // Get region ID from pin_type, z_id, and radial_idx
596 const dof_id_type radial_idx = elem->get_extra_integer(radial_id_int);
597 const auto elem_rid = _pin_region_id_map[pin_type_id][z_id][radial_idx];
598 elem->set_extra_integer(region_id_int, elem_rid);
599
600 // Set element block name and block id
601 bool has_block_names = !_pin_block_name_map[pin_type_id].empty();
602 auto elem_block_name = default_block_name;
603 if (has_block_names)
604 elem_block_name += "_" + _pin_block_name_map[pin_type_id][z_id][radial_idx];
605 else if (getReactorParam<bool>(RGMB::region_id_as_block_name))
606 elem_block_name += "_REG" + std::to_string(elem_rid);
607 if (elem->type() == TRI3 || elem->type() == PRISM6)
608 elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
610 *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
611 }
612 else
613 {
614 // Assembly peripheral element (background / duct), set subdomains according
615 // to user preferences and set pin type id to RGMB::MAX_PIN_TYPE_ID - peripheral index
616 // Region id is inferred from z_id and peripheral_idx
617 const auto base_block_id = elem->subdomain_id();
618 const auto base_block_name = (*_build_mesh)->subdomain_name(base_block_id);
619
620 // Check if block name has correct prefix
621 std::string prefix = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R";
622 if (!(base_block_name.find(prefix, 0) == 0))
623 continue;
624 // Peripheral index is integer value of substring after prefix
625 const unsigned int peripheral_idx = std::stoi(base_block_name.substr(prefix.length()));
626
627 bool is_background_region = peripheral_idx == 0;
628
629 subdomain_id_type pin_type = RGMB::MAX_PIN_TYPE_ID - peripheral_idx;
630 elem->set_extra_integer(pin_type_id_int, pin_type);
631
632 const auto elem_rid = (is_background_region ? _background_region_id[z_id]
633 : _duct_region_ids[z_id][peripheral_idx - 1]);
634 elem->set_extra_integer(region_id_int, elem_rid);
635
636 // Set element block name and block id
637 auto elem_block_name = default_block_name;
638 if (getReactorParam<bool>(RGMB::region_id_as_block_name))
639 elem_block_name += "_REG" + std::to_string(elem_rid);
640 else if (is_background_region && _has_background_block_name)
641 elem_block_name += "_" + _background_block_name[z_id];
642 else if (!is_background_region && _has_duct_block_names)
643 elem_block_name += "_" + _duct_block_names[z_id][peripheral_idx - 1];
644 if (elem->type() == TRI3 || elem->type() == PRISM6)
645 elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
647 *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
648 }
649 }
650
651 if (getParam<bool>("generate_depletion_id"))
652 {
653 const MooseEnum option = getParam<MooseEnum>("depletion_id_type");
655 }
656
657 // Mark mesh as not prepared, as block IDs were re-assigned in this method
658 (*_build_mesh)->unset_is_prepared();
659
660 return std::move(*_build_mesh);
661}
662
663std::unique_ptr<CSG::CSGBase>
665{
666 // Must be called to free the ReactorMeshParams CSGBase object
668
669 auto csg_obj = std::make_unique<CSG::CSGBase>();
670
671 // Combine all bases from PinMG inputs into this base. Root universes from
672 // inputs are renamed to a new universe name. These universes and their
673 // cells will be discarded, so that only the infinite pin universe is retained
674 std::unordered_map<unsigned int, std::string> univ_id_names;
675 for (const auto i : index_range(_inputs))
676 {
677 const auto input_univ_name_discard = _inputs[i] + "_root_univ";
678 csg_obj->joinOtherBase(std::move(*_input_csg_bases[i]), true, input_univ_name_discard);
679
680 const auto & universe_to_delete = csg_obj->getUniverseByName(input_univ_name_discard);
681 const auto cells_to_delete = universe_to_delete.getAllCells();
682 csg_obj->deleteUniverse(universe_to_delete);
683
684 // Store universe fill of cell that will be deleted. This will be the universe
685 // that gets populated into the assembly lattice
686 for (const auto & cell : cells_to_delete)
687 {
688 const auto & cell_to_delete = cell.get();
689 const auto & fill_univ_name = cell_to_delete.getFillUniverse().getName();
690 // Store surfaces use to define cell region. The radial surfaces will be deleted
691 // as they are no longer used in CSGBase instance after cell deletion.
692 const auto & surfs_to_delete = cell_to_delete.getRegion().getSurfaces();
693 csg_obj->deleteCell(cell_to_delete);
694 univ_id_names[i] = fill_univ_name;
695 for (const auto & surf : surfs_to_delete)
696 {
697 const auto & surf_name = surf.get().getName();
698 // Only delete those surfaces that aren't used as axial planes
699 if (surf_name.find(RGMB::CSG_AXIAL_PLANE_PREFIX) == std::string::npos)
700 csg_obj->deleteSurface(csg_obj->getSurfaceByName(surf_name));
701 }
702 }
703 }
704
705 // Build the universe pattern for the assembly lattice from the input pattern
706 std::vector<std::vector<std::reference_wrapper<const CSG::CSGUniverse>>> universe_pattern;
707 for (const auto & row : _pattern)
708 {
709 std::vector<std::reference_wrapper<const CSG::CSGUniverse>> universe_row;
710 for (const auto & univ_id : row)
711 {
712 const auto & lattice_univ = csg_obj->getUniverseByName(univ_id_names[univ_id]);
713 universe_row.push_back(lattice_univ);
714 }
715 universe_pattern.push_back(universe_row);
716 }
717
718 // Get axial boundaries for problem
719 std::vector<std::reference_wrapper<const CSG::CSGSurface>> surfaces_by_axial_region;
720 CSG::CSGRegion axial_extent;
721 const auto extruded_assembly = _mesh_dimensions == 3;
722 if (extruded_assembly)
723 {
724 // Skip intermediate plane generation if background regions do not exist in assembly
725 const bool skip_intermediate_plane_generation = _background_region_id.size() == 0;
726 surfaces_by_axial_region = getAxialPlaneSurfaces(*csg_obj, skip_intermediate_plane_generation);
727 const auto & lowest_axial_surf = surfaces_by_axial_region.front().get();
728 const auto & highest_axial_surf = surfaces_by_axial_region.back().get();
729 axial_extent = +lowest_axial_surf & -highest_axial_surf;
730 }
731
732 // Define all duct boundaries and create the appropriate cell to fill each duct region.
733 // Add these cells to a separate universe
734 std::vector<Real> duct_boundaries = _duct_sizes;
735 duct_boundaries.push_back(getReactorParam<Real>(RGMB::assembly_pitch) / 2.);
736 CSG::CSGRegion inner_region;
737 const auto & assembly_univ = csg_obj->createUniverse(name() + "_univ");
738 const auto n_sides = (_geom_type == "Hex") ? 6 : 4;
739 for (const auto i : index_range(duct_boundaries))
740 {
741 bool is_last_radial_region = i == duct_boundaries.size() - 1;
742 if (i == 0)
743 {
744 // For innermost duct region, we create a lattice cell as the fill
745 const auto pin_pitch = getMeshProperty<Real>(RGMB::pitch, _inputs[0]);
746 auto & assembly_lattice = createRGMBLattice(pin_pitch, universe_pattern, *csg_obj);
747 setAssemblyLatticeOuter(assembly_lattice, surfaces_by_axial_region, *csg_obj);
748
749 // Define lattice cell
750 std::string lat_cell_name = name() + "_lattice_cell";
751 if (!is_last_radial_region)
752 {
753 const auto unit_name = name() + "_radial_duct_" + std::to_string(i);
754 std::unique_ptr<CSG::CSGNPolygonUnit> duct_ptr =
755 std::make_unique<CSG::CSGNPolygonUnit>(unit_name, n_sides, duct_boundaries[i]);
756 auto & duct_unit = csg_obj->addEngUnit(std::move(duct_ptr));
757 inner_region = -duct_unit;
758 }
759 if (_geom_type == "Hex")
760 {
761 // For hex lattices, apply a 90 degree rotation to the lattice to match orientation
762 // of FEM mesh
763 csg_obj->applyAxisRotation(assembly_lattice, CSG::RotationAxisType::Z, 90.);
764 }
765 csg_obj->createCell(lat_cell_name, assembly_lattice, inner_region, &assembly_univ);
766 }
767 else
768 {
769 // Update ducted region
770 CSG::CSGRegion radial_region = ~inner_region;
771 if (!is_last_radial_region)
772 {
773 const auto unit_name = name() + "_radial_duct_" + std::to_string(i);
774 std::unique_ptr<CSG::CSGNPolygonUnit> duct_ptr =
775 std::make_unique<CSG::CSGNPolygonUnit>(unit_name, n_sides, duct_boundaries[i]);
776 auto & duct_unit = csg_obj->addEngUnit(std::move(duct_ptr));
777 inner_region = -duct_unit;
778 radial_region &= inner_region;
779 }
780
781 // Define cell fill of lattice
782 std::string duct_cell_name = name() + "_duct_cell_radial_" + std::to_string(i - 1);
783 if (!extruded_assembly)
784 {
785 // In 2D, the cell fill will be a material
786 std::string region_name = "rgmb_region_" + std::to_string(_duct_region_ids[0][i - 1]);
787 csg_obj->createCell(duct_cell_name, region_name, radial_region, &assembly_univ);
788 }
789 else
790 {
791 // In 3D, the cell fill will be a universe
792 const auto & name_prefix = name() + "_duct_radial_" + std::to_string(i - 1);
793 std::vector<subdomain_id_type> duct_region_ids;
794 for (const auto j : make_range(_duct_region_ids.size()))
795 duct_region_ids.push_back(_duct_region_ids[j][i - 1]);
796 auto & fill_univ = createDuctFillUniverse(
797 name_prefix, surfaces_by_axial_region, duct_region_ids, *csg_obj);
798 csg_obj->createCell(duct_cell_name, fill_univ, radial_region, &assembly_univ);
799 }
800 }
801 }
802
803 // Create new cell to bound universe based on assembly outer boundaries, and add this cell
804 // to the root universe
805 const auto unit_name = name() + "_radial_boundary";
806 std::unique_ptr<CSG::CSGNPolygonUnit> duct_ptr =
807 std::make_unique<CSG::CSGNPolygonUnit>(unit_name, n_sides, duct_boundaries.back());
808 auto & duct_unit = csg_obj->addEngUnit(std::move(duct_ptr));
809 auto assembly_region = -duct_unit;
810 if (extruded_assembly)
811 assembly_region &= axial_extent;
812 csg_obj->createCell(name() + "_root_cell", assembly_univ, assembly_region);
813
814 if (getReactorParam<bool>(RGMB::expand_units))
815 csg_obj->expandAllEngUnits();
816
817 return csg_obj;
818}
819
820void
822 const CSG::CSGLattice & assembly_lattice,
823 const std::vector<std::reference_wrapper<const CSG::CSGSurface>> & surfaces_by_axial_region,
824 CSG::CSGBase & csg_obj)
825{
826 if (_background_region_id.size() == 0)
827 // Cartesian lattices may not have a background region. In this case, the outer is left as void
828 return;
829
830 // Define outer fill of lattice
831 if (_mesh_dimensions == 2)
832 {
833 // In 2D, the outer fill will be a material fill
834 std::string region_name = "rgmb_region_" + std::to_string(_background_region_id[0]);
835 csg_obj.setLatticeOuter(assembly_lattice, region_name);
836 }
837 else // _mesh_dimension == 3
838 {
839 // In 3D, we define the outer fill as a universe
840 auto & outer_univ = createDuctFillUniverse(
841 name() + "_lattice_outer", surfaces_by_axial_region, _background_region_id, csg_obj);
842 csg_obj.setLatticeOuter(assembly_lattice, outer_univ);
843 }
844}
845
846const CSG::CSGUniverse &
848 const std::string & name_prefix,
849 const std::vector<std::reference_wrapper<const CSG::CSGSurface>> & surfaces_by_axial_region,
850 const std::vector<subdomain_id_type> & region_ids,
851 CSG::CSGBase & csg_obj)
852{
853 mooseAssert(surfaces_by_axial_region.size() - region_ids.size() == 1,
854 "Incorrect length of axial data vectors");
855 auto & fill_univ = csg_obj.createUniverse(name_prefix + "_univ");
856 for (const auto i : make_range(surfaces_by_axial_region.size() - 1))
857 {
858 CSG::CSGRegion axial_region;
859 const auto & lower_surf = surfaces_by_axial_region[i].get();
860 if (lower_surf != surfaces_by_axial_region.front())
861 axial_region = +lower_surf;
862 const auto & upper_surf = surfaces_by_axial_region[i + 1].get();
863 if (upper_surf != surfaces_by_axial_region.back())
864 {
865 if (axial_region.getRegionType() == CSG::CSGRegion::RegionType::EMPTY)
866 axial_region = -upper_surf;
867 else
868 axial_region &= -upper_surf;
869 }
870 auto cell_name = name_prefix + "_axial_" + std::to_string(i);
871 const auto mat_name = "rgmb_region_" + std::to_string(region_ids[i]);
872 csg_obj.createCell(cell_name, mat_name, axial_region, &fill_univ);
873 }
874
875 return fill_univ;
876}
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.
bool checkInputVectorInAscendingOrder(const std::vector< Real > &input_vec)
Check if input vector of Reals is defined in ascending order.
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.
bool checkUndefinedLatticeRefs(const std::vector< std::vector< unsigned int > > &input_lattice, unsigned int num_inputs)
Check if input lattice has values that are less than the number of input elements in lattice.
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