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 "AssemblyMeshGenerator.h"
11 :
12 : #include "ReactorGeometryMeshBuilderBase.h"
13 : #include "MooseApp.h"
14 : #include "Factory.h"
15 : #include "libmesh/elem.h"
16 : #include "MooseMeshUtils.h"
17 : #include "CSGCartesianLattice.h"
18 : #include "CSGHexagonalLattice.h"
19 : #include "CSGUtils.h"
20 :
21 : registerMooseObject("ReactorApp", AssemblyMeshGenerator);
22 :
23 : InputParameters
24 894 : AssemblyMeshGenerator::validParams()
25 : {
26 894 : auto params = ReactorGeometryMeshBuilderBase::validParams();
27 :
28 1788 : params.addRequiredParam<std::vector<MeshGeneratorName>>(
29 : "inputs", "The PinMeshGenerators that form the components of the assembly.");
30 :
31 1788 : params.addRequiredParam<subdomain_id_type>("assembly_type",
32 : "The integer ID for this assembly type definition");
33 :
34 1788 : params.addRequiredParam<std::vector<std::vector<unsigned int>>>(
35 : "pattern",
36 : "A double-indexed array starting with the upper-left corner where the index"
37 : "represents the layout of input pins in the assembly lattice.");
38 :
39 1788 : params.addRangeCheckedParam<std::vector<Real>>(
40 : "duct_halfpitch",
41 : "duct_halfpitch>0.0",
42 : "Distance(s) from center to duct(s) inner boundaries.");
43 :
44 1788 : params.addRangeCheckedParam<unsigned int>("background_intervals",
45 : "background_intervals>0",
46 : "Radial intervals in the assembly peripheral region.");
47 :
48 1788 : params.addRangeCheckedParam<std::vector<unsigned int>>(
49 : "duct_intervals", "duct_intervals>0", "Number of meshing intervals in each enclosing duct.");
50 :
51 1788 : params.addParam<std::vector<subdomain_id_type>>(
52 : "background_region_id",
53 : "The region id for the background area between the pins and the ducts to set region_id "
54 : "extra-element integer");
55 :
56 1788 : params.addParam<std::vector<std::vector<subdomain_id_type>>>(
57 : "duct_region_ids",
58 : "The region id for the ducts from innermost to outermost, to set region_id "
59 : "extra-element integer.");
60 :
61 1788 : params.addParam<std::vector<std::string>>("background_block_name",
62 : "The block names for the assembly background regions");
63 :
64 1788 : params.addParam<std::vector<std::vector<std::string>>>(
65 : "duct_block_names",
66 : "The block names for the assembly duct regions from innermost to outermost");
67 :
68 1788 : params.addParam<bool>("extrude",
69 1788 : false,
70 : "Determines if this is the final step in the geometry construction"
71 : " and extrudes the 2D geometry to 3D. If this is true then this mesh "
72 : "cannot be used in further mesh building in the Reactor workflow");
73 1788 : params.addParamNamesToGroup("background_region_id duct_region_ids assembly_type", "ID assigment");
74 1788 : params.addParamNamesToGroup("background_intervals background_region_id",
75 : "Background specifications");
76 1788 : params.addParamNamesToGroup("duct_intervals duct_region_ids duct_halfpitch",
77 : "Duct specifications");
78 :
79 894 : params.addClassDescription("This AssemblyMeshGenerator object is designed to generate "
80 : "assembly-like structures, with IDs, from a reactor geometry. "
81 : "The assembly-like structures must consist of a full pattern of equal "
82 : "sized pins from PinMeshGenerator. "
83 : "A hexagonal assembly will be placed inside of a bounding hexagon "
84 : "consisting of a background region and, optionally,"
85 : " duct regions.");
86 : // depletion id generation params are added
87 894 : addDepletionIDParams(params);
88 :
89 : // Declare that this generator has a generateCSG method
90 894 : MeshGenerator::setHasGenerateCSG(params);
91 :
92 894 : return params;
93 0 : }
94 :
95 447 : AssemblyMeshGenerator::AssemblyMeshGenerator(const InputParameters & parameters)
96 : : ReactorGeometryMeshBuilderBase(parameters),
97 447 : _inputs(getParam<std::vector<MeshGeneratorName>>("inputs")),
98 894 : _assembly_type(getParam<subdomain_id_type>("assembly_type")),
99 894 : _pattern(getParam<std::vector<std::vector<unsigned int>>>("pattern")),
100 1541 : _duct_sizes(isParamValid("duct_halfpitch") ? getParam<std::vector<Real>>("duct_halfpitch")
101 : : std::vector<Real>()),
102 447 : _background_intervals(
103 1516 : isParamValid("background_intervals") ? getParam<unsigned int>("background_intervals") : 0),
104 1294 : _duct_intervals(isParamValid("duct_intervals")
105 447 : ? getParam<std::vector<unsigned int>>("duct_intervals")
106 : : std::vector<unsigned int>()),
107 1516 : _background_region_id(isParamValid("background_region_id")
108 447 : ? getParam<std::vector<subdomain_id_type>>("background_region_id")
109 : : std::vector<subdomain_id_type>()),
110 1294 : _duct_region_ids(isParamValid("duct_region_ids")
111 447 : ? getParam<std::vector<std::vector<subdomain_id_type>>>("duct_region_ids")
112 : : std::vector<std::vector<subdomain_id_type>>()),
113 1341 : _extrude(getParam<bool>("extrude"))
114 : {
115 : MeshGeneratorName reactor_params =
116 447 : MeshGeneratorName(getMeshProperty<std::string>(RGMB::reactor_params_name, _inputs[0]));
117 : // Check that MG name for reactor params is consistent across all assemblies
118 710 : for (unsigned int i = 1; i < _inputs.size(); i++)
119 263 : if (getMeshProperty<std::string>(RGMB::reactor_params_name, _inputs[i]) != reactor_params)
120 0 : mooseError("The name of all reactor_params objects should be identical across all input pins "
121 : "in the assembly.\n");
122 :
123 : // Initialize ReactorMeshParams object stored in pin input
124 894 : initializeReactorMeshParams(reactor_params);
125 :
126 447 : _geom_type = getReactorParam<std::string>(RGMB::mesh_geometry);
127 447 : _mesh_dimensions = getReactorParam<unsigned int>(RGMB::mesh_dimensions);
128 :
129 447 : if (_extrude && _mesh_dimensions != 3)
130 0 : paramError("extrude",
131 : "In order to extrude this mesh, ReactorMeshParams/dim needs to be set to 3\n");
132 1030 : if (_extrude && (!hasReactorParam<boundary_id_type>(RGMB::top_boundary_id) ||
133 583 : !hasReactorParam<boundary_id_type>(RGMB::bottom_boundary_id)))
134 0 : mooseError("Both top_boundary_id and bottom_boundary_id must be provided in ReactorMeshParams "
135 : "if using extruded geometry");
136 :
137 : Real base_pitch = 0.0;
138 :
139 : // Check constitutent pins do not have shared pin_type ids
140 : std::map<subdomain_id_type, std::string> pin_map_type_to_name;
141 1155 : for (const auto i : index_range(_inputs))
142 : {
143 : auto pin = _inputs[i];
144 710 : if (i == 0)
145 447 : base_pitch = getMeshProperty<Real>(RGMB::pitch, pin);
146 : else
147 : {
148 263 : auto pitch = getMeshProperty<Real>(RGMB::pitch, pin);
149 263 : if (!MooseUtils::absoluteFuzzyEqual(pitch, base_pitch))
150 0 : mooseError("All pins within an assembly must have the same pitch");
151 : }
152 710 : if (getMeshProperty<bool>(RGMB::extruded, pin))
153 0 : mooseError("Pins that have already been extruded cannot be used in AssemblyMeshGenerator "
154 : "definition.\n");
155 710 : const auto pin_type = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pin);
156 712 : if (pin_map_type_to_name.find(pin_type) != pin_map_type_to_name.end() &&
157 2 : pin_map_type_to_name[pin_type] != pin)
158 2 : mooseError("Constituent pins have shared pin_type ids but different names. Each uniquely "
159 : "defined pin in PinMeshGenerator must have its own pin_type id.");
160 708 : pin_map_type_to_name[pin_type] = pin;
161 : }
162 445 : auto assembly_pitch = getReactorParam<Real>(RGMB::assembly_pitch);
163 :
164 : unsigned int n_axial_levels =
165 445 : (_mesh_dimensions == 3)
166 : ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
167 835 : : 1;
168 445 : if (_geom_type == "Square")
169 : {
170 : const auto ny = _pattern.size();
171 : const auto nx = _pattern[0].size();
172 178 : if (_background_region_id.size() == 0)
173 : {
174 134 : if ((!MooseUtils::absoluteFuzzyEqual(base_pitch * ny, assembly_pitch)) ||
175 134 : (!MooseUtils::absoluteFuzzyEqual(base_pitch * nx, assembly_pitch)))
176 0 : mooseError(
177 : "Assembly pitch must be equal to lattice dimension times pin pitch for Cartesian "
178 : "assemblies with no background region");
179 134 : if (_background_intervals > 0)
180 0 : mooseError("\"background_region_id\" must be defined if \"background_intervals\" is "
181 : "greater than 0");
182 : }
183 : else
184 : {
185 44 : if ((base_pitch * ny > assembly_pitch) || (base_pitch * nx > assembly_pitch))
186 0 : mooseError(
187 : "Assembly pitch must be larger than lattice dimension times pin pitch for Cartesian "
188 : "assemblies with background region");
189 44 : if (_background_intervals == 0)
190 0 : mooseError("\"background_intervals\" must be greater than 0 if \"background_region_id\" is "
191 : "defined");
192 44 : if (_background_region_id.size() != n_axial_levels)
193 0 : mooseError(
194 : "The size of background_region_id must be equal to the number of axial levels as "
195 : "defined in the ReactorMeshParams object");
196 : }
197 : }
198 : else
199 : {
200 267 : if ((_background_region_id.size() == 0) || _background_intervals == 0)
201 0 : mooseError("Hexagonal assemblies must have a background region defined");
202 267 : if (assembly_pitch / std::sin(M_PI / 3.0) < _pattern.size() * base_pitch)
203 0 : mooseError("Hexagonal diameter of assembly must be larger than the number of assembly rows "
204 : "times the pin pitch");
205 : // Check size of background region id matches number of axial levels
206 267 : if (_background_region_id.size() != n_axial_levels)
207 0 : mooseError("The size of background_region_id must be equal to the number of axial levels as "
208 : "defined in the ReactorMeshParams object");
209 : }
210 :
211 445 : if (_duct_sizes.size() != _duct_intervals.size())
212 0 : mooseError("If ducts are defined then \"duct_intervals\" and \"duct_region_ids\" must also be "
213 : "defined and of equal size.");
214 :
215 445 : if (_duct_sizes.size() != 0)
216 : {
217 : // Check size of duct region id matches number of axial levels
218 200 : if (_duct_region_ids.size() != n_axial_levels)
219 0 : mooseError("The size of duct_region_id must be equal to the number of axial levels as "
220 : "defined in the ReactorMeshParams object");
221 200 : if (_duct_region_ids[0].size() != _duct_sizes.size())
222 0 : paramError("duct_halfpitch",
223 : "If ducts are defined, then \"duct_intervals\" and \"duct_region_ids\" "
224 : "must also be defined and of equal size.");
225 : }
226 :
227 : // Check whether block names are defined properly
228 890 : if (isParamValid("background_block_name"))
229 : {
230 100 : if (getReactorParam<bool>(RGMB::region_id_as_block_name))
231 2 : paramError("background_block_name",
232 : "If ReactorMeshParams/region_id_as_block_name is set, background_block_name "
233 : "should not be specified in AssemblyMeshGenerator");
234 98 : _has_background_block_name = true;
235 294 : _background_block_name = getParam<std::vector<std::string>>("background_block_name");
236 98 : if (_background_region_id.size() != _background_block_name.size())
237 0 : mooseError("The size of background_block_name must match the size of background_region_id");
238 : }
239 : else
240 345 : _has_background_block_name = false;
241 :
242 886 : if (isParamValid("duct_block_names"))
243 : {
244 49 : if (getReactorParam<bool>(RGMB::region_id_as_block_name))
245 0 : paramError("duct_block_names",
246 : "If ReactorMeshParams/region_id_as_block_name is set, duct_block_names should not "
247 : "be specified in AssemblyMeshGenerator");
248 49 : _has_duct_block_names = true;
249 147 : _duct_block_names = getParam<std::vector<std::vector<std::string>>>("duct_block_names");
250 49 : if (_duct_region_ids.size() != _duct_block_names.size())
251 0 : mooseError("The size of duct_block_names must match the size of duct_region_ids");
252 147 : for (const auto i : index_range(_duct_region_ids))
253 98 : if (_duct_region_ids[i].size() != _duct_block_names[i].size())
254 0 : mooseError("The size of duct_block_names must match the size of duct_region_ids");
255 : }
256 : else
257 394 : _has_duct_block_names = false;
258 :
259 : // No subgenerators will be called if option to bypass mesh generators is enabled
260 443 : if (!getReactorParam<bool>(RGMB::bypass_meshgen))
261 : {
262 : // Declare dependency of inputs to sub generator calls. If mesh generation
263 353 : declareMeshesForSub("inputs");
264 :
265 353 : _assembly_boundary_id = RGMB::ASSEMBLY_BOUNDARY_ID_START + _assembly_type;
266 353 : _assembly_boundary_name = RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX + std::to_string(_assembly_type);
267 :
268 : // Call PatternedHexMeshGenerator or PatternedCartesianMeshGenerator to stitch assembly
269 : {
270 : const auto patterned_mg_name =
271 353 : _geom_type == "Hex" ? "PatternedHexMeshGenerator" : "PatternedCartesianMeshGenerator";
272 353 : auto params = _app.getFactory().getValidParams(patterned_mg_name);
273 :
274 353 : if (_geom_type == "Hex")
275 : {
276 227 : params.set<Real>("hexagon_size") = getReactorParam<Real>(RGMB::assembly_pitch) / 2.0;
277 454 : params.set<MooseEnum>("hexagon_size_style") = "apothem";
278 : }
279 : else
280 : {
281 126 : if (_background_region_id.size() == 0)
282 184 : params.set<MooseEnum>("pattern_boundary") = "none";
283 : else
284 : {
285 68 : params.set<MooseEnum>("pattern_boundary") = "expanded";
286 34 : params.set<Real>("square_size") = getReactorParam<Real>(RGMB::assembly_pitch);
287 34 : params.set<bool>("uniform_mesh_on_sides") = true;
288 : }
289 : }
290 :
291 1059 : params.set<std::vector<std::string>>("id_name") = {"pin_id"};
292 706 : params.set<std::vector<MooseEnum>>("assign_type") = {
293 1412 : MooseEnum("cell", "cell")}; // give elems IDs relative to position in assembly
294 353 : params.set<std::vector<MeshGeneratorName>>("inputs") = _inputs;
295 353 : params.set<std::vector<std::vector<unsigned int>>>("pattern") = _pattern;
296 353 : params.set<bool>("create_outward_interface_boundaries") = false;
297 :
298 353 : if (_background_intervals > 0)
299 : {
300 261 : params.set<unsigned int>("background_intervals") = _background_intervals;
301 : // Initial block id used to define peripheral regions of assembly
302 :
303 : const auto background_block_name =
304 261 : RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R0";
305 : const auto background_block_id = RGMB::ASSEMBLY_BLOCK_ID_START;
306 261 : params.set<subdomain_id_type>("background_block_id") = background_block_id;
307 522 : params.set<SubdomainName>("background_block_name") = background_block_name;
308 : }
309 :
310 353 : if (_duct_sizes.size() > 0)
311 : {
312 : std::vector<subdomain_id_type> duct_block_ids;
313 : std::vector<SubdomainName> duct_block_names;
314 334 : for (const auto duct_it : index_range(_duct_region_ids[0]))
315 : {
316 167 : const auto duct_block_name = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX +
317 334 : std::to_string(_assembly_type) + "_R" +
318 334 : std::to_string(duct_it + 1);
319 167 : const auto duct_block_id = RGMB::ASSEMBLY_BLOCK_ID_START + duct_it + 1;
320 167 : duct_block_ids.push_back(duct_block_id);
321 167 : duct_block_names.push_back(duct_block_name);
322 : }
323 :
324 167 : params.set<std::vector<Real>>("duct_sizes") = _duct_sizes;
325 167 : params.set<std::vector<subdomain_id_type>>("duct_block_ids") = duct_block_ids;
326 167 : params.set<std::vector<SubdomainName>>("duct_block_names") = duct_block_names;
327 167 : params.set<std::vector<unsigned int>>("duct_intervals") = _duct_intervals;
328 167 : }
329 :
330 353 : params.set<boundary_id_type>("external_boundary_id") = _assembly_boundary_id;
331 353 : params.set<BoundaryName>("external_boundary_name") = _assembly_boundary_name;
332 :
333 706 : addMeshSubgenerator(patterned_mg_name, name() + "_pattern", params);
334 :
335 : // Pass mesh meta-data defined in subgenerator constructor to this MeshGenerator
336 706 : copyMeshProperty<bool>("is_control_drum_meta", name() + "_pattern");
337 706 : copyMeshProperty<std::vector<Point>>("control_drum_positions", name() + "_pattern");
338 706 : copyMeshProperty<std::vector<Real>>("control_drum_angles", name() + "_pattern");
339 706 : copyMeshProperty<std::vector<std::vector<Real>>>("control_drums_azimuthal_meta",
340 706 : name() + "_pattern");
341 706 : copyMeshProperty<std::string>("position_file_name", name() + "_pattern");
342 706 : copyMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern");
343 353 : }
344 :
345 353 : std::string build_mesh_name = name() + "_delbds";
346 :
347 : // Remove outer pin sidesets created by PolygonConcentricCircleMeshGenerator
348 : {
349 : // Get outer boundaries of all constituent pins based on pin_type
350 : std::vector<BoundaryName> boundaries_to_delete = {};
351 1311 : for (const auto & pattern_x : _pattern)
352 : {
353 3196 : for (const auto & pattern_idx : pattern_x)
354 : {
355 2238 : const auto pin_name = _inputs[pattern_idx];
356 2238 : const auto pin_id = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pin_name);
357 : const BoundaryName boundary_name =
358 4476 : RGMB::PIN_BOUNDARY_NAME_PREFIX + std::to_string(pin_id);
359 2238 : if (!std::count(boundaries_to_delete.begin(), boundaries_to_delete.end(), boundary_name))
360 548 : boundaries_to_delete.push_back(boundary_name);
361 : }
362 : }
363 706 : auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
364 :
365 1059 : params.set<MeshGeneratorName>("input") = name() + "_pattern";
366 353 : params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
367 :
368 706 : addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
369 353 : }
370 :
371 : // Modify outermost mesh interval to enable flexible assembly stitching
372 353 : const auto use_flexible_stitching = getReactorParam<bool>(RGMB::flexible_assembly_stitching);
373 353 : if (use_flexible_stitching)
374 : {
375 75 : generateFlexibleAssemblyBoundaries();
376 150 : build_mesh_name = name() + "_fpg_delbds";
377 : }
378 :
379 901 : for (auto pinMG : _inputs)
380 : {
381 : std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>> region_id_map =
382 : getMeshProperty<std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>>(
383 548 : RGMB::pin_region_ids, pinMG);
384 1096 : _pin_region_id_map.insert(
385 0 : std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
386 548 : region_id_map.begin()->first, region_id_map.begin()->second));
387 548 : subdomain_id_type pin_type_id = getMeshProperty<subdomain_id_type>(RGMB::pin_type, pinMG);
388 : std::vector<std::vector<std::string>> pin_block_names =
389 548 : getMeshProperty<std::vector<std::vector<std::string>>>(RGMB::pin_block_names, pinMG);
390 1096 : _pin_block_name_map.insert(
391 548 : std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(pin_type_id,
392 : pin_block_names));
393 548 : }
394 :
395 353 : if (_extrude && _mesh_dimensions == 3)
396 98 : build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
397 :
398 : // Store final mesh subgenerator
399 353 : _build_mesh = &getMeshByName(build_mesh_name);
400 : }
401 : // If mesh generation should be bypassed, call getMeshes to resolve MeshGeneratorSystem
402 : // dependencies
403 : else
404 180 : auto input_meshes = getMeshes("inputs");
405 :
406 : // If we are in CSG only mode, store the CSGBase objects associated with input MG's
407 443 : if (_app.getMeshGeneratorSystem().getCSGOnly())
408 60 : _input_csg_bases = getCSGBases("inputs");
409 :
410 443 : generateMetadata();
411 1502 : }
412 :
413 : void
414 443 : AssemblyMeshGenerator::generateMetadata()
415 : {
416 : // Declare metadata for use in downstream mesh generators
417 443 : declareMeshProperty(RGMB::assembly_type, _assembly_type);
418 : declareMeshProperty(RGMB::pitch, getReactorParam<Real>(RGMB::assembly_pitch));
419 443 : declareMeshProperty(RGMB::background_region_id, _background_region_id);
420 443 : declareMeshProperty(RGMB::background_block_name, _background_block_name);
421 443 : declareMeshProperty(RGMB::duct_halfpitches, _duct_sizes);
422 443 : declareMeshProperty(RGMB::duct_region_ids, _duct_region_ids);
423 443 : declareMeshProperty(RGMB::duct_block_names, _duct_block_names);
424 443 : declareMeshProperty(RGMB::is_homogenized, false);
425 443 : declareMeshProperty(RGMB::is_single_pin, false);
426 822 : declareMeshProperty(RGMB::extruded, _extrude && _mesh_dimensions == 3);
427 443 : declareMeshProperty(RGMB::is_control_drum, false);
428 : // Following metadata is only relevant if an output mesh is generated by RGMB
429 443 : if (!getReactorParam<bool>(RGMB::bypass_meshgen))
430 : {
431 353 : declareMeshProperty(RGMB::pin_region_id_map, _pin_region_id_map);
432 353 : declareMeshProperty(RGMB::pin_block_name_map, _pin_block_name_map);
433 : }
434 :
435 : // Determine constituent pin names and define lattice as metadata
436 : std::vector<std::vector<int>> pin_name_lattice;
437 : std::vector<std::string> input_pin_names;
438 1619 : for (const auto i : index_range(_pattern))
439 : {
440 1176 : std::vector<int> pin_name_idx(_pattern[i].size());
441 3888 : for (const auto j : index_range(_pattern[i]))
442 : {
443 2712 : const auto input_pin_name = _inputs[_pattern[i][j]];
444 2712 : const auto it = std::find(input_pin_names.begin(), input_pin_names.end(), input_pin_name);
445 2712 : if (it == input_pin_names.end())
446 : {
447 702 : pin_name_idx[j] = input_pin_names.size();
448 702 : input_pin_names.push_back(input_pin_name);
449 : }
450 : else
451 2010 : pin_name_idx[j] = it - input_pin_names.begin();
452 : }
453 1176 : pin_name_lattice.push_back(pin_name_idx);
454 1176 : }
455 : declareMeshProperty(RGMB::pin_names, input_pin_names);
456 : declareMeshProperty(RGMB::pin_lattice, pin_name_lattice);
457 443 : }
458 :
459 : void
460 75 : AssemblyMeshGenerator::generateFlexibleAssemblyBoundaries()
461 : {
462 : // Assemblies that invoke this method have constituent pin lattice, delete outermost background or
463 : // duct region (if present)
464 : SubdomainName block_to_delete = "";
465 75 : if (_background_region_id.size() == 0)
466 0 : mooseError("Attempting to use flexible stitching on assembly " + name() +
467 : " that does not have a background region. This is not yet supported.");
468 75 : const auto radial_index = _duct_region_ids.size() == 0 ? 0 : _duct_region_ids[0].size();
469 225 : block_to_delete = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R" +
470 75 : std::to_string(radial_index);
471 :
472 : {
473 : // Invoke BlockDeletionGenerator to delete outermost mesh interval of assembly
474 150 : auto params = _app.getFactory().getValidParams("BlockDeletionGenerator");
475 :
476 225 : params.set<std::vector<SubdomainName>>("block") = {block_to_delete};
477 225 : params.set<MeshGeneratorName>("input") = name() + "_delbds";
478 :
479 150 : addMeshSubgenerator("BlockDeletionGenerator", name() + "_del_outer", params);
480 75 : }
481 : {
482 : // Invoke FlexiblePatternGenerator to triangulate deleted mesh region
483 150 : auto params = _app.getFactory().getValidParams("FlexiblePatternGenerator");
484 :
485 375 : params.set<std::vector<MeshGeneratorName>>("inputs") = {name() + "_del_outer"};
486 75 : params.set<std::vector<libMesh::Point>>("extra_positions") = {libMesh::Point(0, 0, 0)};
487 75 : params.set<std::vector<unsigned int>>("extra_positions_mg_indices") = {0};
488 75 : params.set<bool>("use_auto_area_func") = true;
489 170 : params.set<MooseEnum>("boundary_type") = (_geom_type == "Hex") ? "HEXAGON" : "CARTESIAN";
490 75 : params.set<unsigned int>("boundary_sectors") =
491 75 : getReactorParam<unsigned int>(RGMB::num_sectors_flexible_stitching);
492 75 : params.set<Real>("boundary_size") = getReactorParam<Real>(RGMB::assembly_pitch);
493 75 : params.set<boundary_id_type>("external_boundary_id") = _assembly_boundary_id;
494 75 : params.set<BoundaryName>("external_boundary_name") = _assembly_boundary_name;
495 150 : params.set<SubdomainName>("background_subdomain_name") =
496 75 : block_to_delete + RGMB::TRI_BLOCK_NAME_SUFFIX;
497 75 : params.set<bool>("verify_holes") = false;
498 75 : params.set<unsigned short>("background_subdomain_id") = RGMB::ASSEMBLY_BLOCK_ID_TRI_FLEXIBLE;
499 :
500 150 : addMeshSubgenerator("FlexiblePatternGenerator", name() + "_fpg", params);
501 75 : }
502 : {
503 : // Delete extra boundary created by FlexiblePatternGenerator
504 150 : auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
505 :
506 225 : params.set<MeshGeneratorName>("input") = name() + "_fpg";
507 300 : params.set<std::vector<BoundaryName>>("boundary_names") = {std::to_string(1)};
508 :
509 150 : addMeshSubgenerator("BoundaryDeletionGenerator", name() + "_fpg_delbds", params);
510 75 : }
511 75 : }
512 :
513 : std::unique_ptr<MeshBase>
514 341 : AssemblyMeshGenerator::generate()
515 : {
516 : // Must be called to free the ReactorMeshParams mesh
517 341 : freeReactorParamsMesh();
518 :
519 : // If bypass_mesh is true, return a null mesh. In this mode, an output mesh is not
520 : // generated and only metadata is defined on the generator, so logic related to
521 : // generation of output mesh will not be called
522 341 : if (getReactorParam<bool>(RGMB::bypass_meshgen))
523 : {
524 : auto null_mesh = nullptr;
525 : return null_mesh;
526 : }
527 :
528 : // Update metadata at this point since values for these metadata only get set by PCCMG
529 : // at generate() stage
530 682 : if (hasMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern"))
531 : {
532 : const auto pattern_pitch_meta =
533 341 : getMeshProperty<Real>("pattern_pitch_meta", name() + "_pattern");
534 682 : setMeshProperty("pattern_pitch_meta", pattern_pitch_meta);
535 : }
536 :
537 : // This generate() method will be called once the subgenerators that we depend on are
538 : // called. This is where we reassign subdomain ids/name in case they were merged when
539 : // stitching pins into an assembly. This is also where we set region_id and
540 : // assembly_type_id element integers.
541 :
542 : // Define all extra element names and integers
543 341 : std::string plane_id_name = "plane_id";
544 341 : std::string region_id_name = "region_id";
545 341 : std::string pin_type_id_name = "pin_type_id";
546 341 : std::string assembly_type_id_name = "assembly_type_id";
547 341 : std::string radial_id_name = "radial_id";
548 : const std::string default_block_name =
549 341 : RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type);
550 :
551 341 : auto pin_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), pin_type_id_name, true);
552 341 : auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name, true);
553 341 : auto radial_id_int = getElemIntegerFromMesh(*(*_build_mesh), radial_id_name, true);
554 :
555 341 : auto assembly_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), assembly_type_id_name);
556 :
557 : unsigned int plane_id_int = 0;
558 341 : if (_extrude)
559 98 : plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
560 :
561 : // Get next free block ID in mesh in case subdomain ids need to be remapped
562 341 : auto next_block_id = MooseMeshUtils::getNextFreeSubdomainID(*(*(_build_mesh)));
563 : std::map<std::string, SubdomainID> rgmb_name_id_map;
564 :
565 : // Loop through all mesh elements and set region ids and reassign block IDs/names
566 : // if they were merged during pin stitching
567 275054 : for (auto & elem : (*_build_mesh)->active_element_ptr_range())
568 : {
569 137186 : elem->set_extra_integer(assembly_type_id_int, _assembly_type);
570 137186 : const dof_id_type pin_type_id = elem->get_extra_integer(pin_type_id_int);
571 137186 : const dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
572 :
573 : // Element is part of a pin mesh
574 137186 : if (_pin_region_id_map.find(pin_type_id) != _pin_region_id_map.end())
575 : {
576 : // Get region ID from pin_type, z_id, and radial_idx
577 66610 : const dof_id_type radial_idx = elem->get_extra_integer(radial_id_int);
578 66610 : const auto elem_rid = _pin_region_id_map[pin_type_id][z_id][radial_idx];
579 66610 : elem->set_extra_integer(region_id_int, elem_rid);
580 :
581 : // Set element block name and block id
582 66610 : bool has_block_names = !_pin_block_name_map[pin_type_id].empty();
583 66610 : auto elem_block_name = default_block_name;
584 66610 : if (has_block_names)
585 31080 : elem_block_name += "_" + _pin_block_name_map[pin_type_id][z_id][radial_idx];
586 51070 : else if (getReactorParam<bool>(RGMB::region_id_as_block_name))
587 40820 : elem_block_name += "_REG" + std::to_string(elem_rid);
588 66610 : if (elem->type() == TRI3 || elem->type() == PRISM6)
589 : elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
590 133220 : updateElementBlockNameId(
591 66610 : *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
592 : }
593 : else
594 : {
595 : // Assembly peripheral element (background / duct), set subdomains according
596 : // to user preferences and set pin type id to RGMB::MAX_PIN_TYPE_ID - peripheral index
597 : // Region id is inferred from z_id and peripheral_idx
598 70576 : const auto base_block_id = elem->subdomain_id();
599 70576 : const auto base_block_name = (*_build_mesh)->subdomain_name(base_block_id);
600 :
601 : // Check if block name has correct prefix
602 141152 : std::string prefix = RGMB::ASSEMBLY_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R";
603 70576 : if (!(base_block_name.find(prefix, 0) == 0))
604 : continue;
605 : // Peripheral index is integer value of substring after prefix
606 141152 : const unsigned int peripheral_idx = std::stoi(base_block_name.substr(prefix.length()));
607 :
608 : bool is_background_region = peripheral_idx == 0;
609 :
610 70576 : subdomain_id_type pin_type = RGMB::MAX_PIN_TYPE_ID - peripheral_idx;
611 70576 : elem->set_extra_integer(pin_type_id_int, pin_type);
612 :
613 70576 : const auto elem_rid = (is_background_region ? _background_region_id[z_id]
614 21038 : : _duct_region_ids[z_id][peripheral_idx - 1]);
615 70576 : elem->set_extra_integer(region_id_int, elem_rid);
616 :
617 : // Set element block name and block id
618 70576 : auto elem_block_name = default_block_name;
619 70576 : if (getReactorParam<bool>(RGMB::region_id_as_block_name))
620 117576 : elem_block_name += "_REG" + std::to_string(elem_rid);
621 11788 : else if (is_background_region && _has_background_block_name)
622 6552 : elem_block_name += "_" + _background_block_name[z_id];
623 4648 : else if (!is_background_region && _has_duct_block_names)
624 3024 : elem_block_name += "_" + _duct_block_names[z_id][peripheral_idx - 1];
625 70576 : if (elem->type() == TRI3 || elem->type() == PRISM6)
626 : elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
627 141152 : updateElementBlockNameId(
628 70576 : *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
629 : }
630 341 : }
631 :
632 682 : if (getParam<bool>("generate_depletion_id"))
633 : {
634 28 : const MooseEnum option = getParam<MooseEnum>("depletion_id_type");
635 14 : addDepletionId(*(*_build_mesh), option, DepletionIDGenerationLevel::Assembly, _extrude);
636 14 : }
637 :
638 : // Mark mesh as not prepared, as block IDs were re-assigned in this method
639 341 : (*_build_mesh)->unset_is_prepared();
640 :
641 341 : return std::move(*_build_mesh);
642 : }
643 :
644 : std::unique_ptr<CSG::CSGBase>
645 30 : AssemblyMeshGenerator::generateCSG()
646 : {
647 : // Must be called to free the ReactorMeshParams CSGBase object
648 30 : freeReactorParamsCSG();
649 :
650 30 : auto csg_obj = std::make_unique<CSG::CSGBase>();
651 :
652 : // Combine all bases from PinMG inputs into this base. Root universes from
653 : // inputs are renamed to a new universe name. These universes and their
654 : // cells will be discarded, so that only the infinite pin universe is retained
655 : std::unordered_map<unsigned int, std::string> univ_id_names;
656 : std::vector<std::string> univs_to_discard;
657 85 : for (const auto i : index_range(_inputs))
658 : {
659 55 : const auto input_univ_name_discard = _inputs[i] + "_root_univ";
660 55 : const auto input_univ_name = _inputs[i] + "_univ";
661 55 : csg_obj->joinOtherBase(std::move(*_input_csg_bases[i]), true, input_univ_name_discard);
662 55 : univs_to_discard.push_back(input_univ_name_discard);
663 110 : univ_id_names[i] = input_univ_name;
664 : }
665 :
666 : // Discard root universes of the input pins and their cells
667 85 : for (const auto & univ_name : univs_to_discard)
668 : {
669 : const auto & universe_to_delete = csg_obj->getUniverseByName(univ_name);
670 55 : const auto cells_to_delete = universe_to_delete.getAllCells();
671 55 : csg_obj->deleteUniverse(universe_to_delete);
672 110 : for (const auto & cell : cells_to_delete)
673 55 : csg_obj->deleteCell(cell.get());
674 55 : }
675 :
676 : // Build the universe pattern for the assembly lattice from the input pattern
677 : std::vector<std::vector<std::reference_wrapper<const CSG::CSGUniverse>>> universe_pattern;
678 100 : for (const auto & row : _pattern)
679 : {
680 : std::vector<std::reference_wrapper<const CSG::CSGUniverse>> universe_row;
681 220 : for (const auto & univ_id : row)
682 : {
683 : const auto & lattice_univ = csg_obj->getUniverseByName(univ_id_names[univ_id]);
684 150 : universe_row.push_back(lattice_univ);
685 : }
686 70 : universe_pattern.push_back(universe_row);
687 70 : }
688 :
689 : // Define axial boundaries for problem
690 : std::vector<std::reference_wrapper<const CSG::CSGSurface>> surfaces_by_axial_region;
691 30 : CSG::CSGRegion axial_extent;
692 30 : const auto extruded_assembly = _mesh_dimensions == 3;
693 30 : if (extruded_assembly)
694 : {
695 30 : surfaces_by_axial_region = getAxialPlaneSurfaces(*csg_obj);
696 : const auto & lowest_axial_surf = surfaces_by_axial_region.front().get();
697 : const auto & highest_axial_surf = surfaces_by_axial_region.back().get();
698 15 : axial_extent = +lowest_axial_surf & -highest_axial_surf;
699 : }
700 :
701 : // Define all duct boundaries and create the appropriate cell to fill each duct region.
702 : // Add these cells to a separate universe
703 30 : std::vector<Real> duct_boundaries = _duct_sizes;
704 30 : duct_boundaries.push_back(getReactorParam<Real>(RGMB::assembly_pitch) / 2.);
705 30 : CSG::CSGRegion inner_region;
706 60 : const auto & assembly_univ = csg_obj->createUniverse(name() + "_univ");
707 70 : for (const auto i : index_range(duct_boundaries))
708 : {
709 40 : bool is_last_radial_region = i == duct_boundaries.size() - 1;
710 40 : if (i == 0)
711 : {
712 : // For innermost duct region, we create a lattice cell as the fill
713 30 : const auto pin_pitch = getMeshProperty<Real>(RGMB::pitch, _inputs[0]);
714 30 : auto & assembly_lattice = createAssemblyLattice(pin_pitch, universe_pattern, *csg_obj);
715 30 : setAssemblyLatticeOuter(assembly_lattice, surfaces_by_axial_region, *csg_obj);
716 :
717 : // Define lattice cell
718 30 : std::string lat_cell_name = name() + "_lattice_cell";
719 30 : if (!is_last_radial_region)
720 : {
721 : const auto & duct_surfaces =
722 10 : getOuterRadialSurfacesForUnitCell(i, duct_boundaries[i], *csg_obj);
723 10 : inner_region = CSGUtils::getInnerRegion(duct_surfaces, Point(0, 0, 0));
724 10 : }
725 30 : if (_geom_type == "Hex")
726 : {
727 : // For hex lattices, apply a 90 degree rotation to the lattice to match orientation
728 : // of FEM mesh
729 10 : csg_obj->applyAxisRotation(assembly_lattice, CSG::RotationAxisType::Z, 90.);
730 : }
731 30 : csg_obj->createCell(lat_cell_name, assembly_lattice, inner_region, &assembly_univ);
732 : }
733 : else
734 : {
735 : // Update ducted region
736 10 : CSG::CSGRegion radial_region = ~inner_region;
737 10 : if (!is_last_radial_region)
738 : {
739 : const auto & duct_surfaces =
740 0 : getOuterRadialSurfacesForUnitCell(i, duct_boundaries[i], *csg_obj);
741 0 : inner_region = CSGUtils::getInnerRegion(duct_surfaces, Point(0, 0, 0));
742 0 : radial_region &= inner_region;
743 0 : }
744 :
745 : // Define cell fill of lattice
746 20 : std::string duct_cell_name = name() + "_duct_cell_radial_" + std::to_string(i - 1);
747 10 : if (!extruded_assembly)
748 : {
749 : // In 2D, the cell fill will be a material
750 10 : std::string region_name = "rgmb_region_" + std::to_string(_duct_region_ids[0][i - 1]);
751 5 : csg_obj->createCell(duct_cell_name, region_name, radial_region, &assembly_univ);
752 : }
753 : else
754 : {
755 : // In 3D, the cell fill will be a universe
756 10 : const auto & name_prefix = name() + "_duct_radial_" + std::to_string(i - 1);
757 : std::vector<subdomain_id_type> duct_region_ids;
758 15 : for (const auto j : make_range(_duct_region_ids.size()))
759 10 : duct_region_ids.push_back(_duct_region_ids[j][i - 1]);
760 5 : auto & fill_univ = createDuctFillUniverse(
761 : name_prefix, surfaces_by_axial_region, duct_region_ids, *csg_obj);
762 5 : csg_obj->createCell(duct_cell_name, fill_univ, radial_region, &assembly_univ);
763 5 : }
764 10 : }
765 : }
766 :
767 : // Create new cell to bound universe based on assembly outer boundaries, and add this cell
768 : // to the root universe
769 : const auto & duct_surfaces = getOuterRadialSurfacesForUnitCell(
770 30 : duct_boundaries.size() - 1, duct_boundaries.back(), *csg_obj);
771 30 : auto assembly_region = CSGUtils::getInnerRegion(duct_surfaces, Point(0, 0, 0));
772 30 : if (extruded_assembly)
773 15 : assembly_region &= axial_extent;
774 30 : csg_obj->createCell(name() + "_root_cell", assembly_univ, assembly_region);
775 :
776 30 : return csg_obj;
777 30 : }
778 :
779 : const CSG::CSGLattice &
780 30 : AssemblyMeshGenerator::createAssemblyLattice(
781 : const Real pitch,
782 : const std::vector<std::vector<std::reference_wrapper<const CSG::CSGUniverse>>> pattern,
783 : CSG::CSGBase & csg_obj)
784 : {
785 : // Create lattice based on whether it is hexagonal or Cartesian
786 30 : std::string lat_name = name() + "_lattice";
787 30 : std::unique_ptr<CSG::CSGLattice> lat_ptr;
788 30 : if (_geom_type == "Square")
789 20 : lat_ptr = std::make_unique<CSG::CSGCartesianLattice>(lat_name, pitch, pattern);
790 : else // _geom_type == "Hex"
791 10 : lat_ptr = std::make_unique<CSG::CSGHexagonalLattice>(lat_name, pitch, pattern);
792 :
793 30 : auto & assembly_lattice = csg_obj.addLattice(std::move(lat_ptr));
794 30 : return assembly_lattice;
795 30 : }
796 :
797 : void
798 30 : AssemblyMeshGenerator::setAssemblyLatticeOuter(
799 : const CSG::CSGLattice & assembly_lattice,
800 : const std::vector<std::reference_wrapper<const CSG::CSGSurface>> & surfaces_by_axial_region,
801 : CSG::CSGBase & csg_obj)
802 : {
803 30 : if (_background_region_id.size() == 0)
804 : // Cartesian lattices may not have a background region. In this case, the outer is left as void
805 : return;
806 :
807 : // Define outer fill of lattice
808 20 : if (_mesh_dimensions == 2)
809 : {
810 : // In 2D, the outer fill will be a material fill
811 10 : std::string region_name = "rgmb_region_" + std::to_string(_background_region_id[0]);
812 10 : csg_obj.setLatticeOuter(assembly_lattice, region_name);
813 : }
814 : else // _mesh_dimension == 3
815 : {
816 : // In 3D, we define the outer fill as a universe
817 10 : auto & outer_univ = createDuctFillUniverse(
818 10 : name() + "_lattice_outer", surfaces_by_axial_region, _background_region_id, csg_obj);
819 10 : csg_obj.setLatticeOuter(assembly_lattice, outer_univ);
820 : }
821 : }
822 :
823 : const CSG::CSGUniverse &
824 15 : AssemblyMeshGenerator::createDuctFillUniverse(
825 : const std::string & name_prefix,
826 : const std::vector<std::reference_wrapper<const CSG::CSGSurface>> & surfaces_by_axial_region,
827 : const std::vector<subdomain_id_type> & region_ids,
828 : CSG::CSGBase & csg_obj)
829 : {
830 : mooseAssert(surfaces_by_axial_region.size() - region_ids.size() == 1,
831 : "Incorrect length of axial data vectors");
832 30 : auto & fill_univ = csg_obj.createUniverse(name_prefix + "_univ");
833 45 : for (const auto i : make_range(surfaces_by_axial_region.size() - 1))
834 : {
835 30 : CSG::CSGRegion axial_region;
836 : const auto & lower_surf = surfaces_by_axial_region[i].get();
837 30 : if (lower_surf != surfaces_by_axial_region.front())
838 15 : axial_region = +lower_surf;
839 30 : const auto & upper_surf = surfaces_by_axial_region[i + 1].get();
840 30 : if (upper_surf != surfaces_by_axial_region.back())
841 : {
842 15 : if (axial_region.getRegionType() == CSG::CSGRegion::RegionType::EMPTY)
843 15 : axial_region = -upper_surf;
844 : else
845 0 : axial_region &= -upper_surf;
846 : }
847 60 : auto cell_name = name_prefix + "_axial_" + std::to_string(i);
848 30 : const auto mat_name = "rgmb_region_" + std::to_string(region_ids[i]);
849 30 : csg_obj.createCell(cell_name, mat_name, axial_region, &fill_univ);
850 30 : }
851 :
852 15 : return fill_univ;
853 : }
|