https://mooseframework.inl.gov
Loading...
Searching...
No Matches
AzimuthalBlockSplitGenerator.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
11#include "MooseMeshUtils.h"
13
14// C++ includes
15#include <cmath> // provides round, not std::round (see http://www.cplusplus.com/reference/cmath/round/)
16
18
21{
23 params.addRequiredParam<MeshGeneratorName>("input", "The input mesh to be modified.");
24 params.addRequiredParam<std::vector<SubdomainName>>(
25 "old_blocks", "The list of blocks in the input mesh that need to be modified.");
26 params.addRequiredParam<std::vector<subdomain_id_type>>(
27 "new_block_ids", "The block IDs to be used for the new selected azimuthal angle blocks.");
28 params.addParam<std::vector<SubdomainName>>(
29 "new_block_names",
30 {},
31 "The optional block names to be used for the new selected azimulathal angle blocks.");
32 params.addParam<bool>("preserve_volumes",
33 true,
34 "Volume of concentric circles can be preserved using this function.");
35 params.addRequiredRangeCheckedParam<Real>("start_angle",
36 "start_angle>=0.0 & start_angle<360.0",
37 "Starting azimuthal angle of the new block.");
38 params.addRequiredRangeCheckedParam<Real>("angle_range",
39 "angle_range>0.0 & angle_range<=180.0",
40 "Azimuthal angle range of the new block.");
42 "This AzimuthalBlockSplitGenerator object takes in a polygon/hexagon concentric circle mesh "
43 "and renames blocks on a user-defined azimuthal segment / wedge of the mesh.");
44
45 return params;
46}
47
49 : PolygonMeshGeneratorBase(parameters),
50 _input_name(getParam<MeshGeneratorName>("input")),
51 _new_block_ids(getParam<std::vector<subdomain_id_type>>("new_block_ids")),
52 _new_block_names(getParam<std::vector<SubdomainName>>("new_block_names")),
53 _preserve_volumes(getParam<bool>("preserve_volumes")),
54 _start_angle(getParam<Real>("start_angle") + 90.0),
55 _angle_range(getParam<Real>("angle_range")),
56 _azimuthal_angle_meta(
57 declareMeshProperty<std::vector<Real>>("azimuthal_angle_meta", std::vector<Real>())),
58 _input(getMeshByName(_input_name))
59{
60 declareMeshProperty<Real>("pattern_pitch_meta", 0.0);
61 declareMeshProperty<bool>("is_control_drum_meta", true);
62 if (!_new_block_names.empty() && _new_block_names.size() != _new_block_ids.size())
63 paramError("new_block_names",
64 "This parameter, if provided, must have the same size as new_block_ids.");
66 getMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", _input_name);
70}
71
72std::unique_ptr<MeshBase>
74{
75 auto replicated_mesh_ptr = dynamic_cast<ReplicatedMesh *>(_input.get());
76 if (!replicated_mesh_ptr)
77 paramError("input", "Input is not a replicated mesh, which is required");
78
79 ReplicatedMesh & mesh = *replicated_mesh_ptr;
80
81 // We'll be querying the mesh for subdomain ids
82 if (!mesh.preparation().has_cached_elem_data)
83 mesh.cache_elem_data();
84
85 // Check the order of the input mesh's elements
86 // Meanwhile, record the vertex average of each element for future comparison
87 unsigned short order = 0;
88 std::map<dof_id_type, Point> vertex_avgs;
89 for (const auto & elem : mesh.element_ptr_range())
90 {
91 switch (elem->type())
92 {
93 case TRI3:
94 case QUAD4:
95 if (order == 2)
96 paramError("input", "This mesh contains elements of different orders.");
97 order = 1;
98 break;
99 case TRI6:
100 case TRI7:
101 case QUAD8:
102 case QUAD9:
103 if (order == 1)
104 paramError("input", "This mesh contains elements of different orders.");
105 order = 2;
106 break;
107 default:
108 paramError("input", "This mesh contains elements of unsupported type.");
109 }
110 vertex_avgs[elem->id()] = elem->vertex_average();
111 }
112
114 MooseMeshUtils::getSubdomainIDs(mesh, getParam<std::vector<SubdomainName>>("old_blocks"));
115 if (_old_block_ids.size() != _new_block_ids.size())
116 paramError("new_block_ids", "This parameter must have the same size as old_blocks.");
117
118 // Check that the block ids/names exist in the mesh
119 std::set<SubdomainID> mesh_blocks;
120 mesh.subdomain_ids(mesh_blocks);
121
122 for (unsigned int i = 0; i < _old_block_ids.size(); i++)
123 if (_old_block_ids[i] == Moose::INVALID_BLOCK_ID || !mesh_blocks.count(_old_block_ids[i]))
124 paramError("old_blocks",
125 "This parameter contains blocks that do not exist in the input mesh. Error "
126 "triggered by block: " +
127 getParam<std::vector<SubdomainName>>("old_blocks")[i]);
128
129 if (std::find(_old_block_ids.begin(),
130 _old_block_ids.end(),
131 getMeshProperty<subdomain_id_type>("quad_center_block_id", _input_name)) !=
132 _old_block_ids.end())
134 "old_blocks",
135 "This parameter contains a block that involves center quad elements, azimuthal splitting "
136 "is currently not supported in this case.");
137
138 MeshTools::Modification::rotate(mesh, 90.0, 0.0, 0.0);
140 std::vector<Real> azimuthal_angles_vtx;
141 const bool is_first_value_vertex =
142 order == 1 ? true : MooseUtils::absoluteFuzzyEqual(_azimuthal_angle_meta[0], -180.0);
143 if (order == 1)
144 azimuthal_angles_vtx = _azimuthal_angle_meta;
145 else
146 for (const auto & i : make_range(_azimuthal_angle_meta.size()))
147 if (i % 2 != is_first_value_vertex)
148 azimuthal_angles_vtx.push_back(_azimuthal_angle_meta[i]);
149
150 // So that this class works for both derived classes of PolygonMeshGeneratorBase
151 auto pattern_pitch_meta = std::max(getMeshProperty<Real>("pitch_meta", _input_name),
152 getMeshProperty<Real>("pattern_pitch_meta", _input_name));
153 setMeshProperty("pattern_pitch_meta", pattern_pitch_meta);
154
155 Real radiusCorrectionFactor_original =
157 _azimuthal_angle_meta, true, order, is_first_value_vertex)
158 : 1.0;
159
160 const Real azi_tol = 1.0E-6;
161 const Real rad_tol = 1.0e-6;
162 const Real side_angular_range = 360.0 / (Real)_num_sectors_per_side_meta.size();
163 const Real side_angular_shift =
164 _num_sectors_per_side_meta.size() % 2 == 0 ? 0.0 : side_angular_range / 2.0;
165
167 _end_angle = _end_angle > 180.0 ? _end_angle - 360.0 : _end_angle;
168
169 Real original_start_down;
170 std::vector<Real>::iterator original_start_down_it;
171 Real original_start_up;
172 std::vector<Real>::iterator original_start_up_it;
173
174 // Identify the mesh azimuthal angles of the elements that need to be modified for the starting
175 // edge of the absorber region.
177 original_start_down,
178 original_start_down_it,
179 original_start_up,
180 original_start_up_it,
181 azimuthal_angles_vtx);
182
183 Real original_end_down;
184 std::vector<Real>::iterator original_end_down_it;
185 Real original_end_up;
186 std::vector<Real>::iterator original_end_up_it;
187
188 // Identify the mesh azimuthal angles of the elements that need to be modified for the ending
189 // edge of the absorber region.
191 original_end_down,
192 original_end_down_it,
193 original_end_up,
194 original_end_up_it,
195 azimuthal_angles_vtx);
196
197 Real azi_to_mod_start;
198 Real azi_to_keep_start;
199
200 // For the two mesh azimuthal angles identified, determine which need to be moved to the starting
201 // edge position.
202 angleModifier(side_angular_shift,
203 side_angular_range,
204 azi_tol,
206 original_start_down,
207 original_start_down_it,
208 original_start_up,
209 original_start_up_it,
210 azi_to_keep_start,
211 azi_to_mod_start);
212
213 Real azi_to_mod_end;
214 Real azi_to_keep_end;
215
216 // For the two mesh azimuthal angles identified, determine which need to be moved to the ending
217 // edge position.
218 angleModifier(side_angular_shift,
219 side_angular_range,
220 azi_tol,
222 original_end_down,
223 original_end_down_it,
224 original_end_up,
225 original_end_up_it,
226 azi_to_keep_end,
227 azi_to_mod_end);
228
229 if (azi_to_mod_end == azi_to_mod_start)
230 paramError("angle_range",
231 "The azimuthal intervals of the input mesh are too coarse for this parameter.");
232
233 const auto & side_list = mesh.get_boundary_info().build_side_list();
234
235 // See if the block that contains the external boundary is modified or not
236 bool external_block_change(false);
237 for (unsigned int i = 0; i < side_list.size(); i++)
238 {
239 if (std::get<2>(side_list[i]) == OUTER_SIDESET_ID)
240 {
241 dof_id_type block_id_tmp = mesh.elem_ref(std::get<0>(side_list[i])).subdomain_id();
242 if (std::find(_old_block_ids.begin(), _old_block_ids.end(), block_id_tmp) !=
243 _old_block_ids.end())
244 external_block_change = true;
245 }
246 }
247
248 mesh.get_boundary_info().build_node_list_from_side_list();
249 std::vector<std::tuple<dof_id_type, boundary_id_type>> node_list =
250 mesh.get_boundary_info().build_node_list();
251
252 std::vector<std::pair<Real, dof_id_type>> node_id_keep_start;
253 std::vector<std::pair<Real, dof_id_type>> node_id_mod_start;
254 std::vector<std::pair<Real, dof_id_type>> node_id_keep_end;
255 std::vector<std::pair<Real, dof_id_type>> node_id_mod_end;
256
257 // Determine which nodes are involved in the elements that are intercepted by the starting/ending
258 // angles
259 Real max_quad_elem_rad(0.0);
260 if (mesh.elem_ref(0).n_vertices() == 4)
261 for (dof_id_type i = 0;
262 i < (_num_sectors_per_side_meta[0] / 2 + 1) * (_num_sectors_per_side_meta[0] / 2 + 1);
263 i++)
264 max_quad_elem_rad =
265 max_quad_elem_rad < mesh.node_ref(i).norm() ? mesh.node_ref(i).norm() : max_quad_elem_rad;
266 for (const auto & node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
267 {
268 const Node & node = *node_ptr;
269 Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
270 Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
271 if (node_rad > max_quad_elem_rad + rad_tol &&
272 (std::abs(node_azi - azi_to_mod_start) < azi_tol ||
273 std::abs(std::abs(node_azi - azi_to_mod_start) - 360.0) < azi_tol))
274 node_id_mod_start.push_back(std::make_pair(node_rad, node.id()));
275 if (node_rad > max_quad_elem_rad + rad_tol &&
276 (std::abs(node_azi - azi_to_keep_start) < azi_tol ||
277 std::abs(std::abs(node_azi - azi_to_keep_start) - 360.0) < azi_tol))
278 node_id_keep_start.push_back(std::make_pair(node_rad, node.id()));
279 if (node_rad > max_quad_elem_rad + rad_tol &&
280 (std::abs(node_azi - azi_to_mod_end) < azi_tol ||
281 std::abs(std::abs(node_azi - azi_to_mod_end) - 360.0) < azi_tol))
282 node_id_mod_end.push_back(std::make_pair(node_rad, node.id()));
283 if (node_rad > max_quad_elem_rad + rad_tol &&
284 (std::abs(node_azi - azi_to_keep_end) < azi_tol ||
285 std::abs(std::abs(node_azi - azi_to_keep_end) - 360.0) < azi_tol))
286 node_id_keep_end.push_back(std::make_pair(node_rad, node.id()));
287 }
288 // Sort the involved nodes using radius as the key; this facilitates the determination of circular
289 // regions nodes
290 std::sort(node_id_mod_start.begin(), node_id_mod_start.end());
291 std::sort(node_id_keep_start.begin(), node_id_keep_start.end());
292 std::sort(node_id_mod_end.begin(), node_id_mod_end.end());
293 std::sort(node_id_keep_end.begin(), node_id_keep_end.end());
294 std::vector<Real> circular_rad_list;
295 std::vector<Real> non_circular_rad_list;
296
297 // Modify the nodes with the azimuthal angles identified before.
299 node_id_mod_start,
300 node_id_keep_start,
301 circular_rad_list,
302 non_circular_rad_list,
303 node_list,
305 external_block_change,
306 rad_tol);
308 node_id_mod_end,
309 node_id_keep_end,
310 circular_rad_list,
311 non_circular_rad_list,
312 node_list,
314 external_block_change,
315 rad_tol);
316
317 const Real max_circular_radius =
318 *std::max_element(circular_rad_list.begin(), circular_rad_list.end());
319 const Real min_non_circular_radius =
320 *std::min_element(non_circular_rad_list.begin(), non_circular_rad_list.end());
321
322 // Before re-correcting the radii, correct the mid-point of the elements that are altered
323 if (order == 2)
324 for (const auto & elem : mesh.element_ptr_range())
325 {
326 const Point & original_vertex_avg = vertex_avgs[elem->id()];
327 const Point & new_vertex_avg = elem->vertex_average();
328 if (MooseUtils::absoluteFuzzyGreaterThan((original_vertex_avg - new_vertex_avg).norm(), 0.0))
329 {
330 if (elem->type() == TRI6 || elem->type() == TRI7)
331 {
332 *elem->node_ptr(3) = midPointCorrector(
333 *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
334 *elem->node_ptr(4) = midPointCorrector(
335 *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
336 *elem->node_ptr(5) = midPointCorrector(
337 *elem->node_ptr(2), *elem->node_ptr(0), max_circular_radius, rad_tol);
338 if (elem->type() == TRI7)
339 *elem->node_ptr(6) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
340 *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5)) /
341 6.0;
342 }
343 else if (elem->type() == QUAD8 || elem->type() == QUAD9)
344 {
345 *elem->node_ptr(4) = midPointCorrector(
346 *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
347 *elem->node_ptr(5) = midPointCorrector(
348 *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
349 *elem->node_ptr(6) = midPointCorrector(
350 *elem->node_ptr(2), *elem->node_ptr(3), max_circular_radius, rad_tol);
351 *elem->node_ptr(7) = midPointCorrector(
352 *elem->node_ptr(3), *elem->node_ptr(0), max_circular_radius, rad_tol);
353 if (elem->type() == QUAD9)
354 *elem->node_ptr(8) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
355 *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5) +
356 *elem->node_ptr(6) + *elem->node_ptr(7)) /
357 8.0;
358 }
359 }
360 }
361
362 std::vector<Real> new_azimuthal_angle;
363 for (const auto & node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
364 {
365 const Node & node = *node_ptr;
366 if (MooseUtils::absoluteFuzzyEqual(
367 std::sqrt(node(0) * node(0) + node(1) * node(1)), max_circular_radius, rad_tol))
368 {
369 Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
370 new_azimuthal_angle.push_back(node_azi);
371 }
372 }
373 std::sort(new_azimuthal_angle.begin(), new_azimuthal_angle.end());
374 _azimuthal_angle_meta = new_azimuthal_angle;
375 const bool is_first_value_vertex_mod =
376 order == 1 ? true : MooseUtils::absoluteFuzzyEqual(_azimuthal_angle_meta[0], -180.0);
377
378 Real radiusCorrectionFactor_mod =
380 _azimuthal_angle_meta, true, order, is_first_value_vertex_mod)
381 : 1.0;
382
383 // Re-correct Radii
385 {
386 if (min_non_circular_radius <
387 max_circular_radius / radiusCorrectionFactor_original * radiusCorrectionFactor_mod)
388 mooseError("In AzimuthalBlockSplitGenerator ",
389 _name,
390 ": the circular region is overlapped with background region after correction.");
391 for (Node * node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
392 {
393 Node & node = *node_ptr;
394 Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
395 // Any nodes with radii smaller than the threshold belong to circular regions
396 if (node_rad > rad_tol && node_rad <= max_circular_radius + rad_tol)
397 {
398 const Real node_azi = atan2(node(1), node(0));
399 const Real node_rad_corr =
400 node_rad / radiusCorrectionFactor_original * radiusCorrectionFactor_mod;
401 node(0) = node_rad_corr * std::cos(node_azi);
402 node(1) = node_rad_corr * std::sin(node_azi);
403 }
404 }
405 }
406 // Assign New Block IDs
407 for (unsigned int block_id_index = 0; block_id_index < _old_block_ids.size(); block_id_index++)
408 for (auto & elem :
409 as_range(mesh.active_subdomain_elements_begin(_old_block_ids[block_id_index]),
410 mesh.active_subdomain_elements_end(_old_block_ids[block_id_index])))
411 {
412 auto p_cent = elem->true_centroid();
413 auto p_cent_azi = atan2(p_cent(1), p_cent(0)) * 180.0 / M_PI;
414 if (_start_angle < _end_angle && p_cent_azi >= _start_angle && p_cent_azi <= _end_angle)
415 elem->subdomain_id() = _new_block_ids[block_id_index];
416 else if (_start_angle > _end_angle &&
417 (p_cent_azi >= _start_angle || p_cent_azi <= _end_angle))
418 elem->subdomain_id() = _new_block_ids[block_id_index];
419 }
420
421 // Assign new Block Names if applicable
422 for (unsigned int i = 0; i < _new_block_names.size(); i++)
423 mesh.set_subdomain_name(_new_block_ids[i], _new_block_names[i]);
424
425 MeshTools::Modification::rotate(mesh, -90.0, 0.0, 0.0);
426
427 // Workaround - the point locator clear should be done in rotate()
428 mesh.clear_point_locator();
429
430 // The rotate() could unset this (because spatial_dimension() can be
431 // changed) but let's unset to be sure our subdomain changes get
432 // recached.
433 mesh.unset_has_cached_elem_data();
434
435 for (unsigned int i = 0; i < _azimuthal_angle_meta.size(); i++)
436 _azimuthal_angle_meta[i] = (_azimuthal_angle_meta[i] - 90.0 <= -180.0)
437 ? (_azimuthal_angle_meta[i] + 270.0)
438 : _azimuthal_angle_meta[i] - 90.0;
439 std::sort(_azimuthal_angle_meta.begin(), _azimuthal_angle_meta.end());
440
441 return std::move(_input);
442}
443
444void
446 Real & original_down,
447 std::vector<Real>::iterator & original_down_it,
448 Real & original_up,
449 std::vector<Real>::iterator & original_up_it,
450 std::vector<Real> & azimuthal_angles_vtx)
451{
452
453 auto term_up =
454 std::lower_bound(azimuthal_angles_vtx.begin(), azimuthal_angles_vtx.end(), terminal_angle);
455 if (term_up == azimuthal_angles_vtx.begin())
456 {
457 original_up = *term_up;
458 original_up_it = term_up;
459 original_down = -180.0;
460 original_down_it = azimuthal_angles_vtx.end() - 1;
461 }
462 else if (term_up == azimuthal_angles_vtx.end())
463 {
464 original_down = azimuthal_angles_vtx.back();
465 original_down_it = azimuthal_angles_vtx.end() - 1;
466 original_up = 180.0;
467 original_up_it = azimuthal_angles_vtx.begin();
468 }
469 else
470 {
471 original_down = *(term_up - 1);
472 original_down_it = term_up - 1;
473 original_up = *term_up;
474 original_up_it = term_up;
475 }
476}
477
478void
479AzimuthalBlockSplitGenerator::angleModifier(const Real & side_angular_shift,
480 const Real & side_angular_range,
481 const Real & azi_tol,
482 const Real & terminal_angle,
483 const Real & original_down,
484 std::vector<Real>::iterator & original_down_it,
485 const Real & original_up,
486 std::vector<Real>::iterator & original_up_it,
487 Real & azi_to_keep,
488 Real & azi_to_mod)
489{
490 // Half interval is used to help determine which of the two identified azimuthal angles needs to
491 // be moved.
492 const Real half_interval = (original_up - original_down) / 2.0;
493 // In this case, the lower azimuthal angle matches a vertex position, while the target angle is
494 // not overlapped with the same vertex position. Thus the lower azimuthal angle is not moved
495 // anyway.
496 if (std::abs((original_down + side_angular_shift) / side_angular_range -
497 std::round((original_down + side_angular_shift) / side_angular_range)) < azi_tol &&
498 std::abs(original_down - terminal_angle) > azi_tol)
499 {
500 azi_to_keep = original_down;
501 azi_to_mod = original_up;
502 *original_up_it = terminal_angle;
503 }
504 // In this case, the upper azimuthal angle matches a vertex position, while the target angle is
505 // not overlapped with the same vertex position. Thus the upper azimuthal angle is not moved
506 // anyway.
507 else if (std::abs((original_up + side_angular_shift) / side_angular_range -
508 std::round((original_up + side_angular_shift) / side_angular_range)) <
509 azi_tol &&
510 std::abs(original_up - terminal_angle) > azi_tol)
511 {
512 azi_to_keep = original_up;
513 azi_to_mod = original_down;
514 *original_down_it = terminal_angle;
515 }
516 // Move upper azimuthal angle as it is closer to the target angle.
517 else if (terminal_angle - original_down > half_interval)
518 {
519 azi_to_keep = original_down;
520 azi_to_mod = original_up;
521 *original_up_it = terminal_angle;
522 }
523 // Move lower azimuthal angle as it is closer to the target angle.
524 else
525 {
526 azi_to_keep = original_up;
527 azi_to_mod = original_down;
528 *original_down_it = terminal_angle;
529 }
530}
531
532void
534 ReplicatedMesh & mesh,
535 const std::vector<std::pair<Real, dof_id_type>> & node_id_mod,
536 const std::vector<std::pair<Real, dof_id_type>> & node_id_keep,
537 std::vector<Real> & circular_rad_list,
538 std::vector<Real> & non_circular_rad_list,
539 const std::vector<std::tuple<dof_id_type, boundary_id_type>> & node_list,
540 const Real & term_angle,
541 const bool & external_block_change,
542 const Real & rad_tol)
543{
544 for (unsigned int i = 0; i < node_id_mod.size(); i++)
545 {
546 // Circular regions, radius is not altered
547 if (std::abs(node_id_mod[i].first - node_id_keep[i].first) < rad_tol)
548 {
549 Node & node_mod = mesh.node_ref(node_id_mod[i].second);
550 circular_rad_list.push_back(node_id_mod[i].first);
551 node_mod(0) = node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0);
552 node_mod(1) = node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0);
553 }
554 else
555 {
556 non_circular_rad_list.push_back(node_id_mod[i].first);
557 // For external boundary nodes, if the external block is not modified, the boundary nodes are
558 // not moved.
559 // Non-circular range, use intercept instead
560 if (std::find(node_list.begin(),
561 node_list.end(),
562 std::make_tuple(node_id_mod[i].second, OUTER_SIDESET_ID)) == node_list.end() ||
563 external_block_change)
564 {
565 Node & node_mod = mesh.node_ref(node_id_mod[i].second);
566 const Node & node_keep = mesh.node_ref(node_id_keep[i].second);
567 std::pair<Real, Real> pair_tmp = fourPointIntercept(
568 std::make_pair(node_mod(0), node_mod(1)),
569 std::make_pair(node_keep(0), node_keep(1)),
570 std::make_pair(0.0, 0.0),
571 std::make_pair(2.0 * node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0),
572 2.0 * node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0)));
573 node_mod(0) = pair_tmp.first;
574 node_mod(1) = pair_tmp.second;
575 }
576 }
577 }
578}
579
580Point
582 const Point vertex_1,
583 const Real max_radius,
584 const Real rad_tol)
585{
586 // Check if two vertices have the same radius
587 const Real r_vertex_0 = std::sqrt(vertex_0(0) * vertex_0(0) + vertex_0(1) * vertex_0(1));
588 const Real r_vertex_1 = std::sqrt(vertex_1(0) * vertex_1(0) + vertex_1(1) * vertex_1(1));
589 // If both vertices have the same radius and they are located in the circular region,
590 // their midpoint should have the same radius and has the average azimuthal angle of the two
591 // vertices.
592 if (std::abs(r_vertex_0 - r_vertex_1) < rad_tol && r_vertex_0 < max_radius + rad_tol)
593 return (vertex_0 + vertex_1).unit() * r_vertex_0;
594 else
595 return (vertex_0 + vertex_1) / 2.0;
596}
registerMooseObject("ReactorApp", AzimuthalBlockSplitGenerator)
This AzimuthalBlockSplitGenerator object takes in a polygon/hexagon concentric circle mesh and rename...
Real _end_angle
Ending angular position of the modified azimuthal blocks.
std::vector< unsigned int > _num_sectors_per_side_meta
MeshMetaData: number of mesh sectors of each polygon side.
const bool _preserve_volumes
Whether volumes of ring regions need to be preserved.
AzimuthalBlockSplitGenerator(const InputParameters &parameters)
Real _start_angle
Starting angular position of the modified azimuthal blocks.
void angleIdentifier(const Real &terminal_angle, Real &original_down, std::vector< Real >::iterator &original_down_it, Real &original_up, std::vector< Real >::iterator &original_up_it, std::vector< Real > &azimuthal_angles_vtx)
Finds the azimuthal angles of the original mesh that need to be modified to match the edge locations ...
std::unique_ptr< MeshBase > generate() override
void nodeModifier(ReplicatedMesh &mesh, const std::vector< std::pair< Real, dof_id_type > > &node_id_mod, const std::vector< std::pair< Real, dof_id_type > > &node_id_keep, std::vector< Real > &circular_rad_list, std::vector< Real > &non_circular_rad_list, const std::vector< std::tuple< dof_id_type, boundary_id_type > > &node_list, const Real &term_angle, const bool &external_block_change, const Real &rad_tol)
Modifies the nodes with the azimuthal angles modified in AzimuthalBlockSplitGenerator::angleModifier(...
const std::vector< subdomain_id_type > _new_block_ids
IDs of the new azimuthal blocks.
std::vector< subdomain_id_type > _old_block_ids
IDs to identify the input mesh blocks to be modified.
std::vector< Real > & _azimuthal_angle_meta
MeshMetaData: vector of all nodes' azimuthal angles.
const std::vector< SubdomainName > _new_block_names
Names of the new azimuthal blocks.
const Real _angle_range
Angular range of the modified azimuthal blocks.
const MeshGeneratorName _input_name
Input mesh to be modified.
std::unique_ptr< MeshBase > & _input
Reference to input mesh pointer.
void angleModifier(const Real &side_angular_shift, const Real &side_angular_range, const Real &azi_tol, const Real &terminal_angle, const Real &original_down, std::vector< Real >::iterator &original_down_it, const Real &original_up, std::vector< Real >::iterator &original_up_it, Real &azi_to_keep, Real &azi_to_mod)
Modifies the azimuthal angle to perform move the edge of the control drum during rotation.
Point midPointCorrector(const Point vertex_0, const Point vertex_1, const Real max_radius, const Real rad_tol)
Calculate the corrected midpoint position of the element with vertices moved.
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
T & setMeshProperty(const std::string &data_name, Args &&... args)
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
const std::string & _name
const T & getParam(const std::string &name) const
A base class that contains common members for Reactor module mesh generators.
std::vector< Real > azimuthalAnglesCollector(ReplicatedMesh &mesh, std::vector< Point > &boundary_points, const Real lower_azi=-30.0, const Real upper_azi=30.0, const unsigned int return_type=ANGLE_TANGENT, const unsigned int num_sides=6, const boundary_id_type bid=OUTER_SIDESET_ID, const bool calculate_origin=true, const Real input_origin_x=0.0, const Real input_origin_y=0.0, const Real tol=1.0E-10) const
Collects sorted azimuthal angles of the external boundary.
static InputParameters validParams()
std::pair< Real, Real > fourPointIntercept(const std::pair< Real, Real > &p1, const std::pair< Real, Real > &p2, const std::pair< Real, Real > &p3, const std::pair< Real, Real > &p4) const
Finds the center of a quadrilateral based on four vertices.
MeshBase & mesh
std::vector< subdomain_id_type > getSubdomainIDs(const libMesh::MeshBase &mesh, const std::vector< SubdomainName > &subdomain_name)
const SubdomainID INVALID_BLOCK_ID
Real radiusCorrectionFactor(const std::vector< Real > &azimuthal_list, const bool full_circle=true, const unsigned int order=1, const bool is_first_value_vertex=true)
Makes radial correction to preserve ring area.