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 "ControlDrumMeshGenerator.h"
11 :
12 : #include "ReactorGeometryMeshBuilderBase.h"
13 : #include "PolygonalMeshGenerationUtils.h"
14 : #include "MooseApp.h"
15 : #include "Factory.h"
16 : #include "libmesh/elem.h"
17 : #include "MooseMeshUtils.h"
18 : #include "PolygonMeshGeneratorBase.h"
19 :
20 : registerMooseObject("ReactorApp", ControlDrumMeshGenerator);
21 :
22 : InputParameters
23 178 : ControlDrumMeshGenerator::validParams()
24 : {
25 178 : auto params = ReactorGeometryMeshBuilderBase::validParams();
26 :
27 356 : params.addRequiredParam<MeshGeneratorName>(
28 : "reactor_params",
29 : "The ReactorMeshParams MeshGenerator that is the basis for this component mesh.");
30 356 : params.addRequiredParam<subdomain_id_type>(
31 : "assembly_type",
32 : "The assembly type integer ID to use for this control drum definition. "
33 : "This parameter should be uniquely defined for each ControlDrumMeshGenerator "
34 : "and AssemblyMeshGenerator structure in the RGMB workflow.");
35 356 : params.addParam<bool>("extrude",
36 356 : false,
37 : "Determines if this is the final step in the geometry construction"
38 : " and extrudes the 2D geometry to 3D. If this is true then this mesh "
39 : "cannot be used in further mesh building in the Reactor workflow");
40 356 : params.addRequiredRangeCheckedParam<Real>(
41 : "drum_inner_radius", "drum_inner_radius>0", "Inner radius of drum region");
42 356 : params.addRequiredRangeCheckedParam<Real>(
43 : "drum_outer_radius", "drum_outer_radius>0", "Outer radius of drum region");
44 534 : params.addRangeCheckedParam<unsigned int>(
45 : "drum_inner_intervals",
46 356 : 1,
47 : "drum_inner_intervals>0",
48 : "Number of radial mesh intervals in region up to inner drum radius");
49 534 : params.addRangeCheckedParam<unsigned int>(
50 356 : "drum_intervals", 1, "drum_intervals>0", "Number of radial mesh intervals in drum region");
51 356 : params.addRangeCheckedParam<Real>("pad_start_angle",
52 : "pad_start_angle>=0 & pad_start_angle < 360",
53 : "Starting angle of drum pad region");
54 356 : params.addRangeCheckedParam<Real>(
55 : "pad_end_angle", "pad_end_angle>0 & pad_end_angle < 720", "Ending angle of drum pad region");
56 356 : params.addRequiredRangeCheckedParam<unsigned int>(
57 : "num_azimuthal_sectors",
58 : "num_azimuthal_sectors>2",
59 : "Number of azimuthal sectors to sub-divide the drum region into");
60 356 : params.addRequiredParam<std::vector<std::vector<subdomain_id_type>>>(
61 : "region_ids",
62 : "IDs for each radial and axial zone for assignment of region_id extra element "
63 : "id. "
64 : "Inner indexing is radial zones (drum inner/drum/drum outer), outer indexing is axial");
65 356 : params.addParam<std::vector<std::vector<std::string>>>(
66 : "block_names",
67 : "Block names for each radial and axial zone. "
68 : "Inner indexing is radial zones (drum inner/drum/drum outer), outer indexing is axial");
69 356 : params.addParam<Real>("azimuthal_node_tolerance",
70 356 : 0.1,
71 : "(in degrees) The absolute tolerance for which to shift an azimuthal node "
72 : "to match the pad start/end angles");
73 :
74 356 : params.addParamNamesToGroup("region_ids assembly_type", "ID assigment");
75 356 : params.addParamNamesToGroup("drum_inner_radius drum_outer_radius drum_inner_intervals "
76 : "drum_intervals num_azimuthal_sectors",
77 : "Drum specifications");
78 356 : params.addParamNamesToGroup("pad_start_angle pad_end_angle azimuthal_node_tolerance",
79 : "Control pad specifications");
80 :
81 178 : params.addClassDescription(
82 : "This ControlDrumMeshGenerator object is designed to generate "
83 : "drum-like structures, with IDs, from a reactor geometry. "
84 : "These structures can be used directly within CoreMeshGenerator to stitch"
85 : "control drums into a core lattice alongside AssemblyMeshGenerator structures");
86 : // depletion id generation params are added
87 178 : addDepletionIDParams(params);
88 :
89 178 : return params;
90 0 : }
91 :
92 105 : ControlDrumMeshGenerator::ControlDrumMeshGenerator(const InputParameters & parameters)
93 : : ReactorGeometryMeshBuilderBase(parameters),
94 210 : _assembly_type(getParam<subdomain_id_type>("assembly_type")),
95 210 : _drum_inner_radius(getParam<Real>("drum_inner_radius")),
96 210 : _drum_outer_radius(getParam<Real>("drum_outer_radius")),
97 210 : _extrude(getParam<bool>("extrude")),
98 420 : _region_ids(getParam<std::vector<std::vector<subdomain_id_type>>>("region_ids"))
99 : {
100 : // Initialize ReactorMeshParams object
101 315 : initializeReactorMeshParams(getParam<MeshGeneratorName>("reactor_params"));
102 :
103 : // Flexible stitching needs to be invoked in order to create control drum mesh
104 105 : if (!getReactorParam<bool>(RGMB::flexible_assembly_stitching))
105 2 : mooseError("'flexible_assembly_stitching' needs to be set to true in ReactorMeshParams in "
106 : "order to use ControlDrumMeshGenerator");
107 :
108 103 : _geom_type = getReactorParam<std::string>(RGMB::mesh_geometry);
109 103 : _mesh_dimensions = getReactorParam<unsigned int>(RGMB::mesh_dimensions);
110 :
111 206 : const auto drum_inner_intervals = getParam<unsigned int>("drum_inner_intervals");
112 206 : const auto drum_intervals = getParam<unsigned int>("drum_intervals");
113 206 : const auto num_sectors = getParam<unsigned int>("num_azimuthal_sectors");
114 103 : const auto assembly_pitch = getReactorParam<Real>(RGMB::assembly_pitch);
115 :
116 : // Check drum pad parameters
117 206 : if (isParamSetByUser("pad_start_angle"))
118 : {
119 146 : _pad_start_angle = getParam<Real>("pad_start_angle");
120 146 : if (!isParamSetByUser("pad_end_angle"))
121 2 : paramError("pad_start_angle",
122 : "If 'pad_start_angle' is set, 'pad_end_angle' needs to also be set.");
123 142 : _pad_end_angle = getParam<Real>("pad_end_angle");
124 :
125 71 : if ((_pad_end_angle - _pad_start_angle >= 360) || (_pad_end_angle - _pad_start_angle <= 0))
126 4 : paramError("pad_start_angle",
127 : "The difference between 'pad_end_angle' and 'pad_start_angle' must be between 0 "
128 : "and 360 exclusive.");
129 67 : _has_pad_region = true;
130 : }
131 : else
132 : {
133 60 : if (isParamSetByUser("pad_end_angle"))
134 2 : paramError("pad_end_angle",
135 : "If 'pad_end_angle' is set, 'pad_start_angle' needs to also be set.");
136 28 : _has_pad_region = false;
137 : }
138 :
139 : // Error checking for azimuthal node tolerance
140 190 : const auto azimuthal_node_tolerance = getParam<Real>("azimuthal_node_tolerance");
141 151 : if (!_has_pad_region && isParamSetByUser("azimuthal_node_tolerance"))
142 0 : paramError("azimuthal_node_tolerance",
143 : "This parameter is relevant only when pad start and end angles are defined");
144 95 : if (_has_pad_region && MooseUtils::absoluteFuzzyGreaterEqual(2. * azimuthal_node_tolerance,
145 67 : 360. / (Real)num_sectors))
146 2 : paramError("azimuthal_node_tolerance",
147 : "Azimuthal node tolerance should be smaller than half the azimuthal interval size "
148 : "as defined by 'num_azimuthal_sectors'");
149 93 : if (_has_pad_region && MooseUtils::absoluteFuzzyGreaterEqual(2. * azimuthal_node_tolerance,
150 65 : _pad_end_angle - _pad_start_angle))
151 2 : paramError("azimuthal_node_tolerance",
152 : "Azimuthal node tolerance should be smaller than half the difference of the pad "
153 : "angle range");
154 :
155 : // Check region IDs have correct size
156 91 : const unsigned int n_radial_regions = _has_pad_region ? 4 : 3;
157 : unsigned int n_axial_levels =
158 91 : (_mesh_dimensions == 3)
159 : ? getReactorParam<std::vector<unsigned int>>(RGMB::axial_mesh_intervals).size()
160 127 : : 1;
161 91 : if (_region_ids.size() != n_axial_levels)
162 2 : mooseError("The size of region IDs must be equal to the number of axial levels as defined in "
163 : "the ReactorMeshParams object");
164 89 : if (_region_ids[0].size() != n_radial_regions)
165 : {
166 : std::string err_msg =
167 4 : "'region_ids' parameter does not have the correct number of elements per axial zone. ";
168 4 : err_msg += _has_pad_region
169 : ? "For control drums with a pad region, 4 radial IDs need to be provided per "
170 : "axial zone (drum inner, drum pad, drum ex-pad, and drum outer)"
171 : : "For control drums with no pad region, 3 radial IDs need to be provided per "
172 4 : "axial zone (drum inner, drum, and drum outer)";
173 4 : paramError("region_ids", err_msg);
174 : }
175 :
176 : // Check block names have the correct size
177 170 : if (isParamValid("block_names"))
178 : {
179 6 : if (getReactorParam<bool>(RGMB::region_id_as_block_name))
180 2 : paramError("block_names",
181 : "If ReactorMeshParams/region_id_as_block_name is set, block_names should not be "
182 : "specified in ControlDrumMeshGenerator");
183 4 : _has_block_names = true;
184 12 : _block_names = getParam<std::vector<std::vector<std::string>>>("block_names");
185 4 : if (_region_ids.size() != _block_names.size())
186 2 : mooseError("The size of block_names must match the size of region_ids");
187 2 : for (const auto i : index_range(_region_ids))
188 2 : if (_region_ids[i].size() != _block_names[i].size())
189 2 : mooseError("The size of block_names must match the size of region_ids");
190 : }
191 : else
192 79 : _has_block_names = false;
193 :
194 : // Check extrusion parameters
195 79 : if (_extrude && _mesh_dimensions != 3)
196 2 : paramError("extrude",
197 : "In order to extrude this mesh, ReactorMeshParams/dim needs to be set to 3\n");
198 156 : if (_extrude && (!hasReactorParam<boundary_id_type>(RGMB::top_boundary_id) ||
199 77 : !hasReactorParam<boundary_id_type>(RGMB::bottom_boundary_id)))
200 2 : mooseError("Both top_boundary_id and bottom_boundary_id must be provided in ReactorMeshParams "
201 : "if using extruded geometry");
202 :
203 : // Define azimuthal angles explicitly based on num_azimuthal_sectors and manually add pad start
204 : // angle and end angle if they are not contained within these angle intervals
205 : std::vector<Real> azimuthal_angles;
206 75 : const auto custom_start_angle = _has_pad_region ? _pad_start_angle : 0.;
207 75 : const auto custom_end_angle =
208 75 : _has_pad_region ? ((_pad_end_angle > 360) ? _pad_end_angle - 360. : _pad_end_angle) : 0.;
209 7941 : for (unsigned int i = 0; i < num_sectors; ++i)
210 : {
211 7866 : Real current_azim_angle = (Real)i * 360. / (Real)num_sectors;
212 7866 : Real next_azim_angle = (Real)(i + 1) * 360. / (Real)num_sectors;
213 7866 : if (!_has_pad_region)
214 2172 : azimuthal_angles.push_back(current_azim_angle);
215 : else
216 : {
217 : // When pad regions are involved, check if the current azimuthal node angle coincides with
218 : // pad start/end angle to within tolerance. If it does, then shift the azimuthal node location
219 : // to match the pad angle location. If it does not and the pad angle falls within the
220 : // azimuthal sector, then create an additional node for the pad location
221 5694 : if (MooseUtils::absoluteFuzzyLessEqual(std::abs(current_azim_angle - custom_start_angle),
222 : azimuthal_node_tolerance))
223 : {
224 56 : azimuthal_angles.push_back(custom_start_angle);
225 : }
226 5638 : else if (MooseUtils::absoluteFuzzyLessEqual(std::abs(current_azim_angle - custom_end_angle),
227 : azimuthal_node_tolerance))
228 : {
229 61 : azimuthal_angles.push_back(custom_end_angle);
230 : }
231 5577 : else if (MooseUtils::absoluteFuzzyGreaterThan(custom_start_angle - current_azim_angle,
232 5577 : azimuthal_node_tolerance) &&
233 1426 : MooseUtils::absoluteFuzzyGreaterThan(next_azim_angle - custom_start_angle,
234 : azimuthal_node_tolerance))
235 : {
236 5 : mooseWarning("pad_start_angle not contained within drum azimuthal discretization so "
237 : "additional azimuthal nodes are created to align mesh with this angle");
238 5 : azimuthal_angles.push_back(current_azim_angle);
239 5 : azimuthal_angles.push_back(custom_start_angle);
240 : }
241 5572 : else if (MooseUtils::absoluteFuzzyGreaterThan(custom_end_angle - current_azim_angle,
242 5572 : azimuthal_node_tolerance) &&
243 2786 : MooseUtils::absoluteFuzzyGreaterThan(next_azim_angle - custom_end_angle,
244 : azimuthal_node_tolerance))
245 : {
246 0 : mooseWarning("pad_end_angle not contained within drum azimuthal discretization so "
247 : "additional azimuthal nodes are created to align mesh with this angle");
248 0 : azimuthal_angles.push_back(current_azim_angle);
249 0 : azimuthal_angles.push_back(custom_end_angle);
250 : }
251 : else
252 5572 : azimuthal_angles.push_back(current_azim_angle);
253 : }
254 : }
255 :
256 : // Check drum radius parameters
257 75 : if (_drum_inner_radius >= _drum_outer_radius)
258 2 : paramError("drum_outer_radius", "Drum outer radius must be larger than the inner radius");
259 : // Check if volume preserved radius of outer radius exceeds assembly halfpitch. In data driven
260 : // mode, radius is assumed not to need volume preservation
261 : auto radius_corrfac =
262 73 : getReactorParam<bool>(RGMB::bypass_meshgen)
263 73 : ? 1.0
264 64 : : PolygonalMeshGenerationUtils::radiusCorrectionFactor(azimuthal_angles);
265 73 : if (_drum_outer_radius * radius_corrfac >= assembly_pitch / 2.)
266 2 : paramError("drum_outer_radius",
267 : "Volume-corrected outer radius of drum region must be smaller than half the "
268 : "assembly pitch as "
269 : "defined by 'ReactorMeshParams/assembly_pitch'");
270 :
271 : // No subgenerators will be called if option to bypass mesh generators is enabled
272 71 : if (!getReactorParam<bool>(RGMB::bypass_meshgen))
273 : {
274 : const std::string block_name_prefix =
275 62 : RGMB::DRUM_BLOCK_NAME_PREFIX + std::to_string(_assembly_type);
276 :
277 : {
278 : // Invoke AdvancedConcentricCircleGenerator to define drum mesh without background region
279 62 : auto params = _app.getFactory().getValidParams("AdvancedConcentricCircleGenerator");
280 :
281 62 : params.set<std::vector<Real>>("customized_azimuthal_angles") = azimuthal_angles;
282 62 : params.set<std::vector<double>>("ring_radii") = {_drum_inner_radius, _drum_outer_radius};
283 62 : params.set<std::vector<unsigned int>>("ring_intervals") = {drum_inner_intervals,
284 124 : drum_intervals};
285 62 : if (drum_inner_intervals > 1)
286 : {
287 62 : params.set<std::vector<subdomain_id_type>>("ring_block_ids") = {
288 : RGMB::CONTROL_DRUM_BLOCK_ID_INNER_TRI,
289 : RGMB::CONTROL_DRUM_BLOCK_ID_INNER,
290 124 : RGMB::CONTROL_DRUM_BLOCK_ID_PAD};
291 124 : params.set<std::vector<SubdomainName>>("ring_block_names") = {
292 620 : block_name_prefix + "_R0_TRI", block_name_prefix + "_R0", block_name_prefix + "_R1"};
293 : }
294 : else
295 : {
296 0 : params.set<std::vector<subdomain_id_type>>("ring_block_ids") = {
297 0 : RGMB::CONTROL_DRUM_BLOCK_ID_INNER, RGMB::CONTROL_DRUM_BLOCK_ID_PAD};
298 0 : params.set<std::vector<SubdomainName>>("ring_block_names") = {block_name_prefix + "_R0",
299 0 : block_name_prefix + "_R1"};
300 : }
301 62 : params.set<bool>("create_outward_interface_boundaries") = false;
302 :
303 124 : addMeshSubgenerator("AdvancedConcentricCircleGenerator", name() + "_accg", params);
304 62 : }
305 : {
306 : // Invoke FlexiblePatternGenerator to triangulate drum background region
307 124 : auto params = _app.getFactory().getValidParams("FlexiblePatternGenerator");
308 :
309 310 : params.set<std::vector<MeshGeneratorName>>("inputs") = {name() + "_accg"};
310 62 : params.set<std::vector<libMesh::Point>>("extra_positions") = {libMesh::Point(0, 0, 0)};
311 62 : params.set<std::vector<unsigned int>>("extra_positions_mg_indices") = {0};
312 62 : params.set<bool>("use_auto_area_func") = true;
313 : // `verify_holes` is set to false to prevent false positive instances of points outside of
314 : // defined holes, which can create test failures on certain dev environments
315 62 : params.set<bool>("verify_holes") = false;
316 139 : params.set<MooseEnum>("boundary_type") = (_geom_type == "Hex") ? "HEXAGON" : "CARTESIAN";
317 62 : params.set<unsigned int>("boundary_sectors") =
318 62 : getReactorParam<unsigned int>(RGMB::num_sectors_flexible_stitching);
319 62 : params.set<Real>("boundary_size") = assembly_pitch;
320 62 : params.set<boundary_id_type>("external_boundary_id") =
321 62 : RGMB::ASSEMBLY_BOUNDARY_ID_START + _assembly_type;
322 124 : params.set<BoundaryName>("external_boundary_name") =
323 62 : RGMB::ASSEMBLY_BOUNDARY_NAME_PREFIX + std::to_string(_assembly_type);
324 186 : params.set<SubdomainName>("background_subdomain_name") = block_name_prefix + "_R2_TRI";
325 62 : params.set<subdomain_id_type>("background_subdomain_id") = RGMB::CONTROL_DRUM_BLOCK_ID_OUTER;
326 :
327 124 : addMeshSubgenerator("FlexiblePatternGenerator", name() + "_fpg", params);
328 :
329 : // Pass mesh meta-data defined in subgenerator constructor to this MeshGenerator
330 124 : copyMeshProperty<bool>("is_control_drum_meta", name() + "_fpg");
331 124 : copyMeshProperty<Real>("pattern_pitch_meta", name() + "_fpg");
332 62 : }
333 62 : std::string build_mesh_name = name() + "_delbds";
334 : {
335 : // Invoke BoundaryDeletionGenerator to delete extra sidesets created by
336 : // AdvancedConcentricCircleGenerator
337 124 : auto params = _app.getFactory().getValidParams("BoundaryDeletionGenerator");
338 :
339 248 : params.set<MeshGeneratorName>("input") = name() + "_fpg";
340 248 : params.set<std::vector<BoundaryName>>("boundary_names") = {"0", "2"};
341 :
342 124 : addMeshSubgenerator("BoundaryDeletionGenerator", build_mesh_name, params);
343 62 : }
344 62 : if (_extrude && _mesh_dimensions == 3)
345 0 : build_mesh_name = callExtrusionMeshSubgenerators(build_mesh_name);
346 :
347 : // Store final mesh subgenerator
348 62 : _build_mesh = &getMeshByName(build_mesh_name);
349 : }
350 :
351 71 : generateMetadata();
352 71 : }
353 :
354 : void
355 71 : ControlDrumMeshGenerator::generateMetadata()
356 : {
357 : // Declare metadata for use in downstream mesh generators
358 71 : declareMeshProperty(RGMB::assembly_type, _assembly_type);
359 : declareMeshProperty(RGMB::pitch, getReactorParam<Real>(RGMB::assembly_pitch));
360 142 : declareMeshProperty(RGMB::extruded, _extrude && _mesh_dimensions == 3);
361 71 : declareMeshProperty(RGMB::is_control_drum, true);
362 71 : declareMeshProperty(RGMB::drum_region_ids, _region_ids);
363 71 : declareMeshProperty(RGMB::drum_block_names, _block_names);
364 : std::vector<Real> drum_pad_angles =
365 81 : _has_pad_region ? std::vector<Real>({_pad_start_angle, _pad_end_angle}) : std::vector<Real>();
366 : declareMeshProperty(RGMB::drum_pad_angles, drum_pad_angles);
367 71 : std::vector<Real> drum_radii = std::vector<Real>({_drum_inner_radius, _drum_outer_radius});
368 : declareMeshProperty(RGMB::drum_radii, drum_radii);
369 71 : declareMeshProperty(RGMB::is_homogenized, false);
370 71 : declareMeshProperty(RGMB::is_single_pin, false);
371 71 : }
372 :
373 : std::unique_ptr<MeshBase>
374 62 : ControlDrumMeshGenerator::generate()
375 : {
376 : // Must be called to free the ReactorMeshParams mesh
377 62 : freeReactorMeshParams();
378 :
379 : // If bypass_mesh is true, return a null mesh. In this mode, an output mesh is not
380 : // generated and only metadata is defined on the generator, so logic related to
381 : // generation of output mesh will not be called
382 62 : if (getReactorParam<bool>(RGMB::bypass_meshgen))
383 : {
384 : auto null_mesh = nullptr;
385 : return null_mesh;
386 : }
387 :
388 : // This generate() method will be called once the subgenerators that we depend on are
389 : // called. This is where we reassign subdomain ids/name in case they were merged when
390 : // stitching pins into an assembly. This is also where we set region_id and
391 : // assembly_type_id element integers.
392 :
393 : // Define all extra element names and integers
394 62 : std::string plane_id_name = "plane_id";
395 62 : std::string region_id_name = "region_id";
396 62 : std::string pin_type_id_name = "pin_type_id";
397 62 : std::string assembly_type_id_name = "assembly_type_id";
398 : const std::string default_block_name =
399 62 : RGMB::DRUM_BLOCK_NAME_PREFIX + std::to_string(_assembly_type);
400 :
401 62 : auto pin_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), pin_type_id_name);
402 62 : auto region_id_int = getElemIntegerFromMesh(*(*_build_mesh), region_id_name);
403 62 : auto assembly_type_id_int = getElemIntegerFromMesh(*(*_build_mesh), assembly_type_id_name);
404 :
405 : unsigned int plane_id_int = 0;
406 62 : if (_extrude)
407 0 : plane_id_int = getElemIntegerFromMesh(*(*_build_mesh), plane_id_name, true);
408 :
409 : // Get next free block ID in mesh in case subdomain ids need to be remapped
410 62 : auto next_block_id = MooseMeshUtils::getNextFreeSubdomainID(*(*(_build_mesh)));
411 : std::map<std::string, SubdomainID> rgmb_name_id_map;
412 :
413 : // Loop through all mesh elements and set region ids and reassign block IDs/names
414 : // if they were merged during pin stitching
415 246272 : for (auto & elem : (*_build_mesh)->active_element_ptr_range())
416 : {
417 123074 : elem->set_extra_integer(assembly_type_id_int, _assembly_type);
418 123074 : const dof_id_type z_id = _extrude ? elem->get_extra_integer(plane_id_int) : 0;
419 :
420 : // Assembly peripheral element (background / duct), set subdomains according
421 : // to user preferences and set pin type id to RGMB::MAX_PIN_TYPE_ID - peripheral index
422 : // Region id is inferred from z_id and peripheral_idx
423 123074 : const auto base_block_id = elem->subdomain_id();
424 123074 : const auto base_block_name = (*_build_mesh)->subdomain_name(base_block_id);
425 :
426 : // Check if block name has correct prefix
427 246148 : std::string prefix = RGMB::DRUM_BLOCK_NAME_PREFIX + std::to_string(_assembly_type) + "_R";
428 123074 : if (!(base_block_name.find(prefix, 0) == 0))
429 : continue;
430 :
431 : // Radial index is integer value of substring after prefix
432 246148 : const unsigned int radial_idx = std::stoi(base_block_name.substr(prefix.length()));
433 :
434 : // Drum index distinguishes between elements in pad and ex-pad regions that have the same radial
435 : // index
436 : const unsigned int drum_idx =
437 123074 : getDrumIdxFromRadialIdx(radial_idx, elem->true_centroid()(0), elem->true_centroid()(1));
438 123074 : subdomain_id_type pin_type = RGMB::MAX_PIN_TYPE_ID - drum_idx;
439 123074 : elem->set_extra_integer(pin_type_id_int, pin_type);
440 :
441 123074 : const auto elem_rid = _region_ids[z_id][drum_idx];
442 123074 : elem->set_extra_integer(region_id_int, elem_rid);
443 :
444 : // Set element block name and block id
445 123074 : auto elem_block_name = default_block_name;
446 123074 : if (getReactorParam<bool>(RGMB::region_id_as_block_name))
447 246148 : elem_block_name += "_REG" + std::to_string(elem_rid);
448 0 : else if (_has_block_names)
449 0 : elem_block_name += "_" + _block_names[z_id][drum_idx];
450 123074 : if (elem->type() == TRI3 || elem->type() == PRISM6)
451 : elem_block_name += RGMB::TRI_BLOCK_NAME_SUFFIX;
452 246148 : updateElementBlockNameId(
453 123074 : *(*_build_mesh), elem, rgmb_name_id_map, elem_block_name, next_block_id);
454 62 : }
455 :
456 124 : if (getParam<bool>("generate_depletion_id"))
457 : {
458 14 : const MooseEnum option = getParam<MooseEnum>("depletion_id_type");
459 7 : addDepletionId(*(*_build_mesh), option, DepletionIDGenerationLevel::Drum, _extrude);
460 5 : }
461 :
462 : // Mark mesh as not prepared, as block ID's were re-assigned in this method
463 60 : (*_build_mesh)->set_isnt_prepared();
464 :
465 : return std::move(*_build_mesh);
466 : }
467 :
468 : unsigned int
469 123074 : ControlDrumMeshGenerator::getDrumIdxFromRadialIdx(const unsigned int radial_idx,
470 : const Real elem_x,
471 : const Real elem_y)
472 : {
473 : // By default, drum index will match radial index unless a pad region is defined
474 : unsigned int drum_idx = radial_idx;
475 123074 : if (_has_pad_region)
476 : {
477 89206 : if (radial_idx == 1)
478 : {
479 : // This is an element in the drum region, use drum angle to determine whether it belongs to
480 : // pad or ex-pad region Note: drum angle of 0 degrees starts in positive y-direction and
481 : // increments in clockwise direction, which is consistent
482 : // with how AdvancedConcentricCircleMeshGenerator defines azimuthal angles
483 5375 : Real drum_angle = std::atan2(elem_x, elem_y) * 180. / M_PI;
484 5375 : if (drum_angle < 0)
485 2685 : drum_angle += 360;
486 :
487 : // If _pad_end_angle does not exceed 360 degrees, check if drum angle lies within
488 : // _pad_start_angle and _pad_end_angle. Drum index needs to be incremented if element in
489 : // ex-pad region
490 5375 : if (_pad_end_angle <= 360)
491 : {
492 5375 : if ((drum_angle < _pad_start_angle) || (drum_angle > _pad_end_angle))
493 : ++drum_idx;
494 : }
495 : else
496 : {
497 : // If _pad_end_angle exceeds 360 degrees, check two intervals - _pad_start_angle to 360, and
498 : // 0 to _pad_end_angle - 360 Drum index needs to be incremented if element in ex-pad region
499 0 : if ((drum_angle < _pad_start_angle) && (drum_angle > _pad_end_angle - 360.))
500 : ++drum_idx;
501 : }
502 : }
503 83831 : else if (radial_idx == 2)
504 : {
505 : // Element is in outer drum region, drum index needs to be incremented to account for presence
506 : // of ex-pad region
507 : ++drum_idx;
508 : }
509 : }
510 123074 : return drum_idx;
511 : }
|