23 params.
addRequiredParam<MeshGeneratorName>(
"input",
"The input mesh to be modified.");
25 "old_blocks",
"The list of blocks in the input mesh that need to be modified.");
27 "new_block_ids",
"The block IDs to be used for the new selected azimuthal angle blocks.");
28 params.
addParam<std::vector<SubdomainName>>(
31 "The optional block names to be used for the new selected azimulathal angle blocks.");
32 params.
addParam<
bool>(
"preserve_volumes",
34 "Volume of concentric circles can be preserved using this function.");
36 "start_angle>=0.0 & start_angle<360.0",
37 "Starting azimuthal angle of the new block.");
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.");
50 _input_name(getParam<MeshGeneratorName>(
"input")),
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))
60 declareMeshProperty<Real>(
"pattern_pitch_meta", 0.0);
61 declareMeshProperty<bool>(
"is_control_drum_meta",
true);
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);
72 std::unique_ptr<MeshBase>
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");
79 ReplicatedMesh &
mesh = *replicated_mesh_ptr;
82 if (!
mesh.preparation().has_cached_elem_data)
83 mesh.cache_elem_data();
87 unsigned short order = 0;
88 std::map<dof_id_type, Point> vertex_avgs;
89 for (
const auto & elem :
mesh.element_ptr_range())
96 paramError(
"input",
"This mesh contains elements of different orders.");
104 paramError(
"input",
"This mesh contains elements of different orders.");
108 paramError(
"input",
"This mesh contains elements of unsupported type.");
110 vertex_avgs[elem->id()] = elem->vertex_average();
116 paramError(
"new_block_ids",
"This parameter must have the same size as old_blocks.");
119 std::set<SubdomainID> mesh_blocks;
120 mesh.subdomain_ids(mesh_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]);
131 getMeshProperty<subdomain_id_type>(
"quad_center_block_id",
_input_name)) !=
135 "This parameter contains a block that involves center quad elements, azimuthal splitting " 136 "is currently not supported in this case.");
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 =
147 if (i % 2 != is_first_value_vertex)
151 auto pattern_pitch_meta = std::max(getMeshProperty<Real>(
"pitch_meta",
_input_name),
152 getMeshProperty<Real>(
"pattern_pitch_meta",
_input_name));
155 Real radiusCorrectionFactor_original =
160 const Real azi_tol = 1.0E-6;
161 const Real rad_tol = 1.0e-6;
163 const Real side_angular_shift =
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;
178 original_start_down_it,
180 original_start_up_it,
181 azimuthal_angles_vtx);
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;
192 original_end_down_it,
195 azimuthal_angles_vtx);
197 Real azi_to_mod_start;
198 Real azi_to_keep_start;
207 original_start_down_it,
209 original_start_up_it,
214 Real azi_to_keep_end;
223 original_end_down_it,
229 if (azi_to_mod_end == azi_to_mod_start)
231 "The azimuthal intervals of the input mesh are too coarse for this parameter.");
233 const auto & side_list =
mesh.get_boundary_info().build_side_list();
236 bool external_block_change(
false);
237 for (
unsigned int i = 0; i < side_list.size(); i++)
241 dof_id_type block_id_tmp =
mesh.elem_ref(std::get<0>(side_list[i])).subdomain_id();
244 external_block_change =
true;
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();
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;
259 Real max_quad_elem_rad(0.0);
260 if (
mesh.elem_ref(0).n_vertices() == 4)
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()))
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()));
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;
302 non_circular_rad_list,
305 external_block_change,
311 non_circular_rad_list,
314 external_block_change,
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());
324 for (
const auto & elem :
mesh.element_ptr_range())
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))
330 if (elem->type() ==
TRI6 || elem->type() ==
TRI7)
333 *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
335 *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
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)) /
343 else if (elem->type() ==
QUAD8 || elem->type() ==
QUAD9)
346 *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
348 *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
350 *elem->node_ptr(2), *elem->node_ptr(3), max_circular_radius, rad_tol);
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)) /
362 std::vector<Real> new_azimuthal_angle;
363 for (
const auto & node_ptr :
as_range(
mesh.nodes_begin(),
mesh.nodes_end()))
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))
369 Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
370 new_azimuthal_angle.push_back(node_azi);
373 std::sort(new_azimuthal_angle.begin(), new_azimuthal_angle.end());
375 const bool is_first_value_vertex_mod =
378 Real radiusCorrectionFactor_mod =
386 if (min_non_circular_radius <
387 max_circular_radius / radiusCorrectionFactor_original * radiusCorrectionFactor_mod)
388 mooseError(
"In AzimuthalBlockSplitGenerator ",
390 ": the circular region is overlapped with background region after correction.");
393 Node & node = *node_ptr;
394 Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
396 if (node_rad > rad_tol && node_rad <= max_circular_radius + rad_tol)
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);
407 for (
unsigned int block_id_index = 0; block_id_index <
_old_block_ids.size(); block_id_index++)
412 auto p_cent = elem->true_centroid();
413 auto p_cent_azi = atan2(p_cent(1), p_cent(0)) * 180.0 / M_PI;
425 MeshTools::Modification::rotate(
mesh, -90.0, 0.0, 0.0);
428 mesh.clear_point_locator();
433 mesh.unset_has_cached_elem_data();
446 Real & original_down,
447 std::vector<Real>::iterator & original_down_it,
449 std::vector<Real>::iterator & original_up_it,
450 std::vector<Real> & azimuthal_angles_vtx)
454 std::lower_bound(azimuthal_angles_vtx.begin(), azimuthal_angles_vtx.end(), terminal_angle);
455 if (term_up == azimuthal_angles_vtx.begin())
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;
462 else if (term_up == azimuthal_angles_vtx.end())
464 original_down = azimuthal_angles_vtx.back();
465 original_down_it = azimuthal_angles_vtx.end() - 1;
467 original_up_it = azimuthal_angles_vtx.begin();
471 original_down = *(term_up - 1);
472 original_down_it = term_up - 1;
473 original_up = *term_up;
474 original_up_it = term_up;
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,
492 const Real half_interval = (original_up - original_down) / 2.0;
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)
500 azi_to_keep = original_down;
501 azi_to_mod = original_up;
502 *original_up_it = terminal_angle;
507 else if (std::abs((original_up + side_angular_shift) / side_angular_range -
508 std::round((original_up + side_angular_shift) / side_angular_range)) <
510 std::abs(original_up - terminal_angle) > azi_tol)
512 azi_to_keep = original_up;
513 azi_to_mod = original_down;
514 *original_down_it = terminal_angle;
517 else if (terminal_angle - original_down > half_interval)
519 azi_to_keep = original_down;
520 azi_to_mod = original_up;
521 *original_up_it = terminal_angle;
526 azi_to_keep = original_up;
527 azi_to_mod = original_down;
528 *original_down_it = terminal_angle;
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)
544 for (
unsigned int i = 0; i < node_id_mod.size(); i++)
547 if (std::abs(node_id_mod[i].first - node_id_keep[i].first) < rad_tol)
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);
556 non_circular_rad_list.push_back(node_id_mod[i].first);
560 if (std::find(node_list.begin(),
562 std::make_tuple(node_id_mod[i].second,
OUTER_SIDESET_ID)) == node_list.end() ||
563 external_block_change)
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);
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;
582 const Point vertex_1,
583 const Real max_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));
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;
595 return (vertex_0 + vertex_1) / 2.0;
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.
T & setMeshProperty(const std::string &data_name, Args &&... args)
const std::string & _name
const Real _angle_range
Angular range of the modified azimuthal blocks.
void paramError(const std::string ¶m, Args... args) const
const T & getParam(const std::string &name) const
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.
std::vector< unsigned int > _num_sectors_per_side_meta
MeshMetaData: number of mesh sectors of each polygon side.
std::vector< subdomain_id_type > getSubdomainIDs(const libMesh::MeshBase &mesh, const std::vector< SubdomainName > &subdomain_name)
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::vector< Real > & _azimuthal_angle_meta
MeshMetaData: vector of all nodes' azimuthal angles.
This AzimuthalBlockSplitGenerator object takes in a polygon/hexagon concentric circle mesh and rename...
const SubdomainID INVALID_BLOCK_ID
static InputParameters validParams()
std::unique_ptr< MeshBase > generate() override
std::unique_ptr< MeshBase > & _input
Reference to input mesh pointer.
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
const bool _preserve_volumes
Whether volumes of ring regions need to be preserved.
const MeshGeneratorName _input_name
Input mesh to be modified.
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(...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const std::vector< SubdomainName > _new_block_names
Names of the new azimuthal blocks.
registerMooseObject("ReactorApp", AzimuthalBlockSplitGenerator)
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::vector< subdomain_id_type > _old_block_ids
IDs to identify the input mesh blocks to be modified.
A base class that contains common members for Reactor module mesh generators.
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
AzimuthalBlockSplitGenerator(const InputParameters ¶meters)
Real _start_angle
Starting angular position of the modified azimuthal blocks.
Real _end_angle
Ending angular position of the modified azimuthal blocks.
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.
const std::vector< subdomain_id_type > _new_block_ids
IDs of the new azimuthal blocks.
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.