https://mooseframework.inl.gov
Loading...
Searching...
No Matches
PinMeshGenerator.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "PinMeshGenerator.h"
11
13#include <cmath>
14#include "MooseApp.h"
15#include "MooseMeshUtils.h"
16#include "Factory.h"
17#include "CSGNPolygonUnit.h"
18#include "DuctedPinEngUnit.h"
19#include "CSGPlane.h"
20#include "libmesh/elem.h"
21
23
26{
28
29 params.addRequiredParam<MeshGeneratorName>(
30 "reactor_params",
31 "The ReactorMeshParams MeshGenerator that is the basis for this component conformal mesh.");
32
33 params.addRequiredParam<subdomain_id_type>("pin_type",
34 "The integer ID for this pin type definition");
35
36 params.addRequiredRangeCheckedParam<Real>(
37 "pitch", "pitch>0.0", "The pitch for the outermost boundary polygon");
38
39 params.addRangeCheckedParam<unsigned int>(
40 "num_sectors", "num_sectors>0", "Number of azimuthal sectors in each quadrant");
41
42 params.addRangeCheckedParam<std::vector<Real>>(
43 "ring_radii",
44 "ring_radii>0.0",
45 "Radii of major concentric circles of the pin. If unspecified, no pin is present.");
46
47 params.addRangeCheckedParam<std::vector<Real>>(
48 "duct_halfpitch",
49 "duct_halfpitch>0.0",
50 "Apothem of the ducts. If unspecified, no duct is present.");
51
52 params.addRangeCheckedParam<std::vector<unsigned int>>(
53 "mesh_intervals",
54 std::vector<unsigned int>{1},
55 "mesh_intervals>0",
56 "The number of meshing intervals for each region starting at the center. Parameter should be "
57 "size:"
58 "((length(ring_radii) + length(duct_halfpitch) + 1");
59
60 params.addParam<std::vector<std::vector<std::string>>>(
61 "block_names",
62 "Block names for each radial and axial zone. "
63 "Inner indexing is radial zones (pin/background/duct), outer indexing is axial");
64
65 params.addParam<std::vector<std::vector<subdomain_id_type>>>(
66 "region_ids",
67 "IDs for each radial and axial zone for assignment of region_id extra element "
68 "id. "
69 "Inner indexing is radial zones (pin/background/duct), outer indexing is axial");
70
71 params.addParam<bool>("extrude",
72 false,
73 "Determines if this is the final step in the geometry construction"
74 " and extrudes the 2D geometry to 3D. If this is true then this mesh "
75 "cannot be used in further mesh building in the Reactor workflow");
76 params.addParam<bool>(
77 "homogenized", false, "Determines whether homogenized pin mesh should be generated");
78 params.addParam<bool>(
79 "use_as_assembly", false, "Determines whether pin mesh should be used as an assembly mesh");
80
81 params.addParam<bool>(
82 "quad_center_elements", true, "Whether the center elements are quad or triangular.");
83 params.addParamNamesToGroup("region_ids pin_type", "ID assigment");
84 params.addParamNamesToGroup(
85 "mesh_intervals ring_radii num_sectors pin_type homogenized use_as_assembly",
86 "Pin specifications");
87 params.addParamNamesToGroup("mesh_intervals duct_halfpitch num_sectors", "Duct specifications");
88
89 params.addClassDescription("This PinMeshGenerator object is designed to generate pin-like "
90 "structures, with IDs, from a reactor geometry. "
91 "Whether it be a square or hexagonal pin, they are divided into three "
92 "substructures - the innermost "
93 "radial pin regions, the single bridging background region, and the "
94 "square or hexagonal ducts regions.");
95
96 // Declare that this generator has a generateCSG method
98
99 return params;
100}
101
103 : ReactorGeometryMeshBuilderBase(parameters),
104 _pin_type(getParam<subdomain_id_type>("pin_type")),
105 _pitch(getParam<Real>("pitch")),
106 _num_sectors(isParamValid("num_sectors") ? getParam<unsigned int>("num_sectors") : 0),
107 _ring_radii(isParamValid("ring_radii") ? getParam<std::vector<Real>>("ring_radii")
108 : std::vector<Real>()),
109 _duct_halfpitch(isParamValid("duct_halfpitch") ? getParam<std::vector<Real>>("duct_halfpitch")
110 : std::vector<Real>()),
111 _intervals(getParam<std::vector<unsigned int>>("mesh_intervals")),
112 _region_ids(isParamValid("region_ids")
113 ? getParam<std::vector<std::vector<subdomain_id_type>>>("region_ids")
114 : std::vector<std::vector<subdomain_id_type>>()),
115 _extrude(getParam<bool>("extrude")),
116 _quad_center(getParam<bool>("quad_center_elements")),
117 _homogenized(getParam<bool>("homogenized")),
118 _is_assembly(getParam<bool>("use_as_assembly"))
119{
120 // Initialize ReactorMeshParams object
121 initializeReactorMeshParams(getParam<MeshGeneratorName>("reactor_params"));
122
123 _mesh_dimensions = getReactorParam<unsigned int>(RGMB::mesh_dimensions);
124 _mesh_geometry = getReactorParam<std::string>(RGMB::mesh_geometry);
125
126 if (_is_assembly)
127 {
128 auto assembly_pitch = getReactorParam<Real>(RGMB::assembly_pitch);
129 if (assembly_pitch != _pitch)
130 mooseError("Pitch defined in PinMeshGenerator must match assembly_pitch defined in "
131 "ReactorMeshParams if use_as_assembly is set to true");
132 }
133
134 if (_extrude && _mesh_dimensions != 3)
135 paramError("extrude",
136 "In order to extrude this mesh, ReactorMeshParams/dim needs to be set to 3\n");
137 if (_extrude && (!hasReactorParam<boundary_id_type>(RGMB::top_boundary_id) ||
138 !hasReactorParam<boundary_id_type>(RGMB::bottom_boundary_id)))
139 mooseError("Both top_boundary_id and bottom_boundary_id must be provided in ReactorMeshParams "
140 "if using extruded geometry");
141
142 if (_homogenized)
143 {
144 if (_mesh_geometry == "Square")
145 mooseError("Homogenization in PinMeshGenerator is only supported for hexagonal geometries");
146 const std::vector<std::string> disallowed_parameters = {
147 "num_sectors", "ring_radii", "duct_halfpitch", "mesh_intervals"};
148 for (const auto & parameter : disallowed_parameters)
149 if (parameters.isParamSetByUser(parameter))
150 paramError(parameter,
151 "Parameter " + parameter + " should not be defined for a homogenized pin mesh");
152 }
153 else
154 {
155 if (_num_sectors == 0)
157 "Number of sectors must be assigned with parameter num_sectors for non-homogenized pins");
158 if (_intervals.size() != (_ring_radii.size() + _duct_halfpitch.size() + 1))
160 "The number of mesh intervals must be equal to the number of annular regions + the "
161 "number of duct regions + 1"
162 " for the region between the rings and ducts\n");
163 }
164
165 if (isParamValid("region_ids"))
166 {
167 unsigned int n_axial_levels =
168 (_mesh_dimensions == 3)
169 ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
170 : 1;
171 if (_region_ids.size() != n_axial_levels)
172 mooseError("The size of region IDs must be equal to the number of axial levels as defined in "
173 "the ReactorMeshParams object");
174 if (_region_ids[0].size() != (_ring_radii.size() + _duct_halfpitch.size() + 1))
175 mooseError("The number of region IDs given needs to be one more than the number of "
176 "ring_radii + the number of duct_radii\n");
177 }
178 else
179 {
180 mooseError("Region IDs must be assigned with parameter region_ids");
181 }
182 if (isParamValid("block_names"))
183 {
184 if (getReactorParam<bool>(RGMB::region_id_as_block_name))
185 paramError("block_names",
186 "If ReactorMeshParams/region_id_as_block_name is set, block_names should not be "
187 "specified in PinMeshGenerator");
188 _has_block_names = true;
189 _block_names = getParam<std::vector<std::vector<std::string>>>("block_names");
190 if (_region_ids.size() != _block_names.size())
191 mooseError("The size of block_names must match the size of region_ids");
192 for (const auto i : index_range(_region_ids))
193 if (_region_ids[i].size() != _block_names[i].size())
194 mooseError("The size of block_names must match the size of region_ids");
195 }
196 else
197 _has_block_names = false;
198
199 const auto use_flexible_stitching = getReactorParam<bool>(RGMB::flexible_assembly_stitching);
200 std::string build_mesh_name;
201
202 // No subgenerators will be called if option to bypass mesh generators is enabled
203 if (!getReactorParam<bool>(RGMB::bypass_meshgen))
204 {
205 if (_homogenized)
206 {
207 // If flexible assembly stitching is invoked and this is a homogeneous assembly mesh, do not
208 // call mesh subgenerators here. The homogeneous assembly mesh should be created entirely in
209 // generateFlexibleAssemblyBoundaries()
210 bool skip_assembly_generation = _is_assembly && use_flexible_stitching;
211
212 auto params = _app.getFactory().getValidParams("SimpleHexagonGenerator");
213
214 params.set<Real>("hexagon_size") = _pitch / 2.0;
215 params.set<boundary_id_type>("external_boundary_id") =
217 const auto boundary_name =
219 std::to_string(_pin_type);
220 params.set<BoundaryName>("external_boundary_name") = boundary_name;
221 params.set<std::vector<subdomain_id_type>>("block_id") = {
223 params.set<MooseEnum>("element_type") = _quad_center ? "QUAD" : "TRI";
224 auto block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R0";
225 if (!_quad_center)
226 block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
227 params.set<std::vector<SubdomainName>>("block_name") = {block_name};
228
229 if (!skip_assembly_generation)
230 {
231 build_mesh_name = name() + "_2D";
232 addMeshSubgenerator("SimpleHexagonGenerator", build_mesh_name, params);
233 }
234 }
235 else
236 {
237 // Define all id variables used in the pin
238 std::vector<unsigned int> ring_intervals;
239 std::vector<subdomain_id_type> ring_blk_ids;
240 std::vector<SubdomainName> ring_blk_names;
241 unsigned int background_intervals = 1;
242 std::vector<subdomain_id_type> background_blk_ids;
243 std::vector<SubdomainName> background_blk_names;
244 std::vector<unsigned int> duct_intervals;
245 std::vector<subdomain_id_type> duct_blk_ids;
246 std::vector<SubdomainName> duct_blk_names;
247
248 for (const auto i : index_range(_intervals))
249 {
250 const auto block_name =
251 RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R" + std::to_string(i);
252 const auto block_id = RGMB::PIN_BLOCK_ID_START + i;
253
254 if (i < _ring_radii.size())
255 {
256 ring_intervals.push_back(_intervals[i]);
257 ring_blk_ids.push_back(block_id);
258 ring_blk_names.push_back(block_name);
259 }
260 else if (i > _ring_radii.size())
261 {
262 duct_intervals.push_back(_intervals[i]);
263 duct_blk_ids.push_back(block_id);
264 duct_blk_names.push_back(block_name);
265 }
266 else
267 {
268 background_intervals = _intervals[i];
269 background_blk_ids.push_back(block_id);
270 background_blk_names.push_back(block_name);
271 }
272 }
273 if (ring_intervals.size() > 0)
274 {
275 if (ring_intervals.front() != 1)
276 {
277 // If quad center elements, copy element at beginning of block names and
278 // block ids. Otherwise add RGMB::TRI_BLOCK_NAME_SUFFIX to block names and generate new
279 // block id
280 if (_quad_center)
281 {
282 ring_blk_ids.insert(ring_blk_ids.begin(), ring_blk_ids.front());
283 ring_blk_names.insert(ring_blk_names.begin(), ring_blk_names.front());
284 }
285 else
286 {
287 const auto block_name = ring_blk_names.front() + RGMB::TRI_BLOCK_NAME_SUFFIX;
288 const auto block_id = RGMB::PIN_BLOCK_ID_TRI;
289 ring_blk_ids.insert(ring_blk_ids.begin(), block_id);
290 ring_blk_names.insert(ring_blk_names.begin(), block_name);
291 }
292 }
293 // Add RGMB::TRI_BLOCK_NAME_SUFFIX if only one radial region and tri center elements
294 else if (!_quad_center)
295 {
296 ring_blk_ids[0] = RGMB::PIN_BLOCK_ID_TRI;
297 ring_blk_names[0] += RGMB::TRI_BLOCK_NAME_SUFFIX;
298 }
299 }
300 else
301 {
302 if (background_intervals > 1)
303 {
304 // If quad center elements, copy element at beginning of block names and
305 // block ids. Otherwise add RGMB::TRI_BLOCK_NAME_SUFFIX to block names and generate new
306 // block id
307 if (_quad_center)
308 {
309 background_blk_ids.insert(background_blk_ids.begin(), background_blk_ids.front());
310 background_blk_names.insert(background_blk_names.begin(), background_blk_names.front());
311 }
312 else
313 {
314 const auto block_name = background_blk_names.front() + RGMB::TRI_BLOCK_NAME_SUFFIX;
315 const auto block_id = RGMB::PIN_BLOCK_ID_TRI;
316 background_blk_ids.insert(background_blk_ids.begin(), block_id);
317 background_blk_names.insert(background_blk_names.begin(), block_name);
318 }
319 }
320 // Add RGMB::TRI_BLOCK_NAME_SUFFIX if only one background region and tri center elements
321 // and no ring regions
322 else if (!_quad_center)
323 {
324 background_blk_ids[0] = RGMB::PIN_BLOCK_ID_TRI;
325 background_blk_names[0] += RGMB::TRI_BLOCK_NAME_SUFFIX;
326 }
327 }
328
329 // If flexible assembly stitching is invoked and this is an assembly mesh with only a
330 // background region, do not call mesh subgenerators here. This assembly mesh should be
331 // created entirely in generateFlexibleAssemblyBoundaries()
332 bool skip_assembly_generation =
333 _is_assembly && use_flexible_stitching && _intervals.size() == 1;
334
335 if (!skip_assembly_generation)
336 {
337 // Generate Cartesian/hex pin using PolygonConcentricCircleMeshGenerator
338 {
339 // Get and assign parameters for the main geometry feature of the Pin
340 // which is created with a PolygonConcentricCircleMeshGenerator subgenerator
341 auto params = _app.getFactory().getValidParams("PolygonConcentricCircleMeshGenerator");
342 params.set<bool>("preserve_volumes") = true;
343 params.set<bool>("quad_center_elements") = _quad_center;
344 params.set<MooseEnum>("polygon_size_style") = "apothem";
345 params.set<Real>("polygon_size") = _pitch / 2.0;
346 params.set<boundary_id_type>("external_boundary_id") =
348 const auto boundary_name = (_is_assembly ? RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX
350 std::to_string(_pin_type);
351 params.set<BoundaryName>("external_boundary_name") = boundary_name;
352 bool flat_side_up = (_mesh_geometry == "Square");
353 params.set<bool>("flat_side_up") = flat_side_up;
354 params.set<bool>("create_outward_interface_boundaries") = false;
355
356 const auto num_sides = (_mesh_geometry == "Square") ? 4 : 6;
357 params.set<unsigned int>("num_sides") = num_sides;
358 params.set<std::vector<unsigned int>>("num_sectors_per_side") =
359 std::vector<unsigned int>(num_sides, _num_sectors);
360
361 if (ring_intervals.size() > 0)
362 {
363 params.set<std::vector<Real>>("ring_radii") = _ring_radii;
364 params.set<std::vector<subdomain_id_type>>("ring_block_ids") = ring_blk_ids;
365 params.set<std::vector<SubdomainName>>("ring_block_names") = ring_blk_names;
366 params.set<std::vector<unsigned int>>("ring_intervals") = ring_intervals;
367 }
368
369 params.set<std::vector<subdomain_id_type>>("background_block_ids") = background_blk_ids;
370 params.set<std::vector<SubdomainName>>("background_block_names") = background_blk_names;
371 params.set<unsigned int>("background_intervals") = background_intervals;
372
373 if (duct_intervals.size() > 0)
374 {
375 params.set<MooseEnum>("duct_sizes_style") = "apothem";
376 params.set<std::vector<Real>>("duct_sizes") = _duct_halfpitch;
377 params.set<std::vector<subdomain_id_type>>("duct_block_ids") = duct_blk_ids;
378 params.set<std::vector<SubdomainName>>("duct_block_names") = duct_blk_names;
379 params.set<std::vector<unsigned int>>("duct_intervals") = duct_intervals;
380 }
381
382 addMeshSubgenerator("PolygonConcentricCircleMeshGenerator", name() + "_2D", params);
383 }
384
385 // Remove extra sidesets created by PolygonConcentricCircleMeshGenerator
386 {
387 auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
388
389 params.set<MeshGeneratorName>("input") = name() + "_2D";
390
391 auto num_sides = (_mesh_geometry == "Square") ? 4 : 6;
392 std::vector<BoundaryName> boundaries_to_delete = {};
393 for (const auto i : make_range(num_sides))
394 boundaries_to_delete.insert(boundaries_to_delete.end(),
395 {std::to_string(10001 + i), std::to_string(15001 + i)});
396 params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
397
398 build_mesh_name = name() + "_delbds";
399 addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
400 }
401 }
402 }
403
404 // For pin acting as assembly, modify outermost mesh interval to enable flexible assembly
405 // stitching
406 if (_is_assembly && use_flexible_stitching)
407 {
409 build_mesh_name = name() + "_fpg_delbds";
410 }
411
412 // Pass mesh meta-data defined in subgenerator constructor to this MeshGenerator
413 if (hasMeshProperty<Real>("pitch_meta", name() + "_2D"))
414 copyMeshProperty<Real>("pitch_meta", name() + "_2D");
415 if (hasMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", name() + "_2D"))
416 copyMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", name() + "_2D");
417 if (hasMeshProperty<Real>("max_radius_meta", name() + "_2D"))
418 copyMeshProperty<Real>("max_radius_meta", name() + "_2D");
419 if (hasMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D"))
420 copyMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D");
421 if (hasMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D"))
422 copyMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D");
423
424 if (_is_assembly)
425 declareMeshProperty("pattern_pitch_meta", getReactorParam<Real>(RGMB::assembly_pitch));
426 else if (hasMeshProperty<Real>("pattern_pitch_meta", name() + "_2D"))
427 copyMeshProperty<Real>("pattern_pitch_meta", name() + "_2D");
428 declareMeshProperty("is_control_drum_meta", false);
429
430 if (_extrude)
431 build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
432
433 // Store final mesh subgenerator
434 _build_mesh = &getMeshByName(build_mesh_name);
435 }
436
438}
439
440void
442{
443 SubdomainName outermost_block_name;
444 bool has_single_mesh_interval;
445
446 // Assemblies that invoke this method are either homogenized or have a single pin. First check if
447 // the assembly only has a single region. Otherwise, determine the outermost region for deletion
448 if (_homogenized || (_intervals.size() == 1))
449 {
450 outermost_block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R0";
451 has_single_mesh_interval = true;
452 }
453 else
454 {
455 outermost_block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R" +
456 std::to_string(_intervals.size() - 1);
457 has_single_mesh_interval = false;
458
459 // Invoke BlockDeletionGenerator to delete outermost mesh interval of assembly
460 auto params = _app.getFactory().getValidParams("BlockDeletionGenerator");
461
462 params.set<std::vector<SubdomainName>>("block") = {outermost_block_name};
463 params.set<MeshGeneratorName>("input") = _homogenized ? name() + "_2D" : name() + "_delbds";
464
465 addMeshSubgenerator("BlockDeletionGenerator", name() + "_del_outer", params);
466 }
467
468 {
469 // Invoke FlexiblePatternGenerator to triangulate deleted mesh interval
470 auto params = _app.getFactory().getValidParams("FlexiblePatternGenerator");
471
472 if (has_single_mesh_interval)
473 params.set<std::vector<MeshGeneratorName>>("inputs") = {};
474 else
475 {
476 params.set<std::vector<MeshGeneratorName>>("inputs") = {name() + "_del_outer"};
477 params.set<std::vector<libMesh::Point>>("extra_positions") = {libMesh::Point(0, 0, 0)};
478 params.set<std::vector<unsigned int>>("extra_positions_mg_indices") = {0};
479 }
480 params.set<bool>("use_auto_area_func") = true;
481 params.set<bool>("verify_holes") = false;
482 params.set<MooseEnum>("boundary_type") = (_mesh_geometry == "Hex") ? "HEXAGON" : "CARTESIAN";
483 params.set<unsigned int>("boundary_sectors") =
484 getReactorParam<unsigned int>(RGMB::num_sectors_flexible_stitching);
485 params.set<Real>("boundary_size") = getReactorParam<Real>(RGMB::assembly_pitch);
486 params.set<boundary_id_type>("external_boundary_id") = RGMB::PIN_BOUNDARY_ID_START + _pin_type;
487 params.set<BoundaryName>("external_boundary_name") =
489 params.set<SubdomainName>("background_subdomain_name") =
490 outermost_block_name + RGMB::TRI_BLOCK_NAME_SUFFIX;
491 params.set<unsigned short>("background_subdomain_id") = RGMB::PIN_BLOCK_ID_TRI_FLEXIBLE;
492
493 addMeshSubgenerator("FlexiblePatternGenerator", name() + "_fpg", params);
494 }
495 {
496 // Delete extra boundary created by FlexiblePatternGenerator
497 auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
498
499 params.set<MeshGeneratorName>("input") = name() + "_fpg";
500 std::vector<BoundaryName> boundaries_to_delete = {};
501 if (!has_single_mesh_interval)
502 boundaries_to_delete.push_back(std::to_string(1));
503 params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
504
505 addMeshSubgenerator("BoundaryDeletionGenerator", name() + "_fpg_delbds", params);
506 }
507}
508
509void
511{
512 // Store pin region ids and block names for id swap after extrusion if needed
513 // by future mesh generators
514 std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>> region_id_map{
516
517 // Declare mesh properties that need to be moved up to the assembly level
518 if (_is_assembly)
519 {
521 declareMeshProperty(RGMB::background_block_name, std::vector<std::string>());
522 declareMeshProperty(RGMB::duct_block_names, std::vector<std::vector<std::string>>());
525 // Following metadata is only relevant if an output mesh is generated by RGMB
526 // because it pertains to region & block ids of elements in the output mesh
527 if (!getReactorParam<bool>(RGMB::bypass_meshgen))
528 {
529 std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>> pin_region_id_map;
530 pin_region_id_map.insert(
531 std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
532 region_id_map.begin()->first, region_id_map.begin()->second));
534 std::map<subdomain_id_type, std::vector<std::vector<std::string>>> pin_block_name_map;
535 pin_block_name_map.insert(std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(
538 }
539 }
540 // Declare mesh properties that are only relevant to pin meshes
541 else
542 {
546 }
547
548 // Set metadata to describe pin attributes
554
555 unsigned int n_axial_levels =
556 (_mesh_dimensions == 3)
557 ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
558 : 1;
559 std::vector<std::vector<subdomain_id_type>> ring_region_ids(
560 n_axial_levels, std::vector<subdomain_id_type>(_ring_radii.size()));
561 std::vector<std::vector<subdomain_id_type>> duct_region_ids(
562 n_axial_levels, std::vector<subdomain_id_type>(_duct_halfpitch.size()));
563 std::vector<subdomain_id_type> background_region_ids(n_axial_levels);
564
565 for (const auto axial_idx : make_range(n_axial_levels))
566 {
567 for (const auto ring_idx : index_range(_ring_radii))
568 ring_region_ids[axial_idx][ring_idx] = _region_ids[axial_idx][ring_idx];
569
570 background_region_ids[axial_idx] = _region_ids[axial_idx][_ring_radii.size()];
571
572 for (unsigned int duct_idx = _ring_radii.size() + 1;
573 duct_idx < _duct_halfpitch.size() + _ring_radii.size() + 1;
574 ++duct_idx)
575 duct_region_ids[axial_idx][duct_idx - _ring_radii.size() - 1] =
576 _region_ids[axial_idx][duct_idx];
577 }
578
579 // Define mesh properties related to region ids
581 declareMeshProperty(RGMB::background_region_id, background_region_ids);
583}
584
585std::unique_ptr<MeshBase>
587{
588 // Must be called to free the ReactorMeshParams mesh
590
591 // If bypass_mesh is true, return a null mesh. In this mode, an output mesh is not
592 // generated and only metadata is defined on the generator, so logic related to
593 // generation of output mesh will not be called
594 if (getReactorParam<bool>(RGMB::bypass_meshgen))
595 {
596 auto null_mesh = nullptr;
597 return null_mesh;
598 }
599
600 // Update metadata at this point since values for these metadata only get set by PCCMG
601 // at generate() stage
602 if (hasMeshProperty<Real>("max_radius_meta", name() + "_2D"))
603 {
604 const auto max_radius_meta = getMeshProperty<Real>("max_radius_meta", name() + "_2D");
605 setMeshProperty("max_radius_meta", max_radius_meta);
606 }
607 if (hasMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D"))
608 {
609 const auto background_intervals_meta =
610 getMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D");
611 setMeshProperty("background_intervals_meta", background_intervals_meta);
612 }
613 if (hasMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D"))
614 {
615 const auto node_id_background_meta =
616 getMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D");
617 setMeshProperty("node_id_background_meta", node_id_background_meta);
618 }
619
620 // This generate() method will be called once the subgenerators that we depend on
621 // have been called. This is where we reassign subdomain ids/names according to what
622 // the user has provided, and also where we set region_id, pin_type_id, and radial_id
623 // extra element integers
624
625 // Add region id, pin type id, and radial id to the mesh as element integers
626 std::string region_id_name = "region_id";
627 std::string pin_type_id_name = "pin_type_id";
628 std::string assembly_type_id_name = "assembly_type_id";
629 std::string plane_id_name = "plane_id";
630 std::string radial_id_name = "radial_id";
631 const std::string default_block_name =
633 std::to_string(_pin_type);
634
635 auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name);
636 auto radial_id_int = getElemIntegerFromMesh(*(*_build_mesh), radial_id_name);
637 auto pin_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), pin_type_id_name);
638 unsigned int plane_id_int = 0;
639 unsigned int assembly_type_id_int = 0;
640 if (_extrude)
641 plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
642 if (_is_assembly)
643 assembly_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), assembly_type_id_name);
644
645 // Get next free block ID in mesh in case subdomain ids need to be remapped
646 auto next_block_id = MooseMeshUtils::getNextFreeSubdomainID(*(*(_build_mesh)));
647 std::map<std::string, SubdomainID> rgmb_name_id_map;
648
649 // Loop through all elements and set regions ids, pin type id, and radial idx.
650 // These element integers are also used to infer the block name for the region,
651 // and block IDs/names will be reassigned on the pin mesh if necessary
652 for (auto & elem : (*_build_mesh)->active_element_ptr_range())
653 {
654 const auto base_block_id = elem->subdomain_id();
655 const auto base_block_name = (*_build_mesh)->subdomain_name(base_block_id);
656
657 // Check if block name has correct prefix
658 std::string prefix = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R";
659 if (!(base_block_name.find(prefix, 0) == 0))
660 continue;
661 // Radial index is integer value of substring after prefix
662 std::string radial_str = base_block_name.substr(prefix.length());
663
664 // Filter out RGMB::TRI_BLOCK_NAME_SUFFIX if needed
665 const std::string suffix = RGMB::TRI_BLOCK_NAME_SUFFIX;
666 const std::size_t found = radial_str.find(suffix);
667 if (found != std::string::npos)
668 radial_str.replace(found, suffix.length(), "");
669 const unsigned int radial_idx = std::stoi(radial_str);
670
671 // Region id is inferred from z_id and radial_idx
672 dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
673 const subdomain_id_type elem_region_id = _region_ids[std::size_t(z_id)][radial_idx];
674
675 // Set element integers
676 elem->set_extra_integer(region_id_int, elem_region_id);
677 elem->set_extra_integer(pin_type_id_int, _pin_type);
678 elem->set_extra_integer(radial_id_int, radial_idx);
679 if (_is_assembly)
680 elem->set_extra_integer(assembly_type_id_int, _pin_type);
681
682 // Set element block name and block id
683 auto elem_block_name = default_block_name;
685 elem_block_name += "_" + _block_names[std::size_t(z_id)][radial_idx];
686 else if (getReactorParam<bool>(RGMB::region_id_as_block_name))
687 elem_block_name += "_REG" + std::to_string(elem_region_id);
688 if (elem->type() == TRI3 || elem->type() == PRISM6)
689 elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
691 *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
692 }
693
694 // Mark mesh as not prepared, as block IDs were re-assigned in this method
695 (*_build_mesh)->unset_is_prepared();
696
697 return std::move(*_build_mesh);
698}
699
700std::unique_ptr<CSG::CSGBase>
702{
703 // Must be called to free the ReactorMeshParams CSGBase object
705
706 auto csg_obj = std::make_unique<CSG::CSGBase>();
707
708 // Convert region IDs to actual region names that will be stored as material fills.
709 std::vector<std::vector<std::string>> region_names(_region_ids.size());
710 for (const auto i : index_range(_region_ids))
711 {
712 region_names[i].resize(_region_ids[i].size());
713 for (const auto j : index_range(_region_ids[i]))
714 region_names[i][j] = "rgmb_region_" + std::to_string(_region_ids[i][j]);
715 }
716
717 std::vector<Real> axial_plane_levels;
718 std::vector<std::string> axial_plane_names;
719 Real top_axial_level = 0.;
720 if (_mesh_dimensions == 3)
721 {
722 auto axial_boundaries = getReactorParam<std::vector<Real>>(RGMB::axial_mesh_sizes);
723 for (const auto i : make_range(axial_boundaries.size()))
724 {
725 top_axial_level += axial_boundaries[i];
726 // Top and bottom axial planes should be removed for the construction of
727 // DuctedPinEngUnit, as this unit creates an infinite universe axially
728 if (i != axial_boundaries.size() - 1)
729 {
730 axial_plane_levels.push_back(top_axial_level);
731 axial_plane_names.push_back(RGMB::CSG_AXIAL_PLANE_PREFIX + std::to_string(i));
732 }
733 }
734 }
735
736 // Define ducted pin engineering unit and add it to CSGBase
737 std::unique_ptr<CSG::DuctedPinEngUnit> pin_ptr =
738 std::make_unique<CSG::DuctedPinEngUnit>(name() + "_ducted_pin_unit",
742 region_names,
743 axial_plane_levels,
744 axial_plane_names);
745 auto & pin_unit = csg_obj->addEngUnit(std::move(pin_ptr));
746
747 // Define radial and axial extent of pin to constrain the universe created by DuctedPinEngUnit
748 const auto unit_name = _name + "_radial_boundary";
749 const auto n_sides = (_mesh_geometry == "Hex") ? 6 : 4;
750 std::unique_ptr<CSG::CSGNPolygonUnit> radial_boundary_ptr =
751 std::make_unique<CSG::CSGNPolygonUnit>(unit_name, n_sides, _pitch / 2.);
752 auto & radial_boundary_unit = csg_obj->addEngUnit(std::move(radial_boundary_ptr));
753 auto pin_region = -radial_boundary_unit;
754 if (_mesh_dimensions == 3)
755 {
756 const auto bottom_surf_name = RGMB::CSG_AXIAL_PLANE_PREFIX + "bottom_boundary";
757 std::unique_ptr<CSG::CSGSurface> bottom_surf_ptr =
758 std::make_unique<CSG::CSGPlane>(bottom_surf_name, 0, 0, 1, 0.);
759 const auto & bottom_plane_surf = csg_obj->addSurface(std::move(bottom_surf_ptr));
760
761 const auto top_surf_name = RGMB::CSG_AXIAL_PLANE_PREFIX + "top_boundary";
762 std::unique_ptr<CSG::CSGSurface> top_surf_ptr =
763 std::make_unique<CSG::CSGPlane>(top_surf_name, 0, 0, 1, top_axial_level);
764 const auto & top_plane_surf = csg_obj->addSurface(std::move(top_surf_ptr));
765
766 auto axial_region = +bottom_plane_surf & -top_plane_surf;
767 pin_region &= axial_region;
768 }
769
770 // Create cell that constrains DuctedPinEngUnit based on the outer boundary
771 // of the pin
772 csg_obj->createCell(_name + "_root_cell", pin_unit, pin_region);
773
774 if (getReactorParam<bool>(RGMB::expand_units))
775 csg_obj->expandAllEngUnits();
776
777 return csg_obj;
778}
registerMooseObject("ReactorApp", PinMeshGenerator)
void ErrorVector unsigned int
InputParameters getValidParams(const std::string &name) const
bool isParamSetByUser(const std::string &name) const
T & set(const std::string &name, bool quiet_mode=false)
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)
T & declareMeshProperty(const std::string &data_name, Args &&... args)
bool hasMeshProperty(const std::string &data_name, const std::string &prefix) const
Factory & getFactory()
const InputParameters & parameters() const
const std::string & name() const
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
const std::string & _name
bool isParamValid(const std::string &name) const
MooseApp & _app
Mesh generator for defining a reactor pin with background and duct regions, with the option to be 2-D...
std::vector< unsigned int > _intervals
The number of mesh intervals in a radial division starting from the center.
const std::vector< Real > _duct_halfpitch
The inner apothem of any surrounding ducts in the pin.
std::unique_ptr< MeshBase > generate() override
bool _homogenized
Whether the resulting pin mesh should be homogenized.
PinMeshGenerator(const InputParameters &parameters)
unsigned int _mesh_dimensions
The number of dimensions the mesh is ultimately going to have (2 or 3, declared in the ReactorMeshPar...
static InputParameters validParams()
std::unique_ptr< CSG::CSGBase > generateCSG() override
void generateFlexibleAssemblyBoundaries()
const unsigned int _num_sectors
The number of azimuthal divisions.
std::vector< Real > _ring_radii
The outer radii of concentric rings in the pin.
bool _quad_center
Whether the centermost elements in the pin should be quad elements as opposed to tri elements.
std::unique_ptr< MeshBase > * _build_mesh
The final mesh that is generated by the subgenerators; This mesh is generated by the subgenerators wi...
const bool _extrude
Whether this mesh should be extruded to 3-D, making it the final structure in the reactor mesh.
bool _has_block_names
Whether block names have been provided by user.
std::vector< std::vector< subdomain_id_type > > _region_ids
2-D vector (axial outer indexing, radial inner indexing) used to set "region_id" extra-element intege...
const Real _pitch
The face-to-face size of this pin.
std::vector< std::vector< std::string > > _block_names
2-D vector (axial outer indexing, radial inner indexing) used to set block names of pin mesh elements
std::string _mesh_geometry
The type of geometry that is being described (Square or Hex, declared in the ReactorMeshParams object...
const subdomain_id_type _pin_type
The id number for this pin type.
bool _is_assembly
Whether the resulting pin mesh should also be used as an assembly mesh.
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.
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 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)
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
const subdomain_id_type PIN_BLOCK_ID_TRI_FLEXIBLE
static const std::string expand_units
static const std::string is_control_drum
static const std::string bypass_meshgen
const subdomain_id_type PIN_BLOCK_ID_START
static const std::string ring_radii
static const std::string top_boundary_id
const subdomain_id_type PIN_BLOCK_ID_TRI
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 constexpr boundary_id_type PIN_BOUNDARY_ID_START
static const std::string num_sectors_flexible_stitching
static const std::string background_region_id
static const std::string ring_region_ids
static const std::string flexible_assembly_stitching
static const std::string is_homogenized
static const std::string axial_mesh_sizes
static const std::string CSG_AXIAL_PLANE_PREFIX
static const std::string region_id_as_block_name
static const std::string background_block_name
static const std::string axial_mesh_intervals
static const std::string pitch
static const std::string pin_block_names
static const std::string mesh_geometry
static const std::string pin_type
const SubdomainName PIN_BLOCK_NAME_PREFIX