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