Line data Source code
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 :
12 : #include "ReactorGeometryMeshBuilderBase.h"
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 :
23 : registerMooseObject("ReactorApp", PinMeshGenerator);
24 :
25 : InputParameters
26 1790 : PinMeshGenerator::validParams()
27 : {
28 1790 : auto params = ReactorGeometryMeshBuilderBase::validParams();
29 :
30 3580 : params.addRequiredParam<MeshGeneratorName>(
31 : "reactor_params",
32 : "The ReactorMeshParams MeshGenerator that is the basis for this component conformal mesh.");
33 :
34 3580 : params.addRequiredParam<subdomain_id_type>("pin_type",
35 : "The integer ID for this pin type definition");
36 :
37 3580 : params.addRequiredRangeCheckedParam<Real>(
38 : "pitch", "pitch>0.0", "The pitch for the outermost boundary polygon");
39 :
40 3580 : params.addRangeCheckedParam<unsigned int>(
41 : "num_sectors", "num_sectors>0", "Number of azimuthal sectors in each quadrant");
42 :
43 3580 : 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 3580 : 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 5370 : params.addRangeCheckedParam<std::vector<unsigned int>>(
54 : "mesh_intervals",
55 3580 : 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 3580 : 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 3580 : 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 3580 : params.addParam<bool>("extrude",
73 3580 : 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 3580 : params.addParam<bool>(
78 3580 : "homogenized", false, "Determines whether homogenized pin mesh should be generated");
79 3580 : params.addParam<bool>(
80 3580 : "use_as_assembly", false, "Determines whether pin mesh should be used as an assembly mesh");
81 :
82 3580 : params.addParam<bool>(
83 3580 : "quad_center_elements", true, "Whether the center elements are quad or triangular.");
84 3580 : params.addParamNamesToGroup("region_ids pin_type", "ID assigment");
85 3580 : params.addParamNamesToGroup(
86 : "mesh_intervals ring_radii num_sectors pin_type homogenized use_as_assembly",
87 : "Pin specifications");
88 3580 : params.addParamNamesToGroup("mesh_intervals duct_halfpitch num_sectors", "Duct specifications");
89 :
90 1790 : 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
98 1790 : MeshGenerator::setHasGenerateCSG(params);
99 :
100 1790 : return params;
101 0 : }
102 :
103 892 : PinMeshGenerator::PinMeshGenerator(const InputParameters & parameters)
104 : : ReactorGeometryMeshBuilderBase(parameters),
105 892 : _pin_type(getParam<subdomain_id_type>("pin_type")),
106 1784 : _pitch(getParam<Real>("pitch")),
107 3390 : _num_sectors(isParamValid("num_sectors") ? getParam<unsigned int>("num_sectors") : 0),
108 3238 : _ring_radii(isParamValid("ring_radii") ? getParam<std::vector<Real>>("ring_radii")
109 : : std::vector<Real>()),
110 2953 : _duct_halfpitch(isParamValid("duct_halfpitch") ? getParam<std::vector<Real>>("duct_halfpitch")
111 : : std::vector<Real>()),
112 1784 : _intervals(getParam<std::vector<unsigned int>>("mesh_intervals")),
113 3568 : _region_ids(isParamValid("region_ids")
114 892 : ? getParam<std::vector<std::vector<subdomain_id_type>>>("region_ids")
115 : : std::vector<std::vector<subdomain_id_type>>()),
116 1784 : _extrude(getParam<bool>("extrude")),
117 1784 : _quad_center(getParam<bool>("quad_center_elements")),
118 1784 : _homogenized(getParam<bool>("homogenized")),
119 2676 : _is_assembly(getParam<bool>("use_as_assembly"))
120 : {
121 : // Initialize ReactorMeshParams object
122 2676 : initializeReactorMeshParams(getParam<MeshGeneratorName>("reactor_params"));
123 :
124 892 : _mesh_dimensions = getReactorParam<unsigned int>(RGMB::mesh_dimensions);
125 892 : _mesh_geometry = getReactorParam<std::string>(RGMB::mesh_geometry);
126 :
127 892 : if (_is_assembly)
128 : {
129 140 : auto assembly_pitch = getReactorParam<Real>(RGMB::assembly_pitch);
130 140 : if (assembly_pitch != _pitch)
131 0 : mooseError("Pitch defined in PinMeshGenerator must match assembly_pitch defined in "
132 : "ReactorMeshParams if use_as_assembly is set to true");
133 : }
134 :
135 892 : if (_extrude && _mesh_dimensions != 3)
136 0 : paramError("extrude",
137 : "In order to extrude this mesh, ReactorMeshParams/dim needs to be set to 3\n");
138 1852 : if (_extrude && (!hasReactorParam<boundary_id_type>(RGMB::top_boundary_id) ||
139 960 : !hasReactorParam<boundary_id_type>(RGMB::bottom_boundary_id)))
140 0 : mooseError("Both top_boundary_id and bottom_boundary_id must be provided in ReactorMeshParams "
141 : "if using extruded geometry");
142 :
143 892 : if (_homogenized)
144 : {
145 89 : if (_mesh_geometry == "Square")
146 0 : mooseError("Homogenization in PinMeshGenerator is only supported for hexagonal geometries");
147 : const std::vector<std::string> disallowed_parameters = {
148 89 : "num_sectors", "ring_radii", "duct_halfpitch", "mesh_intervals"};
149 445 : for (const auto & parameter : disallowed_parameters)
150 356 : if (parameters.isParamSetByUser(parameter))
151 0 : paramError(parameter,
152 0 : "Parameter " + parameter + " should not be defined for a homogenized pin mesh");
153 89 : }
154 : else
155 : {
156 803 : if (_num_sectors == 0)
157 0 : mooseError(
158 : "Number of sectors must be assigned with parameter num_sectors for non-homogenized pins");
159 803 : if (_intervals.size() != (_ring_radii.size() + _duct_halfpitch.size() + 1))
160 0 : mooseError(
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 1784 : if (isParamValid("region_ids"))
167 : {
168 : unsigned int n_axial_levels =
169 892 : (_mesh_dimensions == 3)
170 : ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
171 1603 : : 1;
172 892 : if (_region_ids.size() != n_axial_levels)
173 0 : mooseError("The size of region IDs must be equal to the number of axial levels as defined in "
174 : "the ReactorMeshParams object");
175 892 : if (_region_ids[0].size() != (_ring_radii.size() + _duct_halfpitch.size() + 1))
176 0 : 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 0 : mooseError("Region IDs must be assigned with parameter region_ids");
182 : }
183 1784 : if (isParamValid("block_names"))
184 : {
185 135 : if (getReactorParam<bool>(RGMB::region_id_as_block_name))
186 2 : paramError("block_names",
187 : "If ReactorMeshParams/region_id_as_block_name is set, block_names should not be "
188 : "specified in PinMeshGenerator");
189 133 : _has_block_names = true;
190 399 : _block_names = getParam<std::vector<std::vector<std::string>>>("block_names");
191 133 : if (_region_ids.size() != _block_names.size())
192 0 : mooseError("The size of block_names must match the size of region_ids");
193 399 : for (const auto i : index_range(_region_ids))
194 266 : if (_region_ids[i].size() != _block_names[i].size())
195 0 : mooseError("The size of block_names must match the size of region_ids");
196 : }
197 : else
198 757 : _has_block_names = false;
199 :
200 890 : 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 890 : if (!getReactorParam<bool>(RGMB::bypass_meshgen))
205 : {
206 700 : 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 72 : bool skip_assembly_generation = _is_assembly && use_flexible_stitching;
212 :
213 72 : auto params = _app.getFactory().getValidParams("SimpleHexagonGenerator");
214 :
215 72 : params.set<Real>("hexagon_size") = _pitch / 2.0;
216 72 : params.set<boundary_id_type>("external_boundary_id") =
217 72 : RGMB::PIN_BOUNDARY_ID_START + _pin_type;
218 : const auto boundary_name =
219 72 : (_is_assembly ? RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX : RGMB::PIN_BOUNDARY_NAME_PREFIX) +
220 216 : std::to_string(_pin_type);
221 144 : params.set<BoundaryName>("external_boundary_name") = boundary_name;
222 72 : params.set<std::vector<subdomain_id_type>>("block_id") = {
223 188 : _quad_center ? RGMB::PIN_BLOCK_ID_START : RGMB::PIN_BLOCK_ID_TRI};
224 188 : params.set<MooseEnum>("element_type") = _quad_center ? "QUAD" : "TRI";
225 72 : auto block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R0";
226 72 : if (!_quad_center)
227 : block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
228 216 : params.set<std::vector<SubdomainName>>("block_name") = {block_name};
229 :
230 72 : if (!skip_assembly_generation)
231 : {
232 42 : build_mesh_name = name() + "_2D";
233 84 : addMeshSubgenerator("SimpleHexagonGenerator", build_mesh_name, params);
234 : }
235 72 : }
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 1923 : for (const auto i : index_range(_intervals))
250 : {
251 : const auto block_name =
252 3885 : RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R" + std::to_string(i);
253 1295 : const auto block_id = RGMB::PIN_BLOCK_ID_START + i;
254 :
255 1295 : if (i < _ring_radii.size())
256 : {
257 463 : ring_intervals.push_back(_intervals[i]);
258 463 : ring_blk_ids.push_back(block_id);
259 463 : ring_blk_names.push_back(block_name);
260 : }
261 832 : else if (i > _ring_radii.size())
262 : {
263 204 : duct_intervals.push_back(_intervals[i]);
264 204 : duct_blk_ids.push_back(block_id);
265 204 : duct_blk_names.push_back(block_name);
266 : }
267 : else
268 : {
269 628 : background_intervals = _intervals[i];
270 628 : background_blk_ids.push_back(block_id);
271 628 : background_blk_names.push_back(block_name);
272 : }
273 : }
274 628 : if (ring_intervals.size() > 0)
275 : {
276 435 : 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 14 : if (_quad_center)
282 : {
283 7 : ring_blk_ids.insert(ring_blk_ids.begin(), ring_blk_ids.front());
284 7 : ring_blk_names.insert(ring_blk_names.begin(), ring_blk_names.front());
285 : }
286 : else
287 : {
288 7 : const auto block_name = ring_blk_names.front() + RGMB::TRI_BLOCK_NAME_SUFFIX;
289 7 : const auto block_id = RGMB::PIN_BLOCK_ID_TRI;
290 7 : ring_blk_ids.insert(ring_blk_ids.begin(), block_id);
291 7 : 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 421 : else if (!_quad_center)
296 : {
297 234 : ring_blk_ids[0] = RGMB::PIN_BLOCK_ID_TRI;
298 234 : ring_blk_names[0] += RGMB::TRI_BLOCK_NAME_SUFFIX;
299 : }
300 : }
301 : else
302 : {
303 193 : 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 134 : if (_quad_center)
309 : {
310 127 : background_blk_ids.insert(background_blk_ids.begin(), background_blk_ids.front());
311 127 : background_blk_names.insert(background_blk_names.begin(), background_blk_names.front());
312 : }
313 : else
314 : {
315 7 : const auto block_name = background_blk_names.front() + RGMB::TRI_BLOCK_NAME_SUFFIX;
316 7 : const auto block_id = RGMB::PIN_BLOCK_ID_TRI;
317 7 : background_blk_ids.insert(background_blk_ids.begin(), block_id);
318 7 : 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 59 : else if (!_quad_center)
324 : {
325 52 : background_blk_ids[0] = RGMB::PIN_BLOCK_ID_TRI;
326 52 : 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 628 : _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 618 : auto params = _app.getFactory().getValidParams("PolygonConcentricCircleMeshGenerator");
343 618 : params.set<bool>("preserve_volumes") = true;
344 618 : params.set<bool>("quad_center_elements") = _quad_center;
345 1236 : params.set<MooseEnum>("polygon_size_style") = "apothem";
346 618 : params.set<Real>("polygon_size") = _pitch / 2.0;
347 618 : params.set<boundary_id_type>("external_boundary_id") =
348 618 : RGMB::PIN_BOUNDARY_ID_START + _pin_type;
349 618 : const auto boundary_name = (_is_assembly ? RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX
350 : : RGMB::PIN_BOUNDARY_NAME_PREFIX) +
351 1854 : std::to_string(_pin_type);
352 1236 : params.set<BoundaryName>("external_boundary_name") = boundary_name;
353 618 : bool flat_side_up = (_mesh_geometry == "Square");
354 618 : params.set<bool>("flat_side_up") = flat_side_up;
355 618 : params.set<bool>("create_outward_interface_boundaries") = false;
356 :
357 618 : const auto num_sides = (_mesh_geometry == "Square") ? 4 : 6;
358 618 : params.set<unsigned int>("num_sides") = num_sides;
359 618 : params.set<std::vector<unsigned int>>("num_sectors_per_side") =
360 1236 : std::vector<unsigned int>(num_sides, _num_sectors);
361 :
362 618 : if (ring_intervals.size() > 0)
363 : {
364 435 : params.set<std::vector<Real>>("ring_radii") = _ring_radii;
365 435 : params.set<std::vector<subdomain_id_type>>("ring_block_ids") = ring_blk_ids;
366 435 : params.set<std::vector<SubdomainName>>("ring_block_names") = ring_blk_names;
367 870 : params.set<std::vector<unsigned int>>("ring_intervals") = ring_intervals;
368 : }
369 :
370 618 : params.set<std::vector<subdomain_id_type>>("background_block_ids") = background_blk_ids;
371 618 : params.set<std::vector<SubdomainName>>("background_block_names") = background_blk_names;
372 618 : params.set<unsigned int>("background_intervals") = background_intervals;
373 :
374 618 : if (duct_intervals.size() > 0)
375 : {
376 408 : params.set<MooseEnum>("duct_sizes_style") = "apothem";
377 204 : params.set<std::vector<Real>>("duct_sizes") = _duct_halfpitch;
378 204 : params.set<std::vector<subdomain_id_type>>("duct_block_ids") = duct_blk_ids;
379 204 : params.set<std::vector<SubdomainName>>("duct_block_names") = duct_blk_names;
380 408 : params.set<std::vector<unsigned int>>("duct_intervals") = duct_intervals;
381 : }
382 :
383 1236 : addMeshSubgenerator("PolygonConcentricCircleMeshGenerator", name() + "_2D", params);
384 618 : }
385 :
386 : // Remove extra sidesets created by PolygonConcentricCircleMeshGenerator
387 : {
388 1236 : auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
389 :
390 1854 : params.set<MeshGeneratorName>("input") = name() + "_2D";
391 :
392 618 : auto num_sides = (_mesh_geometry == "Square") ? 4 : 6;
393 : std::vector<BoundaryName> boundaries_to_delete = {};
394 3810 : for (const auto i : make_range(num_sides))
395 9576 : boundaries_to_delete.insert(boundaries_to_delete.end(),
396 9576 : {std::to_string(10001 + i), std::to_string(15001 + i)});
397 1236 : params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
398 :
399 618 : build_mesh_name = name() + "_delbds";
400 1236 : addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
401 618 : }
402 : }
403 628 : }
404 :
405 : // For pin acting as assembly, modify outermost mesh interval to enable flexible assembly
406 : // stitching
407 700 : if (_is_assembly && use_flexible_stitching)
408 : {
409 55 : generateFlexibleAssemblyBoundaries();
410 110 : build_mesh_name = name() + "_fpg_delbds";
411 : }
412 :
413 : // Pass mesh meta-data defined in subgenerator constructor to this MeshGenerator
414 1400 : if (hasMeshProperty<Real>("pitch_meta", name() + "_2D"))
415 1320 : copyMeshProperty<Real>("pitch_meta", name() + "_2D");
416 1400 : if (hasMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", name() + "_2D"))
417 1320 : copyMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", name() + "_2D");
418 1400 : if (hasMeshProperty<Real>("max_radius_meta", name() + "_2D"))
419 1320 : copyMeshProperty<Real>("max_radius_meta", name() + "_2D");
420 1400 : if (hasMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D"))
421 1320 : copyMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D");
422 1400 : if (hasMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D"))
423 1320 : copyMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D");
424 :
425 700 : if (_is_assembly)
426 236 : declareMeshProperty("pattern_pitch_meta", getReactorParam<Real>(RGMB::assembly_pitch));
427 1164 : else if (hasMeshProperty<Real>("pattern_pitch_meta", name() + "_2D"))
428 1122 : copyMeshProperty<Real>("pattern_pitch_meta", name() + "_2D");
429 700 : declareMeshProperty("is_control_drum_meta", false);
430 :
431 700 : if (_extrude)
432 28 : build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
433 :
434 : // Store final mesh subgenerator
435 700 : _build_mesh = &getMeshByName(build_mesh_name);
436 : }
437 :
438 890 : generateMetadata();
439 890 : }
440 :
441 : void
442 55 : PinMeshGenerator::generateFlexibleAssemblyBoundaries()
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 55 : if (_homogenized || (_intervals.size() == 1))
450 : {
451 80 : 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 45 : outermost_block_name = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R" +
457 15 : std::to_string(_intervals.size() - 1);
458 : has_single_mesh_interval = false;
459 :
460 : // Invoke BlockDeletionGenerator to delete outermost mesh interval of assembly
461 30 : auto params = _app.getFactory().getValidParams("BlockDeletionGenerator");
462 :
463 45 : params.set<std::vector<SubdomainName>>("block") = {outermost_block_name};
464 45 : params.set<MeshGeneratorName>("input") = _homogenized ? name() + "_2D" : name() + "_delbds";
465 :
466 30 : addMeshSubgenerator("BlockDeletionGenerator", name() + "_del_outer", params);
467 15 : }
468 :
469 : {
470 : // Invoke FlexiblePatternGenerator to triangulate deleted mesh interval
471 55 : auto params = _app.getFactory().getValidParams("FlexiblePatternGenerator");
472 :
473 55 : if (has_single_mesh_interval)
474 80 : params.set<std::vector<MeshGeneratorName>>("inputs") = {};
475 : else
476 : {
477 75 : params.set<std::vector<MeshGeneratorName>>("inputs") = {name() + "_del_outer"};
478 15 : params.set<std::vector<libMesh::Point>>("extra_positions") = {libMesh::Point(0, 0, 0)};
479 30 : params.set<std::vector<unsigned int>>("extra_positions_mg_indices") = {0};
480 : }
481 55 : params.set<bool>("use_auto_area_func") = true;
482 55 : params.set<bool>("verify_holes") = false;
483 125 : params.set<MooseEnum>("boundary_type") = (_mesh_geometry == "Hex") ? "HEXAGON" : "CARTESIAN";
484 55 : params.set<unsigned int>("boundary_sectors") =
485 55 : getReactorParam<unsigned int>(RGMB::num_sectors_flexible_stitching);
486 55 : params.set<Real>("boundary_size") = getReactorParam<Real>(RGMB::assembly_pitch);
487 55 : params.set<boundary_id_type>("external_boundary_id") = RGMB::PIN_BOUNDARY_ID_START + _pin_type;
488 110 : params.set<BoundaryName>("external_boundary_name") =
489 55 : RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX + std::to_string(_pin_type);
490 110 : params.set<SubdomainName>("background_subdomain_name") =
491 55 : outermost_block_name + RGMB::TRI_BLOCK_NAME_SUFFIX;
492 55 : params.set<unsigned short>("background_subdomain_id") = RGMB::PIN_BLOCK_ID_TRI_FLEXIBLE;
493 :
494 110 : addMeshSubgenerator("FlexiblePatternGenerator", name() + "_fpg", params);
495 55 : }
496 : {
497 : // Delete extra boundary created by FlexiblePatternGenerator
498 110 : auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
499 :
500 220 : params.set<MeshGeneratorName>("input") = name() + "_fpg";
501 : std::vector<BoundaryName> boundaries_to_delete = {};
502 55 : if (!has_single_mesh_interval)
503 30 : boundaries_to_delete.push_back(std::to_string(1));
504 55 : params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
505 :
506 110 : addMeshSubgenerator("BoundaryDeletionGenerator", name() + "_fpg_delbds", params);
507 55 : }
508 55 : }
509 :
510 : void
511 890 : PinMeshGenerator::generateMetadata()
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{
516 1780 : {_pin_type, _region_ids}};
517 :
518 : // Declare mesh properties that need to be moved up to the assembly level
519 890 : if (_is_assembly)
520 : {
521 140 : declareMeshProperty(RGMB::assembly_type, _pin_type);
522 140 : declareMeshProperty(RGMB::background_block_name, std::vector<std::string>());
523 140 : declareMeshProperty(RGMB::duct_block_names, std::vector<std::vector<std::string>>());
524 140 : declareMeshProperty(RGMB::is_single_pin, _is_assembly);
525 140 : declareMeshProperty(RGMB::is_control_drum, false);
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 140 : 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 118 : pin_region_id_map.insert(
532 118 : std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
533 118 : region_id_map.begin()->first, region_id_map.begin()->second));
534 : declareMeshProperty(RGMB::pin_region_id_map, pin_region_id_map);
535 : std::map<subdomain_id_type, std::vector<std::vector<std::string>>> pin_block_name_map;
536 236 : pin_block_name_map.insert(std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(
537 118 : _pin_type, _block_names));
538 : declareMeshProperty(RGMB::pin_block_name_map, pin_block_name_map);
539 : }
540 : }
541 : // Declare mesh properties that are only relevant to pin meshes
542 : else
543 : {
544 750 : declareMeshProperty(RGMB::pin_type, _pin_type);
545 : declareMeshProperty(RGMB::pin_region_ids, region_id_map);
546 750 : declareMeshProperty(RGMB::pin_block_names, _block_names);
547 : }
548 :
549 : // Set metadata to describe pin attributes
550 890 : declareMeshProperty(RGMB::pitch, _pitch);
551 890 : declareMeshProperty(RGMB::is_homogenized, _homogenized);
552 890 : declareMeshProperty(RGMB::ring_radii, _ring_radii);
553 890 : declareMeshProperty(RGMB::duct_halfpitches, _duct_halfpitch);
554 890 : declareMeshProperty(RGMB::extruded, _extrude);
555 :
556 : unsigned int n_axial_levels =
557 890 : (_mesh_dimensions == 3)
558 : ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
559 1601 : : 1;
560 : std::vector<std::vector<subdomain_id_type>> ring_region_ids(
561 890 : n_axial_levels, std::vector<subdomain_id_type>(_ring_radii.size()));
562 : std::vector<std::vector<subdomain_id_type>> duct_region_ids(
563 890 : n_axial_levels, std::vector<subdomain_id_type>(_duct_halfpitch.size()));
564 890 : std::vector<subdomain_id_type> background_region_ids(n_axial_levels);
565 :
566 2059 : for (const auto axial_idx : make_range(n_axial_levels))
567 : {
568 1956 : for (const auto ring_idx : index_range(_ring_radii))
569 787 : ring_region_ids[axial_idx][ring_idx] = _region_ids[axial_idx][ring_idx];
570 :
571 1169 : background_region_ids[axial_idx] = _region_ids[axial_idx][_ring_radii.size()];
572 :
573 1169 : for (unsigned int duct_idx = _ring_radii.size() + 1;
574 1568 : duct_idx < _duct_halfpitch.size() + _ring_radii.size() + 1;
575 : ++duct_idx)
576 399 : 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
581 : declareMeshProperty(RGMB::ring_region_ids, ring_region_ids);
582 : declareMeshProperty(RGMB::background_region_id, background_region_ids);
583 : declareMeshProperty(RGMB::duct_region_ids, duct_region_ids);
584 1780 : }
585 :
586 : std::unique_ptr<MeshBase>
587 674 : PinMeshGenerator::generate()
588 : {
589 : // Must be called to free the ReactorMeshParams mesh
590 674 : freeReactorParamsMesh();
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 674 : 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 1348 : if (hasMeshProperty<Real>("max_radius_meta", name() + "_2D"))
604 : {
605 634 : const auto max_radius_meta = getMeshProperty<Real>("max_radius_meta", name() + "_2D");
606 1268 : setMeshProperty("max_radius_meta", max_radius_meta);
607 : }
608 1348 : if (hasMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D"))
609 : {
610 : const auto background_intervals_meta =
611 634 : getMeshProperty<unsigned int>("background_intervals_meta", name() + "_2D");
612 1268 : setMeshProperty("background_intervals_meta", background_intervals_meta);
613 : }
614 1348 : if (hasMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D"))
615 : {
616 : const auto node_id_background_meta =
617 634 : getMeshProperty<dof_id_type>("node_id_background_meta", name() + "_2D");
618 1268 : 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 674 : std::string region_id_name = "region_id";
628 674 : std::string pin_type_id_name = "pin_type_id";
629 674 : std::string assembly_type_id_name = "assembly_type_id";
630 674 : std::string plane_id_name = "plane_id";
631 674 : std::string radial_id_name = "radial_id";
632 : const std::string default_block_name =
633 674 : (_is_assembly ? RGMB::ASSEMBLY_BLOCK_NAME_PREFIX : RGMB::PIN_BLOCK_NAME_PREFIX) +
634 1348 : std::to_string(_pin_type);
635 :
636 674 : auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name);
637 674 : auto radial_id_int = getElemIntegerFromMesh(*(*_build_mesh), radial_id_name);
638 674 : 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 674 : if (_extrude)
642 28 : plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
643 674 : if (_is_assembly)
644 236 : 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 674 : 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 55788 : for (auto & elem : (*_build_mesh)->active_element_ptr_range())
654 : {
655 27220 : const auto base_block_id = elem->subdomain_id();
656 27220 : const auto base_block_name = (*_build_mesh)->subdomain_name(base_block_id);
657 :
658 : // Check if block name has correct prefix
659 54440 : std::string prefix = RGMB::PIN_BLOCK_NAME_PREFIX + std::to_string(_pin_type) + "_R";
660 27220 : if (!(base_block_name.find(prefix, 0) == 0))
661 : continue;
662 : // Radial index is integer value of substring after prefix
663 27220 : std::string radial_str = base_block_name.substr(prefix.length());
664 :
665 : // Filter out RGMB::TRI_BLOCK_NAME_SUFFIX if needed
666 27220 : const std::string suffix = RGMB::TRI_BLOCK_NAME_SUFFIX;
667 : const std::size_t found = radial_str.find(suffix);
668 27220 : if (found != std::string::npos)
669 14264 : radial_str.replace(found, suffix.length(), "");
670 27220 : const unsigned int radial_idx = std::stoi(radial_str);
671 :
672 : // Region id is inferred from z_id and radial_idx
673 27220 : dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
674 27220 : const subdomain_id_type elem_region_id = _region_ids[std::size_t(z_id)][radial_idx];
675 :
676 : // Set element integers
677 27220 : elem->set_extra_integer(region_id_int, elem_region_id);
678 27220 : elem->set_extra_integer(pin_type_id_int, _pin_type);
679 27220 : elem->set_extra_integer(radial_id_int, radial_idx);
680 27220 : if (_is_assembly)
681 12098 : elem->set_extra_integer(assembly_type_id_int, _pin_type);
682 :
683 : // Set element block name and block id
684 27220 : auto elem_block_name = default_block_name;
685 27220 : if (_has_block_names)
686 5460 : elem_block_name += "_" + _block_names[std::size_t(z_id)][radial_idx];
687 24490 : else if (getReactorParam<bool>(RGMB::region_id_as_block_name))
688 28904 : elem_block_name += "_REG" + std::to_string(elem_region_id);
689 27220 : if (elem->type() == TRI3 || elem->type() == PRISM6)
690 : elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
691 54440 : updateElementBlockNameId(
692 27220 : *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
693 674 : }
694 :
695 : // Mark mesh as not prepared, as block IDs were re-assigned in this method
696 674 : (*_build_mesh)->unset_is_prepared();
697 :
698 674 : return std::move(*_build_mesh);
699 : }
700 :
701 : std::unique_ptr<CSG::CSGBase>
702 100 : PinMeshGenerator::generateCSG()
703 : {
704 : // Must be called to free the ReactorMeshParams CSGBase object
705 100 : freeReactorParamsCSG();
706 :
707 100 : 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 185 : for (const auto & radius : _ring_radii)
714 : {
715 170 : const auto surf_name = name() + "_radial_ring_" + std::to_string(radial_index);
716 : std::unique_ptr<CSG::CSGSurface> ring_surf_ptr =
717 85 : std::make_unique<CSG::CSGZCylinder>(surf_name, 0, 0, radius);
718 85 : const auto & ring_surf = csg_obj->addSurface(std::move(ring_surf_ptr));
719 170 : surfaces_by_radial_region.push_back({ring_surf});
720 85 : ++radial_index;
721 85 : }
722 :
723 : // Add surfaces corresponding to pin ducts
724 150 : for (const auto & duct_halfpitch : _duct_halfpitch)
725 : {
726 : const auto & duct_surfaces =
727 50 : getOuterRadialSurfacesForUnitCell(radial_index, duct_halfpitch, *csg_obj);
728 50 : surfaces_by_radial_region.push_back(duct_surfaces);
729 50 : ++radial_index;
730 50 : }
731 :
732 : // Add surfaces corresponding to outer pin boundary
733 : const auto & duct_surfaces =
734 100 : getOuterRadialSurfacesForUnitCell(radial_index, _pitch / 2., *csg_obj);
735 100 : surfaces_by_radial_region.push_back(duct_surfaces);
736 :
737 : // Define all radial regions
738 : std::vector<CSG::CSGRegion> radial_regions;
739 100 : CSG::CSGRegion inner_region, outer_region;
740 335 : for (const auto i : index_range(surfaces_by_radial_region))
741 : {
742 : const auto & radial_surfaces = surfaces_by_radial_region[i];
743 235 : CSG::CSGRegion radial_region;
744 235 : bool is_last_radial_region = i == surfaces_by_radial_region.size() - 1;
745 235 : if (inner_region.getRegionType() == CSG::CSGRegion::RegionType::EMPTY)
746 : {
747 100 : if (!is_last_radial_region)
748 : {
749 : // We are in the innermost radial region, the radial region is inner_region
750 65 : inner_region = CSGUtils::getInnerRegion(radial_surfaces, Point(0, 0, 0));
751 65 : 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 135 : outer_region = ~inner_region;
759 135 : inner_region = CSGUtils::getInnerRegion(radial_surfaces, Point(0, 0, 0));
760 135 : radial_region = is_last_radial_region ? outer_region : (inner_region & outer_region);
761 : }
762 235 : radial_regions.push_back(radial_region);
763 235 : }
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 100 : const auto extruded_pin = _mesh_dimensions == 3;
769 100 : if (extruded_pin)
770 : {
771 100 : surfaces_by_axial_region = getAxialPlaneSurfaces(*csg_obj);
772 190 : for (const auto i : make_range(surfaces_by_axial_region.size()))
773 140 : if (i != 0)
774 : {
775 90 : CSG::CSGRegion axial_region;
776 90 : const auto & lower_surf = surfaces_by_axial_region[i - 1].get();
777 90 : if (lower_surf != surfaces_by_axial_region.front())
778 40 : axial_region = +lower_surf;
779 : const auto & upper_surf = surfaces_by_axial_region[i].get();
780 90 : if (upper_surf != surfaces_by_axial_region.back())
781 : {
782 40 : if (axial_region.getRegionType() == CSG::CSGRegion::RegionType::EMPTY)
783 40 : axial_region = -upper_surf;
784 : else
785 0 : axial_region &= -upper_surf;
786 : }
787 90 : axial_regions.push_back(axial_region);
788 90 : }
789 : }
790 :
791 : // Define all cells within pin domain and add to separate universe
792 200 : const auto & pin_univ = csg_obj->createUniverse(name() + "_univ");
793 335 : for (const auto i : index_range(radial_regions))
794 : {
795 680 : for (const auto j : make_range(extruded_pin ? axial_regions.size() : 1))
796 : {
797 330 : auto cell_region = radial_regions[i];
798 660 : auto cell_name = name() + "_cell_radial_" + std::to_string(i);
799 330 : const auto region_id = _region_ids[j][i];
800 330 : const auto mat_name = "rgmb_region_" + std::to_string(region_id);
801 330 : if (extruded_pin)
802 : {
803 : // update name and region with axial info only if extruded
804 210 : const auto axial_region = axial_regions[j];
805 210 : if (axial_region.getRegionType() != CSG::CSGRegion::RegionType::EMPTY)
806 : {
807 190 : if (cell_region.getRegionType() != CSG::CSGRegion::RegionType::EMPTY)
808 160 : cell_region &= axial_region;
809 : else
810 30 : cell_region = axial_region;
811 : }
812 210 : cell_name += "_axial_" + std::to_string(j);
813 210 : }
814 330 : csg_obj->createCell(cell_name, mat_name, cell_region, &pin_univ);
815 330 : }
816 : }
817 :
818 : // Create new cell to bound universe based on pin outer boundaries and add this cell to the root
819 : // universe
820 100 : auto pin_region = CSGUtils::getInnerRegion(surfaces_by_radial_region.back(), Point(0, 0, 0));
821 100 : 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 50 : auto axial_region = +lowest_axial_surf & -highest_axial_surf;
826 50 : pin_region &= axial_region;
827 50 : }
828 100 : csg_obj->createCell(name() + "_root_cell", pin_univ, pin_region);
829 :
830 100 : return csg_obj;
831 100 : }
|