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;
83 unsigned short order = 0;
84 std::map<dof_id_type, Point> vertex_avgs;
85 for (
const auto & elem :
mesh.element_ptr_range())
92 paramError(
"input",
"This mesh contains elements of different orders.");
100 paramError(
"input",
"This mesh contains elements of different orders.");
104 paramError(
"input",
"This mesh contains elements of unsupported type.");
106 vertex_avgs[elem->id()] = elem->vertex_average();
112 paramError(
"new_block_ids",
"This parameter must have the same size as old_blocks.");
115 std::set<SubdomainID> mesh_blocks;
116 mesh.subdomain_ids(mesh_blocks);
121 "This parameter contains blocks that do not exist in the input mesh. Error " 122 "triggered by block: " +
123 getParam<std::vector<SubdomainName>>(
"old_blocks")[i]);
127 getMeshProperty<subdomain_id_type>(
"quad_center_block_id",
_input_name)) !=
131 "This parameter contains a block that involves center quad elements, azimuthal splitting " 132 "is currently not supported in this case.");
134 MeshTools::Modification::rotate(
mesh, 90.0, 0.0, 0.0);
136 std::vector<Real> azimuthal_angles_vtx;
137 const bool is_first_value_vertex =
143 if (i % 2 != is_first_value_vertex)
147 auto pattern_pitch_meta = std::max(getMeshProperty<Real>(
"pitch_meta",
_input_name),
148 getMeshProperty<Real>(
"pattern_pitch_meta",
_input_name));
151 Real radiusCorrectionFactor_original =
156 const Real azi_tol = 1.0E-6;
157 const Real rad_tol = 1.0e-6;
159 const Real side_angular_shift =
165 Real original_start_down;
166 std::vector<Real>::iterator original_start_down_it;
167 Real original_start_up;
168 std::vector<Real>::iterator original_start_up_it;
174 original_start_down_it,
176 original_start_up_it,
177 azimuthal_angles_vtx);
179 Real original_end_down;
180 std::vector<Real>::iterator original_end_down_it;
181 Real original_end_up;
182 std::vector<Real>::iterator original_end_up_it;
188 original_end_down_it,
191 azimuthal_angles_vtx);
193 Real azi_to_mod_start;
194 Real azi_to_keep_start;
203 original_start_down_it,
205 original_start_up_it,
210 Real azi_to_keep_end;
219 original_end_down_it,
225 if (azi_to_mod_end == azi_to_mod_start)
227 "The azimuthal intervals of the input mesh are too coarse for this parameter.");
229 const auto & side_list =
mesh.get_boundary_info().build_side_list();
232 bool external_block_change(
false);
233 for (
unsigned int i = 0; i < side_list.size(); i++)
237 dof_id_type block_id_tmp =
mesh.elem_ref(std::get<0>(side_list[i])).subdomain_id();
240 external_block_change =
true;
244 mesh.get_boundary_info().build_node_list_from_side_list();
245 std::vector<std::tuple<dof_id_type, boundary_id_type>> node_list =
246 mesh.get_boundary_info().build_node_list();
248 std::vector<std::pair<Real, dof_id_type>> node_id_keep_start;
249 std::vector<std::pair<Real, dof_id_type>> node_id_mod_start;
250 std::vector<std::pair<Real, dof_id_type>> node_id_keep_end;
251 std::vector<std::pair<Real, dof_id_type>> node_id_mod_end;
255 Real max_quad_elem_rad(0.0);
256 if (
mesh.elem_ref(0).n_vertices() == 4)
261 max_quad_elem_rad <
mesh.node_ref(i).norm() ?
mesh.node_ref(i).norm() : max_quad_elem_rad;
262 for (
const auto & node_ptr :
as_range(
mesh.nodes_begin(),
mesh.nodes_end()))
264 const Node & node = *node_ptr;
265 Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
266 Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
267 if (node_rad > max_quad_elem_rad + rad_tol &&
268 (std::abs(node_azi - azi_to_mod_start) < azi_tol ||
269 std::abs(std::abs(node_azi - azi_to_mod_start) - 360.0) < azi_tol))
270 node_id_mod_start.push_back(std::make_pair(node_rad, node.id()));
271 if (node_rad > max_quad_elem_rad + rad_tol &&
272 (std::abs(node_azi - azi_to_keep_start) < azi_tol ||
273 std::abs(std::abs(node_azi - azi_to_keep_start) - 360.0) < azi_tol))
274 node_id_keep_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_mod_end) < azi_tol ||
277 std::abs(std::abs(node_azi - azi_to_mod_end) - 360.0) < azi_tol))
278 node_id_mod_end.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_keep_end) < azi_tol ||
281 std::abs(std::abs(node_azi - azi_to_keep_end) - 360.0) < azi_tol))
282 node_id_keep_end.push_back(std::make_pair(node_rad, node.id()));
286 std::sort(node_id_mod_start.begin(), node_id_mod_start.end());
287 std::sort(node_id_keep_start.begin(), node_id_keep_start.end());
288 std::sort(node_id_mod_end.begin(), node_id_mod_end.end());
289 std::sort(node_id_keep_end.begin(), node_id_keep_end.end());
290 std::vector<Real> circular_rad_list;
291 std::vector<Real> non_circular_rad_list;
298 non_circular_rad_list,
301 external_block_change,
307 non_circular_rad_list,
310 external_block_change,
313 const Real max_circular_radius =
314 *std::max_element(circular_rad_list.begin(), circular_rad_list.end());
315 const Real min_non_circular_radius =
316 *std::min_element(non_circular_rad_list.begin(), non_circular_rad_list.end());
320 for (
const auto & elem :
mesh.element_ptr_range())
322 const Point & original_vertex_avg = vertex_avgs[elem->id()];
323 const Point & new_vertex_avg = elem->vertex_average();
326 if (elem->type() ==
TRI6 || elem->type() ==
TRI7)
329 *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
331 *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
333 *elem->node_ptr(2), *elem->node_ptr(0), max_circular_radius, rad_tol);
334 if (elem->type() ==
TRI7)
335 *elem->node_ptr(6) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
336 *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5)) /
339 else if (elem->type() ==
QUAD8 || elem->type() ==
QUAD9)
342 *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
344 *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
346 *elem->node_ptr(2), *elem->node_ptr(3), max_circular_radius, rad_tol);
348 *elem->node_ptr(3), *elem->node_ptr(0), max_circular_radius, rad_tol);
349 if (elem->type() ==
QUAD9)
350 *elem->node_ptr(8) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
351 *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5) +
352 *elem->node_ptr(6) + *elem->node_ptr(7)) /
358 std::vector<Real> new_azimuthal_angle;
359 for (
const auto & node_ptr :
as_range(
mesh.nodes_begin(),
mesh.nodes_end()))
361 const Node & node = *node_ptr;
363 std::sqrt(node(0) * node(0) + node(1) * node(1)), max_circular_radius, rad_tol))
365 Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
366 new_azimuthal_angle.push_back(node_azi);
369 std::sort(new_azimuthal_angle.begin(), new_azimuthal_angle.end());
371 const bool is_first_value_vertex_mod =
374 Real radiusCorrectionFactor_mod =
382 if (min_non_circular_radius <
383 max_circular_radius / radiusCorrectionFactor_original * radiusCorrectionFactor_mod)
384 mooseError(
"In AzimuthalBlockSplitGenerator ",
386 ": the circular region is overlapped with background region after correction.");
389 Node & node = *node_ptr;
390 Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
392 if (node_rad > rad_tol && node_rad <= max_circular_radius + rad_tol)
394 const Real node_azi = atan2(node(1), node(0));
395 const Real node_rad_corr =
396 node_rad / radiusCorrectionFactor_original * radiusCorrectionFactor_mod;
397 node(0) = node_rad_corr * std::cos(node_azi);
398 node(1) = node_rad_corr * std::sin(node_azi);
403 for (
unsigned int block_id_index = 0; block_id_index <
_old_block_ids.size(); block_id_index++)
408 auto p_cent = elem->true_centroid();
409 auto p_cent_azi = atan2(p_cent(1), p_cent(0)) * 180.0 / M_PI;
419 MeshTools::Modification::rotate(
mesh, -90.0, 0.0, 0.0);
431 Real & original_down,
432 std::vector<Real>::iterator & original_down_it,
434 std::vector<Real>::iterator & original_up_it,
435 std::vector<Real> & azimuthal_angles_vtx)
439 std::lower_bound(azimuthal_angles_vtx.begin(), azimuthal_angles_vtx.end(), terminal_angle);
440 if (term_up == azimuthal_angles_vtx.begin())
442 original_up = *term_up;
443 original_up_it = term_up;
444 original_down = -180.0;
445 original_down_it = azimuthal_angles_vtx.end() - 1;
447 else if (term_up == azimuthal_angles_vtx.end())
449 original_down = azimuthal_angles_vtx.back();
450 original_down_it = azimuthal_angles_vtx.end() - 1;
452 original_up_it = azimuthal_angles_vtx.begin();
456 original_down = *(term_up - 1);
457 original_down_it = term_up - 1;
458 original_up = *term_up;
459 original_up_it = term_up;
465 const Real & side_angular_range,
466 const Real & azi_tol,
467 const Real & terminal_angle,
468 const Real & original_down,
469 std::vector<Real>::iterator & original_down_it,
470 const Real & original_up,
471 std::vector<Real>::iterator & original_up_it,
477 const Real half_interval = (original_up - original_down) / 2.0;
481 if (std::abs((original_down + side_angular_shift) / side_angular_range -
482 std::round((original_down + side_angular_shift) / side_angular_range)) < azi_tol &&
483 std::abs(original_down - terminal_angle) > azi_tol)
485 azi_to_keep = original_down;
486 azi_to_mod = original_up;
487 *original_up_it = terminal_angle;
492 else if (std::abs((original_up + side_angular_shift) / side_angular_range -
493 std::round((original_up + side_angular_shift) / side_angular_range)) <
495 std::abs(original_up - terminal_angle) > azi_tol)
497 azi_to_keep = original_up;
498 azi_to_mod = original_down;
499 *original_down_it = terminal_angle;
502 else if (terminal_angle - original_down > half_interval)
504 azi_to_keep = original_down;
505 azi_to_mod = original_up;
506 *original_up_it = terminal_angle;
511 azi_to_keep = original_up;
512 azi_to_mod = original_down;
513 *original_down_it = terminal_angle;
519 ReplicatedMesh & mesh,
520 const std::vector<std::pair<Real, dof_id_type>> & node_id_mod,
521 const std::vector<std::pair<Real, dof_id_type>> & node_id_keep,
522 std::vector<Real> & circular_rad_list,
523 std::vector<Real> & non_circular_rad_list,
524 const std::vector<std::tuple<dof_id_type, boundary_id_type>> & node_list,
525 const Real & term_angle,
526 const bool & external_block_change,
527 const Real & rad_tol)
529 for (
unsigned int i = 0; i < node_id_mod.size(); i++)
532 if (std::abs(node_id_mod[i].first - node_id_keep[i].first) < rad_tol)
534 Node & node_mod =
mesh.node_ref(node_id_mod[i].second);
535 circular_rad_list.push_back(node_id_mod[i].first);
536 node_mod(0) = node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0);
537 node_mod(1) = node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0);
541 non_circular_rad_list.push_back(node_id_mod[i].first);
545 if (std::find(node_list.begin(),
547 std::make_tuple(node_id_mod[i].second,
OUTER_SIDESET_ID)) == node_list.end() ||
548 external_block_change)
550 Node & node_mod =
mesh.node_ref(node_id_mod[i].second);
551 const Node & node_keep =
mesh.node_ref(node_id_keep[i].second);
553 std::make_pair(node_mod(0), node_mod(1)),
554 std::make_pair(node_keep(0), node_keep(1)),
555 std::make_pair(0.0, 0.0),
556 std::make_pair(2.0 * node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0),
557 2.0 * node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0)));
558 node_mod(0) = pair_tmp.first;
559 node_mod(1) = pair_tmp.second;
567 const Point vertex_1,
568 const Real max_radius,
572 const Real r_vertex_0 = std::sqrt(vertex_0(0) * vertex_0(0) + vertex_0(1) * vertex_0(1));
573 const Real r_vertex_1 = std::sqrt(vertex_1(0) * vertex_1(0) + vertex_1(1) * vertex_1(1));
577 if (std::abs(r_vertex_0 - r_vertex_1) < rad_tol && r_vertex_0 < max_radius + rad_tol)
578 return (vertex_0 + vertex_1).unit() * r_vertex_0;
580 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)
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
const Real _angle_range
Angular range of the modified azimuthal blocks.
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 T & getParam(const std::string &name) const
void paramError(const std::string ¶m, Args... args) const
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.
bool absoluteFuzzyGreaterThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
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.