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 "CoreMeshGenerator.h"
11 :
12 : #include "MooseApp.h"
13 : #include "MooseMeshUtils.h"
14 : #include "Factory.h"
15 : #include "libmesh/elem.h"
16 :
17 : registerMooseObject("ReactorApp", CoreMeshGenerator);
18 :
19 : InputParameters
20 524 : CoreMeshGenerator::validParams()
21 : {
22 524 : auto params = ReactorGeometryMeshBuilderBase::validParams();
23 :
24 1048 : params.addRequiredParam<std::vector<MeshGeneratorName>>(
25 : "inputs",
26 : "The AssemblyMeshGenerator and ControlDrumMeshGenerator objects that form the components of "
27 : "the assembly.");
28 :
29 1048 : params.addParam<std::string>(
30 : "dummy_assembly_name",
31 : "dummy",
32 : "The place holder name in \"inputs\" that indicates an empty position.");
33 :
34 1048 : 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 assemblies in the core lattice.");
38 1048 : params.addParam<bool>(
39 1048 : "mesh_periphery", false, "Determines if the core periphery should be meshed.");
40 1048 : MooseEnum periphery_mesher("triangle quad_ring", "triangle");
41 1048 : params.addParam<MooseEnum>("periphery_generator",
42 : periphery_mesher,
43 : "The meshgenerator to use when meshing the core boundary.");
44 :
45 : // Periphery meshing interface
46 1048 : params.addRangeCheckedParam<Real>(
47 : "outer_circle_radius", 0, "outer_circle_radius>=0", "Radius of outer circle boundary.");
48 1572 : params.addRangeCheckedParam<unsigned int>(
49 : "outer_circle_num_segments",
50 1048 : 0,
51 : "outer_circle_num_segments>=0",
52 : "Number of radial segments to subdivide outer circle boundary.");
53 1572 : params.addRangeCheckedParam<unsigned int>(
54 : "periphery_num_layers",
55 1048 : 1,
56 : "periphery_num_layers>0",
57 : "Number of layers to subdivide the periphery boundary.");
58 1048 : params.addParam<std::string>(
59 : "periphery_block_name", RGMB::CORE_BLOCK_NAME_PREFIX, "Block name for periphery zone.");
60 1048 : params.addParam<subdomain_id_type>(
61 : "periphery_region_id",
62 1048 : -1,
63 : "ID for periphery zone for assignment of region_id extra element id.");
64 1048 : params.addRangeCheckedParam<Real>(
65 : "desired_area",
66 : 0,
67 : "desired_area>=0",
68 : "Desired (maximum) triangle area, or 0 to skip uniform refinement");
69 1048 : params.addParam<std::string>(
70 : "desired_area_func",
71 524 : std::string(),
72 : "Desired (local) triangle area as a function of x,y; omit to skip non-uniform refinement");
73 1048 : params.addParam<bool>("assign_control_drum_id",
74 1048 : true,
75 : "Whether control drum id is assigned to the mesh as an extra integer.");
76 1048 : params.addParamNamesToGroup("periphery_block_name periphery_region_id outer_circle_radius "
77 : "mesh_periphery periphery_generator",
78 : "Periphery Meshing");
79 1048 : params.addParamNamesToGroup("outer_circle_num_segments desired_area desired_area_func",
80 : "Periphery Meshing: PTMG specific");
81 1048 : params.addParamNamesToGroup("periphery_num_layers", "Periphery Meshing: PRMG specific");
82 : // end meshing interface
83 :
84 1048 : params.addParam<bool>("extrude",
85 1048 : false,
86 : "Determines if this is the final step in the geometry construction"
87 : " and extrudes the 2D geometry to 3D. If this is true then this mesh "
88 : "cannot be used in further mesh building in the Reactor workflow");
89 :
90 524 : params.addClassDescription(
91 : "This CoreMeshGenerator object is designed to generate a core-like "
92 : "structure, with IDs, from a reactor geometry. "
93 : "The core-like structure consists of a pattern of assembly-like "
94 : "structures generated with AssemblyMeshGenerator and/or ControlDrumMeshGenerator "
95 : "and is permitted to have \"empty\" locations. The size and spacing "
96 : "of the assembly-like structures is defined, and "
97 : "enforced by declaration in the ReactorMeshParams.");
98 : // depletion id generation params are added
99 524 : addDepletionIDParams(params);
100 :
101 524 : return params;
102 524 : }
103 :
104 266 : CoreMeshGenerator::CoreMeshGenerator(const InputParameters & parameters)
105 : : ReactorGeometryMeshBuilderBase(parameters),
106 266 : _inputs(getParam<std::vector<MeshGeneratorName>>("inputs")),
107 266 : _empty_key(getParam<std::string>("dummy_assembly_name")),
108 266 : _pattern(getParam<std::vector<std::vector<unsigned int>>>("pattern")),
109 532 : _extrude(getParam<bool>("extrude")),
110 532 : _mesh_periphery(getParam<bool>("mesh_periphery")),
111 532 : _periphery_meshgenerator(getParam<MooseEnum>("periphery_generator")),
112 532 : _periphery_region_id(getParam<subdomain_id_type>("periphery_region_id")),
113 532 : _outer_circle_radius(getParam<Real>("outer_circle_radius")),
114 532 : _outer_circle_num_segments(getParam<unsigned int>("outer_circle_num_segments")),
115 532 : _periphery_block_name(getParam<std::string>("periphery_block_name")),
116 532 : _periphery_num_layers(getParam<unsigned int>("periphery_num_layers")),
117 532 : _desired_area(getParam<Real>("desired_area")),
118 1330 : _desired_area_func(getParam<std::string>("desired_area_func"))
119 : {
120 : // This sets it so that any mesh that is input with the name _empty_key is considered a "null"
121 : // mesh, that is, whenever we try to get it with the standard getMesh() API we get a nullptr
122 : // mesh instead. In the specific case of the CoreMeshGenerator, we use said "null" mesh to
123 : // represent an empty position
124 266 : declareNullMeshName(_empty_key);
125 :
126 : // periphery meshing input checking
127 266 : if (_mesh_periphery)
128 : {
129 : // missing required input
130 112 : if (!parameters.isParamSetByUser("outer_circle_radius"))
131 : {
132 0 : paramError("outer_circle_radius",
133 : "Outer circle radius must be specified when using periphery meshing.");
134 : }
135 112 : if (!parameters.isParamSetByUser("periphery_region_id"))
136 : {
137 0 : paramError("periphery_region_id",
138 : "Periphery region id must be specified when using periphery meshing.");
139 : }
140 : // using PTMG-specific options with PRMG
141 56 : if (_periphery_meshgenerator == "quad_ring")
142 : {
143 90 : if (parameters.isParamSetByUser("outer_circle_num_segments"))
144 : {
145 0 : paramError("outer_circle_num_segments",
146 : "outer_circle_num_segments cannot be used with PRMG periphery mesher.");
147 : }
148 90 : if (parameters.isParamSetByUser("extra_circle_radii"))
149 : {
150 0 : paramError("extra_circle_radii",
151 : "extra_circle_radii cannot be used with PRMG periphery mesher.");
152 : }
153 90 : if (parameters.isParamSetByUser("extra_circle_num_segments"))
154 : {
155 0 : paramError("extra_circle_num_segments",
156 : "extra_circle_num_segments cannot be used with PRMG periphery mesher.");
157 : }
158 : }
159 : // using PRMG-specific options with PTMG
160 11 : else if (_periphery_meshgenerator == "triangle")
161 : {
162 22 : if (parameters.isParamSetByUser("periphery_num_layers"))
163 : {
164 0 : paramError("periphery_num_layers",
165 : "periphery_num_layers cannot be used with PTMG periphery mesher.");
166 : }
167 : }
168 : else
169 0 : paramError("periphery_generator",
170 : "Provided periphery meshgenerator has not been implemented.");
171 : }
172 :
173 : MeshGeneratorName first_nondummy_assembly = "";
174 : MeshGeneratorName reactor_params = "";
175 : bool assembly_homogenization = false;
176 : bool pin_as_assembly = false;
177 : std::map<subdomain_id_type, std::string> global_pin_map_type_to_name;
178 : std::map<subdomain_id_type, std::string> assembly_map_type_to_name;
179 : // Check that MG name for reactor params and assembly homogenization schemes are
180 : // consistent across all assemblies, and there is no overlap in pin_type / assembly_type ids
181 1068 : for (const auto i : index_range(_inputs))
182 : {
183 : // Skip if assembly name is equal to dummy assembly name
184 806 : if (_inputs[i] == _empty_key)
185 226 : continue;
186 :
187 : // Save properties of first non-dummy assembly to compare to other assemblies
188 580 : if (first_nondummy_assembly == "")
189 : {
190 264 : first_nondummy_assembly = MeshGeneratorName(_inputs[i]);
191 : reactor_params =
192 528 : MeshGeneratorName(getMeshProperty<std::string>(RGMB::reactor_params_name, _inputs[i]));
193 264 : assembly_homogenization = getMeshProperty<bool>(RGMB::is_homogenized, _inputs[i]);
194 264 : pin_as_assembly = getMeshProperty<bool>(RGMB::is_single_pin, _inputs[i]);
195 : }
196 580 : if (getMeshProperty<std::string>(RGMB::reactor_params_name, _inputs[i]) != reactor_params)
197 0 : mooseError("The name of all reactor_params objects should be identical across all pins in "
198 : "the input assemblies.\n");
199 580 : if ((getMeshProperty<bool>(RGMB::is_homogenized, _inputs[i]) != assembly_homogenization) &&
200 39 : !getMeshProperty<bool>(RGMB::flexible_assembly_stitching, reactor_params))
201 0 : mooseError("In order to stitch heterogeneous assemblies with homogeneous assemblies in "
202 : "CoreMeshGenerator, ReactorMeshParams/flexible_assembly_stitching should be set "
203 : "to true\n");
204 :
205 : // Check assembly_types across constituent assemblies are uniquely defined
206 580 : const auto assembly_type = getMeshProperty<subdomain_id_type>(RGMB::assembly_type, _inputs[i]);
207 582 : if (assembly_map_type_to_name.find(assembly_type) != assembly_map_type_to_name.end() &&
208 2 : assembly_map_type_to_name[assembly_type] != _inputs[i])
209 2 : mooseError(
210 : "Constituent assemblies have shared assembly_type ids but different names. Each uniquely "
211 : "defined assembly in AssemblyMeshGenerator must have its own assembly_type id.");
212 578 : assembly_map_type_to_name[assembly_type] = _inputs[i];
213 :
214 : // If assembly is composed of pins, check pin_types across all constituent assemblies are
215 : // uniquely defined
216 578 : if (hasMeshProperty<std::vector<std::string>>(RGMB::pin_names, _inputs[i]))
217 : {
218 426 : const auto pin_names = getMeshProperty<std::vector<std::string>>(RGMB::pin_names, _inputs[i]);
219 1078 : for (const auto & input_pin_name : pin_names)
220 : {
221 654 : const auto pin_type = getMeshProperty<subdomain_id_type>(RGMB::pin_type, input_pin_name);
222 739 : if (global_pin_map_type_to_name.find(pin_type) != global_pin_map_type_to_name.end() &&
223 85 : global_pin_map_type_to_name[pin_type] != input_pin_name)
224 2 : mooseError(
225 : "Constituent pins within assemblies have shared pin_type ids but different names. "
226 : "Each uniquely defined pin in AssemblyMeshGenerator must have its own pin_type id.");
227 652 : global_pin_map_type_to_name[pin_type] = input_pin_name;
228 : }
229 424 : }
230 : }
231 :
232 : // Check that there is at least one non-dummy assemby defined in lattice
233 262 : if (first_nondummy_assembly == "")
234 2 : paramError("inputs", "At least one non-dummy assembly must be defined in input assembly names");
235 :
236 : // Initialize ReactorMeshParams object stored in pin input
237 520 : initializeReactorMeshParams(reactor_params);
238 :
239 260 : _geom_type = getReactorParam<std::string>(RGMB::mesh_geometry);
240 260 : _mesh_dimensions = getReactorParam<unsigned int>(RGMB::mesh_dimensions);
241 :
242 260 : if (_extrude && _mesh_dimensions != 3)
243 0 : paramError("extrude",
244 : "In order to extrude this mesh, ReactorMeshParams/dim needs to be set to 3\n");
245 892 : if (_extrude && (!hasReactorParam<boundary_id_type>(RGMB::top_boundary_id) ||
246 632 : !hasReactorParam<boundary_id_type>(RGMB::bottom_boundary_id)))
247 0 : mooseError("Both top_boundary_id and bottom_boundary_id must be provided in ReactorMeshParams "
248 : "if using extruded geometry");
249 520 : if (!hasReactorParam<boundary_id_type>(RGMB::radial_boundary_id))
250 0 : mooseError("radial_boundary_id must be provided in ReactorMeshParams for CoreMeshGenerators");
251 :
252 520 : if (parameters.isParamSetByUser("periphery_block_name") &&
253 11 : getReactorParam<bool>(RGMB::region_id_as_block_name))
254 2 : paramError("periphery_block_name",
255 : "If ReactorMeshParams/region_id_as_block_name is set, periphery_block_name should "
256 : "not be specified in CoreMeshGenerator");
257 :
258 : std::size_t empty_pattern_loc = 0;
259 : bool make_empty = false;
260 1048 : for (auto assembly : _inputs)
261 : {
262 790 : if (assembly != _empty_key)
263 : {
264 568 : ++empty_pattern_loc;
265 568 : if (getMeshProperty<bool>(RGMB::extruded, assembly))
266 0 : mooseError("Assemblies that have already been extruded cannot be used in CoreMeshGenerator "
267 : "definition.\n");
268 : }
269 : else
270 : {
271 : // Found dummy assembly in input assembly names
272 : make_empty = true;
273 805 : for (const auto i : index_range(_pattern))
274 : {
275 1908 : for (const auto j : index_range(_pattern[i]))
276 : {
277 : // Found dummy assembly in input lattice definition
278 1325 : if (_pattern[i][j] == empty_pattern_loc)
279 352 : _empty_pos = true;
280 : }
281 : }
282 : }
283 : }
284 :
285 : // No subgenerators will be called if option to bypass mesh generators is enabled
286 258 : if (!getReactorParam<bool>(RGMB::bypass_meshgen))
287 : {
288 : // Check whether flexible stitching should be used for constituent assemblies and throw a
289 : // warning if flexible stitching option is not enabled
290 382 : if (!getReactorParam<bool>(RGMB::flexible_assembly_stitching) &&
291 171 : constituentAssembliesNeedFlexibleStiching())
292 0 : mooseWarning("Constituent assemblies do not share the same number of nodes at the outer "
293 : "boundary. In order to ensure that output mesh does not having hanging nodes, a "
294 : "flexible stitching approach should be used by setting "
295 : "ReactorMeshParams/flexible_assembly_stitching = true.");
296 :
297 : // Declare that all of the meshes in the "inputs" parameter are to be used by
298 : // a sub mesh generator.
299 211 : declareMeshesForSub("inputs");
300 :
301 : // Stitch assemblies into a hexagonal / Cartesian core lattice
302 : {
303 : // create a dummy assembly that is a renamed version of one of the inputs
304 211 : if (make_empty)
305 : {
306 : {
307 184 : if (assembly_homogenization)
308 : {
309 18 : auto params = _app.getFactory().getValidParams("SimpleHexagonGenerator");
310 :
311 9 : params.set<Real>("hexagon_size") = getReactorParam<Real>(RGMB::assembly_pitch) / 2.0;
312 9 : params.set<std::vector<subdomain_id_type>>("block_id") = {
313 18 : RGMB::DUMMY_ASSEMBLY_BLOCK_ID};
314 :
315 18 : addMeshSubgenerator("SimpleHexagonGenerator", std::string(_empty_key), params);
316 9 : }
317 : else
318 : {
319 : const auto adaptive_mg_name =
320 175 : _geom_type == "Hex" ? "HexagonConcentricCircleAdaptiveBoundaryMeshGenerator"
321 : : "CartesianConcentricCircleAdaptiveBoundaryMeshGenerator";
322 350 : auto params = _app.getFactory().getValidParams(adaptive_mg_name);
323 :
324 175 : const auto assembly_pitch = getReactorParam<Real>(RGMB::assembly_pitch);
325 175 : if (_geom_type == "Hex")
326 : {
327 102 : params.set<Real>("hexagon_size") = assembly_pitch / 2.0;
328 102 : params.set<std::vector<unsigned int>>("num_sectors_per_side") =
329 204 : std::vector<unsigned int>(6, 2);
330 : }
331 : else
332 : {
333 73 : params.set<Real>("square_size") = assembly_pitch;
334 73 : params.set<std::vector<unsigned int>>("num_sectors_per_side") =
335 146 : std::vector<unsigned int>(4, 2);
336 : }
337 350 : params.set<std::vector<unsigned int>>("sides_to_adapt") = std::vector<unsigned int>{0};
338 175 : params.set<std::vector<MeshGeneratorName>>("meshes_to_adapt_to") =
339 525 : std::vector<MeshGeneratorName>{first_nondummy_assembly};
340 175 : params.set<std::vector<subdomain_id_type>>("background_block_ids") =
341 350 : std::vector<subdomain_id_type>{RGMB::DUMMY_ASSEMBLY_BLOCK_ID};
342 :
343 350 : addMeshSubgenerator(adaptive_mg_name, std::string(_empty_key), params);
344 175 : }
345 : }
346 : }
347 : {
348 : const auto patterned_mg_name =
349 211 : _geom_type == "Hex" ? "PatternedHexMeshGenerator" : "PatternedCartesianMeshGenerator";
350 211 : auto params = _app.getFactory().getValidParams(patterned_mg_name);
351 :
352 633 : params.set<std::vector<std::string>>("id_name") = {"assembly_id"};
353 422 : params.set<std::vector<MooseEnum>>("assign_type") = {
354 844 : MooseEnum("cell", "cell")}; // give elems IDs relative to position in assembly
355 211 : params.set<std::vector<MeshGeneratorName>>("inputs") = _inputs;
356 211 : params.set<std::vector<std::vector<unsigned int>>>("pattern") = _pattern;
357 422 : params.set<MooseEnum>("pattern_boundary") = "none";
358 211 : params.set<bool>("generate_core_metadata") = !pin_as_assembly;
359 211 : params.set<bool>("create_outward_interface_boundaries") = false;
360 422 : params.set<bool>("assign_control_drum_id") = getParam<bool>("assign_control_drum_id");
361 211 : if (make_empty)
362 : {
363 184 : params.set<std::vector<MeshGeneratorName>>("exclude_id") =
364 552 : std::vector<MeshGeneratorName>{_empty_key};
365 : }
366 :
367 211 : const auto radial_boundary = getReactorParam<boundary_id_type>(RGMB::radial_boundary_id);
368 211 : params.set<boundary_id_type>("external_boundary_id") = radial_boundary;
369 211 : params.set<BoundaryName>("external_boundary_name") = RGMB::CORE_BOUNDARY_NAME;
370 211 : params.set<double>("rotate_angle") = 0.0;
371 211 : params.set<bool>("allow_unused_inputs") = true;
372 :
373 422 : addMeshSubgenerator(patterned_mg_name, name() + "_pattern", params);
374 211 : }
375 : }
376 211 : if (_empty_pos)
377 : {
378 121 : auto params = _app.getFactory().getValidParams("BlockDeletionGenerator");
379 :
380 242 : params.set<std::vector<SubdomainName>>("block") = {
381 726 : std::to_string(RGMB::DUMMY_ASSEMBLY_BLOCK_ID)};
382 363 : params.set<MeshGeneratorName>("input") = name() + "_pattern";
383 121 : params.set<BoundaryName>("new_boundary") = RGMB::CORE_BOUNDARY_NAME;
384 :
385 242 : addMeshSubgenerator("BlockDeletionGenerator", name() + "_deleted", params);
386 121 : }
387 :
388 : std::string build_mesh_name;
389 :
390 : // Remove outer assembly sidesets created during assembly generation
391 : {
392 : // Get outer boundaries of all constituent assemblies based on assembly_type,
393 : // skipping all dummy assemblies
394 : std::vector<BoundaryName> boundaries_to_delete = {};
395 781 : for (const auto & pattern_x : _pattern)
396 : {
397 1878 : for (const auto & pattern_idx : pattern_x)
398 : {
399 1308 : const auto assembly_name = _inputs[pattern_idx];
400 1308 : if (assembly_name == _empty_key)
401 : continue;
402 : const auto assembly_id =
403 1001 : getMeshProperty<subdomain_id_type>(RGMB::assembly_type, assembly_name);
404 : const BoundaryName boundary_name =
405 2002 : RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX + std::to_string(assembly_id);
406 1001 : if (!std::count(boundaries_to_delete.begin(), boundaries_to_delete.end(), boundary_name))
407 465 : boundaries_to_delete.push_back(boundary_name);
408 : }
409 : }
410 211 : auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
411 :
412 422 : params.set<MeshGeneratorName>("input") =
413 211 : _empty_pos ? name() + "_deleted" : name() + "_pattern";
414 422 : params.set<std::vector<BoundaryName>>("boundary_names") = boundaries_to_delete;
415 :
416 211 : build_mesh_name = name() + "_delbds";
417 422 : addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
418 211 : }
419 :
420 869 : for (auto assembly : _inputs)
421 : {
422 658 : if (assembly != _empty_key)
423 : {
424 : subdomain_id_type assembly_type =
425 474 : getMeshProperty<subdomain_id_type>(RGMB::assembly_type, assembly);
426 474 : if (!getMeshProperty<bool>(RGMB::is_control_drum, assembly))
427 : {
428 : // For assembly structures, store region ID and block names of assembly regions and
429 : // constituent pins
430 : const auto & pin_region_id_map = getMeshProperty<
431 449 : std::map<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>>(
432 : RGMB::pin_region_id_map, assembly);
433 1069 : for (auto pin = pin_region_id_map.begin(); pin != pin_region_id_map.end(); ++pin)
434 620 : _pin_region_id_map.insert(
435 620 : std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
436 620 : pin->first, pin->second));
437 :
438 : const auto & pin_block_name_map =
439 449 : getMeshProperty<std::map<subdomain_id_type, std::vector<std::vector<std::string>>>>(
440 : RGMB::pin_block_name_map, assembly);
441 1069 : for (auto pin = pin_block_name_map.begin(); pin != pin_block_name_map.end(); ++pin)
442 620 : _pin_block_name_map.insert(
443 620 : std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(pin->first,
444 620 : pin->second));
445 :
446 : // Define background and duct region ID map from constituent assemblies
447 449 : if (_background_region_id_map.find(assembly_type) == _background_region_id_map.end())
448 : {
449 : // Store region ids and block names associated with duct and background regions for each
450 : // assembly, in case block names need to be recovered from region ids after
451 : // multiple assemblies have been stitched together into a core
452 : std::vector<subdomain_id_type> background_region_ids =
453 : getMeshProperty<std::vector<subdomain_id_type>>(RGMB::background_region_id,
454 449 : assembly);
455 : std::vector<std::vector<subdomain_id_type>> duct_region_ids =
456 : getMeshProperty<std::vector<std::vector<subdomain_id_type>>>(RGMB::duct_region_ids,
457 449 : assembly);
458 898 : _background_region_id_map.insert(
459 449 : std::pair<subdomain_id_type, std::vector<subdomain_id_type>>(
460 : assembly_type, background_region_ids));
461 898 : _duct_region_id_map.insert(
462 449 : std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
463 : assembly_type, duct_region_ids));
464 :
465 : std::vector<std::string> background_block_names =
466 449 : getMeshProperty<std::vector<std::string>>(RGMB::background_block_name, assembly);
467 : std::vector<std::vector<std::string>> duct_block_names =
468 : getMeshProperty<std::vector<std::vector<std::string>>>(RGMB::duct_block_names,
469 449 : assembly);
470 898 : _background_block_name_map.insert(
471 449 : std::pair<subdomain_id_type, std::vector<std::string>>(assembly_type,
472 : background_block_names));
473 898 : _duct_block_name_map.insert(
474 449 : std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(
475 : assembly_type, duct_block_names));
476 449 : }
477 : }
478 : else
479 : {
480 : // For control drum structures, store region ID and block name information of drum regions
481 : const auto & drum_region_ids =
482 25 : getMeshProperty<std::vector<std::vector<subdomain_id_type>>>(RGMB::drum_region_ids,
483 : assembly);
484 50 : _drum_region_id_map.insert(
485 0 : std::pair<subdomain_id_type, std::vector<std::vector<subdomain_id_type>>>(
486 : assembly_type, drum_region_ids));
487 : std::vector<std::vector<std::string>> drum_block_names =
488 : getMeshProperty<std::vector<std::vector<std::string>>>(RGMB::drum_block_names,
489 25 : assembly);
490 50 : _drum_block_name_map.insert(
491 25 : std::pair<subdomain_id_type, std::vector<std::vector<std::string>>>(
492 : assembly_type, drum_block_names));
493 25 : }
494 : }
495 : }
496 :
497 : // periphery meshing
498 211 : if (_mesh_periphery)
499 : {
500 45 : std::string periphery_mg_name = (_periphery_meshgenerator == "triangle")
501 : ? "PeripheralTriangleMeshGenerator"
502 81 : : "PeripheralRingMeshGenerator";
503 :
504 : // set up common options
505 45 : auto params = _app.getFactory().getValidParams(periphery_mg_name);
506 135 : params.set<MeshGeneratorName>("input") = name() + "_delbds";
507 45 : params.set<Real>("peripheral_ring_radius") = _outer_circle_radius;
508 90 : params.set<BoundaryName>("external_boundary_name") = "outside_periphery";
509 45 : params.set<SubdomainName>("peripheral_ring_block_name") = RGMB::PERIPHERAL_RING_BLOCK_NAME;
510 :
511 : // unique MG options
512 45 : if (_periphery_meshgenerator == "triangle")
513 : {
514 9 : params.set<unsigned int>("peripheral_ring_num_segments") = _outer_circle_num_segments;
515 9 : params.set<Real>("desired_area") = _desired_area;
516 18 : params.set<std::string>("desired_area_func") = _desired_area_func;
517 : }
518 36 : else if (_periphery_meshgenerator == "quad_ring")
519 : {
520 36 : params.set<subdomain_id_type>("peripheral_ring_block_id") = RGMB::PERIPHERAL_RING_BLOCK_ID;
521 36 : params.set<BoundaryName>("input_mesh_external_boundary") = RGMB::CORE_BOUNDARY_NAME;
522 36 : params.set<unsigned int>("peripheral_layer_num") = _periphery_num_layers;
523 : }
524 :
525 : // finish periphery input
526 45 : build_mesh_name = name() + "_periphery";
527 45 : addMeshSubgenerator(periphery_mg_name, build_mesh_name, params);
528 45 : }
529 :
530 211 : if (_extrude && _mesh_dimensions == 3)
531 296 : build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
532 :
533 : // Store final mesh subgenerator
534 211 : _build_mesh = &getMeshByName(build_mesh_name);
535 : }
536 : // If mesh generation should be bypassed, call getMeshes to resolve MeshGeneratorSystem
537 : // dependencies
538 : else
539 94 : auto input_meshes = getMeshes("inputs");
540 :
541 258 : generateMetadata();
542 891 : }
543 :
544 : void
545 258 : CoreMeshGenerator::generateMetadata()
546 : {
547 : // Define metadata related to downstream function calls
548 258 : if (_mesh_periphery)
549 : {
550 54 : declareMeshProperty(RGMB::peripheral_ring_radius, _outer_circle_radius);
551 54 : declareMeshProperty(RGMB::peripheral_ring_region_id, _periphery_region_id);
552 : }
553 :
554 : // Determine constituent pin type ids and define lattice
555 : std::vector<std::vector<int>> assembly_name_lattice;
556 : std::vector<std::string> input_assembly_names;
557 : std::vector<std::string> input_pin_names;
558 :
559 : // Iterate through input assembly names and define constituent assemblies and pins
560 1048 : for (const auto i : index_range(_inputs))
561 : {
562 : const auto input_assembly_name = _inputs[i];
563 790 : if (input_assembly_name != _empty_key)
564 : {
565 568 : input_assembly_names.push_back(input_assembly_name);
566 568 : if (!getMeshProperty<bool>(RGMB::is_control_drum, input_assembly_name) &&
567 534 : !getMeshProperty<bool>(RGMB::is_single_pin, input_assembly_name))
568 : {
569 : const auto pin_names =
570 416 : getMeshProperty<std::vector<std::string>>(RGMB::pin_names, input_assembly_name);
571 1052 : for (const auto & pin_name : pin_names)
572 636 : if (std::find(input_pin_names.begin(), input_pin_names.end(), pin_name) ==
573 : input_pin_names.end())
574 553 : input_pin_names.push_back(pin_name);
575 416 : }
576 : }
577 : }
578 :
579 : // Iterate through pattern and remap dummy assemblies with index -1
580 949 : for (const auto i : index_range(_pattern))
581 : {
582 691 : std::vector<int> assembly_name_idx(_pattern[i].size());
583 2268 : for (const auto j : index_range(_pattern[i]))
584 : {
585 1577 : const auto input_assembly_name = _inputs[_pattern[i][j]];
586 : // Use an assembly type of -1 to represent a dummy assembly
587 1577 : if (input_assembly_name == _empty_key)
588 352 : assembly_name_idx[j] = -1;
589 : // Set index of assembly name based on `input_assembly_names` variable
590 : else
591 : {
592 1225 : const auto it = std::find(
593 : input_assembly_names.begin(), input_assembly_names.end(), input_assembly_name);
594 1225 : assembly_name_idx[j] = it - input_assembly_names.begin();
595 : }
596 : }
597 691 : assembly_name_lattice.push_back(assembly_name_idx);
598 691 : }
599 :
600 258 : declareMeshProperty(RGMB::pin_names, input_pin_names);
601 : declareMeshProperty(RGMB::assembly_names, input_assembly_names);
602 : declareMeshProperty(RGMB::assembly_lattice, assembly_name_lattice);
603 330 : declareMeshProperty(RGMB::extruded, _extrude && _mesh_dimensions == 3);
604 258 : }
605 :
606 : bool
607 171 : CoreMeshGenerator::constituentAssembliesNeedFlexibleStiching()
608 : {
609 : MeshGeneratorName first_nondummy_assembly = "";
610 : bool assembly_homogenization = false;
611 : unsigned int n_constituent_pins = 0;
612 : unsigned int n_pin_sectors = 0;
613 :
614 : // Loop through all non-dummy input assemblies. Flexible assembly stitching is needed if one of
615 : // the following criteria are met:
616 : // 1. The number of constituent pins within the assembly does not match with another assembly
617 : // 2. The value of is_single_pin and is_homogenized metadata do not agree with another assembly
618 : // 3. The number of sectors of the constituent pins of an assembly do not match with the
619 : // constituent pins of another assembly
620 639 : for (const auto i : index_range(_inputs))
621 : {
622 : // Skip if assembly name is equal to dummy assembly name
623 468 : if (_inputs[i] == _empty_key)
624 144 : continue;
625 :
626 : // Compute total number of constituent pins in assembly, as well as the number of sectors per
627 : // side for each pin Note: number of sectors per side is defined uniformly across constituent
628 : // pins of an assembly, so only first one needs to be checked
629 : unsigned int total_pins = 0;
630 : unsigned int pin_sectors_per_side = 0;
631 324 : if (!getMeshProperty<bool>(RGMB::is_single_pin, _inputs[i]))
632 : {
633 : const auto first_pin_name =
634 270 : getMeshProperty<std::vector<std::string>>(RGMB::pin_names, _inputs[i])[0];
635 270 : pin_sectors_per_side = getMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta",
636 540 : first_pin_name + "_2D")[0];
637 : const auto pin_lattice =
638 270 : getMeshProperty<std::vector<std::vector<int>>>(RGMB::pin_lattice, _inputs[i]);
639 972 : for (const auto i : index_range(pin_lattice))
640 702 : total_pins += pin_lattice[i].size();
641 270 : }
642 : else
643 : {
644 54 : if (getMeshProperty<bool>(RGMB::is_homogenized, _inputs[i]))
645 : {
646 : // Homogenized assembly
647 : total_pins = 0;
648 : pin_sectors_per_side = 0;
649 : }
650 : else
651 : {
652 : // Assembly with single constituent pin
653 : total_pins = 1;
654 36 : pin_sectors_per_side = getMeshProperty<std::vector<unsigned int>>(
655 72 : "num_sectors_per_side_meta", _inputs[i] + "_2D")[0];
656 : }
657 : }
658 :
659 324 : if (first_nondummy_assembly == "")
660 : {
661 171 : first_nondummy_assembly = MeshGeneratorName(_inputs[i]);
662 171 : assembly_homogenization = getMeshProperty<bool>(RGMB::is_homogenized, _inputs[i]);
663 : n_constituent_pins = total_pins;
664 : n_pin_sectors = pin_sectors_per_side;
665 : }
666 : else
667 : {
668 153 : if (getMeshProperty<bool>(RGMB::is_homogenized, _inputs[i]) != assembly_homogenization)
669 : {
670 0 : mooseWarning("Detected mix of homogenized and heterogeneous assemblies between " +
671 0 : first_nondummy_assembly + " and " + _inputs[i]);
672 0 : return true;
673 : }
674 153 : if (total_pins != n_constituent_pins)
675 : {
676 0 : mooseWarning(
677 0 : "Detected assemblies with different number of total constituent pins between " +
678 0 : first_nondummy_assembly + " and " + _inputs[i]);
679 0 : return true;
680 : }
681 153 : if (pin_sectors_per_side != n_pin_sectors)
682 : {
683 0 : mooseWarning("Constituent pins in " + first_nondummy_assembly + " and " + _inputs[i] +
684 : " differ in terms of number of sectors per side");
685 0 : return true;
686 : }
687 : }
688 : }
689 : return false;
690 : }
691 :
692 : std::unique_ptr<MeshBase>
693 213 : CoreMeshGenerator::generate()
694 : {
695 : // Must be called to free the ReactorMeshParams mesh
696 213 : freeReactorMeshParams();
697 :
698 : // If bypass_mesh is true, return a null mesh. In this mode, an output mesh is not
699 : // generated and only metadata is defined on the generator, so logic related to
700 : // generation of output mesh will not be called
701 213 : if (getReactorParam<bool>(RGMB::bypass_meshgen))
702 : {
703 : auto null_mesh = nullptr;
704 : return null_mesh;
705 : }
706 : // This generate() method will be called once the subgenerators that we depend on are
707 : // called. This is where we reassign subdomain ids/names in case they were merged
708 : // when stitching assemblies into the core. This is also where we set region_id extra
709 : // element integers, which has not been set yet for extruded geometries
710 :
711 : // Define all extra element names and integers
712 211 : std::string pin_type_id_name = "pin_type_id";
713 211 : std::string assembly_type_id_name = "assembly_type_id";
714 211 : std::string plane_id_name = "plane_id";
715 211 : std::string region_id_name = "region_id";
716 211 : std::string radial_id_name = "radial_id";
717 211 : const std::string default_block_name = RGMB::CORE_BLOCK_NAME_PREFIX;
718 :
719 211 : auto pin_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), pin_type_id_name, true);
720 211 : auto assembly_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), assembly_type_id_name, true);
721 211 : auto radial_id_int = getElemIntegerFromMesh(*(*_build_mesh), radial_id_name, true);
722 211 : auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name, true);
723 : unsigned int plane_id_int = 0;
724 211 : if (_extrude)
725 296 : plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
726 :
727 : // Get next free block ID in mesh in case subdomain ids need to be remapped
728 211 : auto next_block_id = MooseMeshUtils::getNextFreeSubdomainID(*(*(_build_mesh)));
729 : std::map<std::string, SubdomainID> rgmb_name_id_map;
730 :
731 : // Loop through all mesh elements and set region ids and reassign block IDs/names
732 : // if they were merged during assembly stitching
733 616486 : for (auto & elem : (*_build_mesh)->active_element_ptr_range())
734 : {
735 308032 : dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
736 308032 : dof_id_type pin_type_id = elem->get_extra_integer(pin_type_id_int);
737 :
738 308032 : if (_pin_region_id_map.find(pin_type_id) != _pin_region_id_map.end())
739 : {
740 : // Pin type element, get region ID from pin_type, z_id, and radial_idx
741 172346 : const dof_id_type radial_idx = elem->get_extra_integer(radial_id_int);
742 172346 : const auto elem_rid = _pin_region_id_map[pin_type_id][z_id][radial_idx];
743 172346 : elem->set_extra_integer(region_id_int, elem_rid);
744 :
745 : // Set element block name and block id
746 172346 : bool has_block_names = !_pin_block_name_map[pin_type_id].empty();
747 172346 : auto elem_block_name = default_block_name;
748 172346 : if (has_block_names)
749 115776 : elem_block_name += "_" + _pin_block_name_map[pin_type_id][z_id][radial_idx];
750 114458 : else if (getReactorParam<bool>(RGMB::region_id_as_block_name))
751 67384 : elem_block_name += "_REG" + std::to_string(elem_rid);
752 172346 : if (elem->type() == TRI3 || elem->type() == PRISM6)
753 : elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
754 344692 : updateElementBlockNameId(
755 172346 : *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
756 : }
757 135686 : else if ((*_build_mesh)->subdomain_name(elem->subdomain_id()) ==
758 : RGMB::PERIPHERAL_RING_BLOCK_NAME)
759 : // periphery type element
760 : {
761 : // set region ID of core periphery element
762 17454 : elem->set_extra_integer(region_id_int, _periphery_region_id);
763 : // set block name and block name of core periphery element
764 17454 : auto elem_block_name = _periphery_block_name;
765 17454 : if (getReactorParam<bool>(RGMB::region_id_as_block_name))
766 0 : elem_block_name += "_REG" + std::to_string(_periphery_region_id);
767 17454 : if (elem->type() == TRI3 || elem->type() == PRISM6)
768 : elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
769 34908 : updateElementBlockNameId(
770 17454 : *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
771 : }
772 : else
773 : {
774 118232 : dof_id_type assembly_type_id = elem->get_extra_integer(assembly_type_id_int);
775 : // Infer peripheral index of assembly background, assembly duct, or control drum regions from
776 : // pin_type_id
777 118232 : unsigned int peripheral_idx = RGMB::MAX_PIN_TYPE_ID - pin_type_id;
778 :
779 : // check if element is part of drum region
780 118232 : if (_drum_region_id_map.find(assembly_type_id) != _drum_region_id_map.end())
781 : {
782 : // Element is in a control drum region. Infer region id from assembly_type_id, z_id, and
783 : // peripheral_index
784 24128 : const auto elem_rid = _drum_region_id_map[assembly_type_id][z_id][peripheral_idx];
785 24128 : elem->set_extra_integer(region_id_int, elem_rid);
786 :
787 : // Set element block name and block id
788 24128 : auto elem_block_name = default_block_name;
789 24128 : if (getReactorParam<bool>(RGMB::region_id_as_block_name))
790 48256 : elem_block_name += "_REG" + std::to_string(elem_rid);
791 : else
792 : {
793 0 : bool has_drum_block_name = !_drum_block_name_map[assembly_type_id].empty();
794 0 : if (has_drum_block_name)
795 0 : elem_block_name += "_" + _drum_block_name_map[assembly_type_id][z_id][peripheral_idx];
796 : }
797 24128 : if (elem->type() == TRI3 || elem->type() == PRISM6)
798 : elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
799 48256 : updateElementBlockNameId(
800 24128 : *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
801 : }
802 : else
803 : {
804 : // Element is in an assembly duct or background region since it doesn't
805 : // have an assembly type id in the drum region map. Infer region id from
806 : // assembly_type_id, z_id, and peripheral_index
807 : bool is_background_region = peripheral_idx == 0;
808 : const auto elem_rid =
809 : (is_background_region
810 94104 : ? _background_region_id_map[assembly_type_id][z_id]
811 30394 : : _duct_region_id_map[assembly_type_id][z_id][peripheral_idx - 1]);
812 94104 : elem->set_extra_integer(region_id_int, elem_rid);
813 :
814 : // Set element block name and block id
815 94104 : auto elem_block_name = default_block_name;
816 94104 : if (getReactorParam<bool>(RGMB::region_id_as_block_name))
817 114480 : elem_block_name += "_REG" + std::to_string(elem_rid);
818 : else
819 : {
820 36864 : if (is_background_region)
821 : {
822 21996 : bool has_background_block_name = !_background_block_name_map[assembly_type_id].empty();
823 21996 : if (has_background_block_name)
824 20736 : elem_block_name += "_" + _background_block_name_map[assembly_type_id][z_id];
825 : }
826 : else
827 : {
828 14868 : bool has_duct_block_names = !_duct_block_name_map[assembly_type_id].empty();
829 14868 : if (has_duct_block_names)
830 : elem_block_name +=
831 15552 : "_" + _duct_block_name_map[assembly_type_id][z_id][peripheral_idx - 1];
832 : }
833 : }
834 94104 : if (elem->type() == TRI3 || elem->type() == PRISM6)
835 : elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
836 188208 : updateElementBlockNameId(
837 94104 : *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
838 : }
839 : }
840 211 : }
841 :
842 : // Sideset 10000 does not get stitched properly when BlockDeletionGenerator
843 : // is used for deleting dummy assemblies. This block copies missing sides
844 : // into sideset 10000 from sideset RGMB::CORE_BOUNDARY_NAME
845 211 : BoundaryInfo & boundary_info = (*_build_mesh)->get_boundary_info();
846 : boundary_id_type source_id =
847 422 : MooseMeshUtils::getBoundaryIDs(**_build_mesh, {RGMB::CORE_BOUNDARY_NAME}, true)[0];
848 : boundary_id_type target_id = 10000;
849 : const auto sideset_map = boundary_info.get_sideset_map();
850 :
851 358710 : for (const auto & [elem, id_pair] : sideset_map)
852 : {
853 358499 : const auto side_id = id_pair.first;
854 358499 : const auto sideset_id = id_pair.second;
855 :
856 : // Filter all sides that belong to RGMB::CORE_BOUNDARY_NAME sideset
857 358499 : if (sideset_id == source_id)
858 : {
859 : auto mm_it = sideset_map.equal_range(elem);
860 : bool found = false;
861 : // Check if side is defined in sideset 10000
862 68754 : for (auto it = mm_it.first; it != mm_it.second; it++)
863 : {
864 50160 : if (it->second.first == side_id && it->second.second == target_id)
865 : found = true;
866 : }
867 : // Add side if not found in sideset 10000
868 18594 : if (!found)
869 4893 : boundary_info.add_side(elem, side_id, target_id);
870 : }
871 : }
872 :
873 422 : if (getParam<bool>("generate_depletion_id"))
874 : {
875 56 : const MooseEnum option = getParam<MooseEnum>("depletion_id_type");
876 28 : addDepletionId(*(*_build_mesh), option, DepletionIDGenerationLevel::Core, _extrude);
877 28 : }
878 :
879 : // Mark mesh as not prepared, as block ID's were re-assigned in this method
880 211 : (*_build_mesh)->set_isnt_prepared();
881 :
882 : return std::move(*_build_mesh);
883 : }
|