15 #include "libmesh/cell_prism6.h" 16 #include "libmesh/unstructured_mesh.h" 20 SCMDetailedTriSubChannelMeshGenerator,
24 DetailedTriSubChannelMeshGenerator,
28 SCMDetailedTriPinMeshGenerator,
32 DetailedTriPinMeshGenerator,
41 "Creates a detailed mesh of subchannels and pins in a triangular lattice arrangement");
44 params.
addParam<
Real>(
"unheated_length_entry", 0.0,
"Unheated length at entry [m]");
46 params.
addParam<
Real>(
"unheated_length_exit", 0.0,
"Unheated length at exit [m]");
49 "Number of fuel-pin rings per assembly, counting the center pin as the first ring [-]");
51 "Flat to flat distance for the hexagonal assembly [m]");
52 params.
addRequiredParam<
unsigned int>(
"n_cells",
"The number of cells in the axial direction");
56 "Number of azimuthal sectors used to discretize each " 57 "circular pin cross section.");
58 params.
addParam<
unsigned int>(
"block_id", 0,
"Subchannel block id.");
59 params.
deprecateParam(
"block_id",
"subchannel_block_id",
"07/01/2027");
60 params.
addParam<
unsigned int>(
"pin_block_id", 1,
"Fuel pin block id.");
61 params.
addParam<
bool>(
"verbose_flag",
false,
"Flag to print out the mesh coordinates.");
68 _unheated_length_entry(getParam<
Real>(
"unheated_length_entry")),
69 _heated_length(getParam<
Real>(
"heated_length")),
70 _unheated_length_exit(getParam<
Real>(
"unheated_length_exit")),
71 _pitch(getParam<
Real>(
"pitch")),
72 _pin_diameter(getParam<
Real>(
"pin_diameter")),
73 _n_rings(getParam<unsigned
int>(
"nrings")),
74 _flat_to_flat(getParam<
Real>(
"flat_to_flat")),
75 _num_sectors(getParam<unsigned
int>(
"num_sectors")),
76 _subchannel_block_id(getParam<unsigned
int>(
"subchannel_block_id")),
77 _pin_block_id(getParam<unsigned
int>(
"pin_block_id")),
78 _n_cells(getParam<unsigned
int>(
"n_cells")),
81 _verbose(getParam<bool>(
"verbose_flag")),
88 "'nrings' must be at least 2. In this mesh generator, the center pin counts as " 89 "the first ring, so a 7-pin bundle uses nrings = 2.");
92 paramError(
"n_cells",
"The number of axial cells must be greater than zero");
95 mooseError(
"Total bundle length must be greater than zero");
98 for (
unsigned int i = 0; i <
_n_cells + 1; i++)
114 Real avg_coor_x = 0.0;
116 Real avg_coor_y = 0.0;
122 std::vector<std::pair<unsigned int, unsigned int>> gap_fill;
129 for (
unsigned int i = 1; i <
_n_rings; i++)
130 for (
unsigned int j = 0;
j < i * 6;
j++)
134 unsigned int chancount = 0;
149 for (
unsigned int j = 0;
j < 3;
j++)
157 for (
unsigned int i = 1; i <
_n_rings; i++)
259 Real _duct_to_pin_gap =
310 a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
322 a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
338 std::vector<std::vector<Node *>> nodes;
343 nodes[
k].push_back(mesh_base->add_point(Point(
center(0),
center(1), elev)));
349 nodes[
k].push_back(mesh_base->add_point(Point(
center(0) + dx,
center(1) + dy, elev)));
357 Elem * elem = mesh_base->add_elem(std::make_unique<Prism6>());
360 const unsigned int ctr_idx = 0;
363 elem->set_node(0, nodes[
k][ctr_idx]);
364 elem->set_node(1, nodes[
k][idx1]);
365 elem->set_node(2, nodes[
k][idx2]);
366 elem->set_node(3, nodes[
k + 1][ctr_idx]);
367 elem->set_node(4, nodes[
k + 1][idx1]);
368 elem->set_node(5, nodes[
k + 1][idx2]);
372 std::unique_ptr<MeshBase>
376 BoundaryInfo & boundary_info = mesh_base->get_boundary_info();
377 mesh_base->set_spatial_dimension(3);
380 const unsigned int theta_res_triangle = 24;
381 const unsigned int theta_res_square = 24;
383 const unsigned int points_per_sixth = theta_res_triangle / 6 + 1;
384 const unsigned int points_per_quadrant = theta_res_square / 4 + 1;
389 const unsigned int points_per_center = points_per_sixth * 3 + 1;
392 const unsigned int points_per_corner = points_per_sixth * 1 + 1 + 3;
395 const unsigned int points_per_side = points_per_quadrant * 2 + 1 + 2;
399 _console <<
"Points per center: " << points_per_center << std::endl;
400 _console <<
"Points per side: " << points_per_side << std::endl;
401 _console <<
"Points per corner: " << points_per_corner << std::endl;
406 const unsigned int elems_per_center = theta_res_triangle * 3 / 6 + 3;
407 const unsigned int elems_per_corner = theta_res_triangle / 6 + 4;
408 const unsigned int elems_per_side = 2 * theta_res_square / 4 + 4;
411 _console <<
"Elems per center: " << elems_per_center << std::endl;
412 _console <<
"Elems per side: " << elems_per_side << std::endl;
413 _console <<
"Elems per corner: " << elems_per_corner << std::endl;
417 unsigned int n_corner = 6;
418 unsigned int n_side = (
_n_rings - 1) * 6;
419 unsigned int n_center =
_n_channels - n_side - n_corner;
422 _console <<
"Centers: " << n_center << std::endl;
423 _console <<
"Sides: " << n_side << std::endl;
424 _console <<
"Corners: " << n_corner << std::endl;
428 const unsigned int points_per_level =
429 n_corner * points_per_corner + n_side * points_per_side + n_center * points_per_center;
430 const unsigned int elems_per_level =
431 n_corner * elems_per_corner + n_side * elems_per_side + n_center * elems_per_center;
434 _console <<
"Points per level: " << points_per_level << std::endl;
435 _console <<
"Elements per level: " << elems_per_level << std::endl;
437 const unsigned int n_points = points_per_level * (
_n_cells + 1);
438 const unsigned int n_elems = elems_per_level *
_n_cells;
444 _console <<
"Number of points: " << n_points << std::endl;
445 _console <<
"Number of elements: " << n_elems << std::endl;
447 mesh_base->reserve_nodes(n_points + pin_points);
448 mesh_base->reserve_elem(n_elems + pin_elems);
453 std::array<Point, theta_res_square + 1> circle_points_square;
456 for (
unsigned int i = 0; i < theta_res_square + 1; i++)
458 circle_points_square[i](0) =
radius * std::cos(theta);
459 circle_points_square[i](1) =
radius * std::sin(theta);
463 std::array<Point, theta_res_triangle + 1> circle_points_triangle;
466 for (
unsigned int i = 0; i < theta_res_triangle + 1; i++)
468 circle_points_triangle[i](0) =
radius * std::cos(theta);
469 circle_points_triangle[i](1) =
radius * std::sin(theta);
478 const Real shrink_factor = 0.99999;
480 Real _duct_to_pin_gap =
482 std::array<Point, 2> quadrant_centers_sides;
483 quadrant_centers_sides[0] = Point(
484 -
_pitch * 0.5 * shrink_factor, -(_duct_to_pin_gap +
_pin_diameter) * 0.5 * shrink_factor, 0);
485 quadrant_centers_sides[1] = Point(
486 _pitch * 0.5 * shrink_factor, -(_duct_to_pin_gap +
_pin_diameter) * 0.5 * shrink_factor, 0);
487 std::array<Point, 1> quadrant_centers_corner;
488 quadrant_centers_corner[0] =
493 std::array<Point, 3> triangle_centers;
494 triangle_centers[0] = Point(0,
_pitch * std::cos(
libMesh::pi / 6) * 2 / 3 * shrink_factor, 0);
495 triangle_centers[1] = Point(-
_pitch * 0.5 * shrink_factor,
498 triangle_centers[2] = Point(
501 const unsigned int m_sixth = theta_res_triangle / 6;
502 const unsigned int m_quarter = theta_res_square / 4;
510 std::array<Point, points_per_center> center_points{};
511 center_points[0] = Point(0, 0, 0);
514 for (
unsigned int i = 0; i < 3; i++)
517 start = 5 * (m_sixth);
519 start = 1 * (m_sixth);
521 start = 3 * (m_sixth);
522 for (
unsigned int ii = 0; ii < points_per_sixth; ii++)
524 auto c_pt = circle_points_triangle[start - ii];
525 center_points[i * points_per_sixth + ii + 1] = triangle_centers[i] + c_pt;
538 std::array<Point, points_per_corner> corner_points{};
539 corner_points[0] = Point(0, 0, 0);
541 for (
unsigned int ii = 0; ii < points_per_sixth; ii++)
543 auto c_pt = circle_points_triangle[1 * m_quarter - ii];
544 corner_points[ii + 1] = quadrant_centers_corner[0] + c_pt;
549 2 * side_short * side_long * std::cos(
libMesh::pi / 6));
553 (2 * side_short * side_length));
554 corner_points[points_per_sixth + 1] = Point(side_length * std::cos(angle) * shrink_factor,
555 -side_length * std::sin(angle) * shrink_factor,
557 corner_points[points_per_sixth + 2] =
562 corner_points[points_per_sixth + 3] =
575 std::array<Point, points_per_side> side_points{};
576 side_points[0] = Point(0, 0, 0);
578 for (
unsigned int ii = 0; ii < points_per_quadrant; ii++)
580 auto c_pt = circle_points_square[m_quarter - ii];
581 side_points[ii + 1] = quadrant_centers_sides[0] + c_pt;
583 for (
unsigned int ii = 0; ii < points_per_quadrant; ii++)
585 auto c_pt = circle_points_square[2 * m_quarter - ii];
586 side_points[points_per_quadrant + ii + 1] = quadrant_centers_sides[1] + c_pt;
588 side_points[2 * points_per_quadrant + 1] =
589 Point(
_pitch * 0.5 * shrink_factor, 0.5 * _duct_to_pin_gap * shrink_factor, 0);
590 side_points[2 * points_per_quadrant + 2] =
591 Point(-
_pitch * 0.5 * shrink_factor, 0.5 * _duct_to_pin_gap * shrink_factor, 0);
594 int point_counter = 0;
595 unsigned int node_id = 0;
610 Point p0{loc_position[0], loc_position[1], 0};
614 Point subchannel_side =
616 Point base_center_orientation = {0, -1};
620 for (
unsigned int lp = 0; lp < 2; lp++)
621 dot_prod += base_center_orientation(lp) * subchannel_side(lp);
623 std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
624 if (subchannel_side(0) < 0)
640 _console <<
"Subchannel Position: " << p0 << std::endl;
650 for (
unsigned int i = 0; i < points_per_center; i++)
652 auto new_point =
rotatePoint(center_points[i], theta) + p0 + z0;
656 _console << i <<
" - " << new_point << std::endl;
658 mesh_base->add_point(new_point, node_id++);
672 Point p0{loc_position[0], loc_position[1], 0};
676 Point subchannel_side =
678 Point base_center_orientation = {0, 1};
682 for (
unsigned int lp = 0; lp < 2; lp++)
683 dot_prod += base_center_orientation(lp) * subchannel_side(lp);
685 std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
686 if (subchannel_side(0) > 0)
694 _console <<
"Subchannel Position: " << p0 << std::endl;
704 for (
unsigned int i = 0; i < points_per_side; i++)
706 auto new_point =
rotatePoint(side_points[i], theta) + p0 + z0;
710 _console << i <<
" - " << new_point << std::endl;
712 mesh_base->add_point(new_point, node_id++);
726 Point p0{loc_position[0], loc_position[1], 0};
731 Point base_center_orientation = {1, 1};
735 for (
unsigned int lp = 0; lp < 2; lp++)
736 dot_prod += base_center_orientation(lp) * subchannel_side(lp);
738 std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
739 if (subchannel_side(0) > 0)
747 _console <<
"Subchannel Position: " << p0 << std::endl;
757 for (
unsigned int i = 0; i < points_per_corner; i++)
759 auto new_point =
rotatePoint(corner_points[i], theta) + p0 + z0;
763 _console << i <<
" - " << new_point << std::endl;
765 mesh_base->add_point(new_point, node_id++);
772 _console <<
"Point counter: " << point_counter << std::endl;
774 int element_counter = 0;
775 unsigned int elem_id = 0;
776 unsigned int number_of_corner = 0;
777 unsigned int number_of_side = 0;
778 unsigned int number_of_center = 0;
779 unsigned int elems_per_channel = 0;
780 unsigned int points_per_channel = 0;
787 elems_per_channel = elems_per_corner;
788 points_per_channel = points_per_corner;
795 elems_per_channel = elems_per_side;
796 points_per_channel = points_per_side;
803 elems_per_channel = elems_per_center;
804 points_per_channel = points_per_center;
808 for (
unsigned int iz = 0; iz <
_n_cells; iz++)
810 unsigned int elapsed_points = number_of_corner * points_per_corner * (
_n_cells + 1) +
811 number_of_side * points_per_side * (
_n_cells + 1) +
812 number_of_center * points_per_center * (
_n_cells + 1) -
813 points_per_channel * (
_n_cells + 1);
815 unsigned int indx1 = iz * points_per_channel + elapsed_points;
817 unsigned int indx2 = (iz + 1) * points_per_channel + elapsed_points;
819 for (
unsigned int i = 0; i < elems_per_channel; i++)
821 Elem * elem = mesh_base->add_elem(std::make_unique<Prism6>());
823 elem->set_id(elem_id++);
826 _console <<
"Node 0: " << *mesh_base->node_ptr(indx1) << std::endl;
828 elem->set_node(0, mesh_base->node_ptr(indx1));
829 elem->set_node(1, mesh_base->node_ptr(indx1 + i + 1));
830 if (i != elems_per_channel - 1)
831 elem->set_node(2, mesh_base->node_ptr(indx1 + i + 2));
833 elem->set_node(2, mesh_base->node_ptr(indx1 + 1));
835 elem->set_node(3, mesh_base->node_ptr(indx2));
836 elem->set_node(4, mesh_base->node_ptr(indx2 + i + 1));
837 if (i != elems_per_channel - 1)
838 elem->set_node(5, mesh_base->node_ptr(indx2 + i + 2));
840 elem->set_node(5, mesh_base->node_ptr(indx2 + 1));
843 boundary_info.add_side(elem, 0, 0);
845 boundary_info.add_side(elem, 4, 1);
847 element_counter += 1;
852 _console <<
"Element counter: " << element_counter << std::endl;
853 boundary_info.sideset_name(0) =
"inlet";
854 boundary_info.sideset_name(1) =
"outlet";
865 _console <<
"Mesh assembly done" << std::endl;
866 mesh_base->prepare_for_use();
874 std::vector<std::vector<Real>>
A(3, std::vector<Real>(3));
876 A[0] = {std::cos(theta), -std::sin(theta), 0.0};
877 A[1] = {std::sin(theta), std::cos(theta), 0.0};
878 A[2] = {0.0, 0.0, 1.0};
880 Point rotated_vector = Point(0.0, 0.0, 0.0);
881 for (
unsigned int i = 0; i < 3; i++)
882 for (
unsigned int j = 0;
j < 3;
j++)
883 rotated_vector(i) +=
A[i][
j] *
b(
j);
885 return rotated_vector;
const unsigned int _pin_block_id
Pin subdomain ID.
dof_id_type _elem_id
counter for element numbering
std::vector< Point > _pin_position
x,y coordinates of the fuel pins
void paramError(const std::string ¶m, Args... args) const
const unsigned int _num_sectors
Number of azimuthal sectors used to discretize each circular pin cross section.
const unsigned int _n_cells
Number of cells in the axial direction.
const Real _pitch
Distance between the neighbor fuel pins, pitch.
virtual std::unique_ptr< MeshBase > generate() override
static void pinPositions(std::vector< Point > &positions, unsigned int nrings, Real pitch, Point center)
Calculates and stores the pin positions/centers for a hexagonal assembly containing the given number ...
static InputParameters validParams()
EChannelType getSubchannelType(unsigned int index) const
returns the type of the subchannel given the index
std::vector< std::vector< unsigned int > > _pins_in_rings
fuel pins that are belonging to each ring
const Real _flat_to_flat
Half of gap between adjacent assemblies.
unsigned int _nrods
Number of pins.
const Real _pin_diameter
fuel Pin diameter
const Real _unheated_length_entry
unheated length of the fuel Pin at the entry of the assembly
std::vector< EChannelType > _subch_type
Subchannel type.
const Real _heated_length
heated length of the fuel Pin
std::vector< Real > getSubchannelPosition(unsigned int i)
returns the position of subchannel given pin index
static InputParameters validParams()
const unsigned int _subchannel_block_id
Subchannel subdomain ID.
void generatePin(std::unique_ptr< MeshBase > &mesh_base, const Point ¢er)
Generate one detailed fuel pin volume centered at the supplied point.
ExpressionBuilder::EBTerm pow(const ExpressionBuilder::EBTerm &left, T exponent)
const bool _verbose
Flag to print out the detailed mesh assembly and coordinates.
Point rotatePoint(Point b, Real theta)
rotate a point by theta radians about the origin
std::vector< std::vector< Real > > _subchannel_position
x,y coordinates of the subchannels
SCMDetailedTriAssemblyMeshGenerator(const InputParameters ¶meters)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
static const std::string alpha
void mooseError(Args &&... args) const
Point getPinPosition(unsigned int i)
returns the position of pin given pin index
unsigned int _n_channels
number of subchannels
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
registerMooseObject("SubChannelApp", SCMDetailedTriAssemblyMeshGenerator)
const ConsoleStream _console
Mesh generator that builds a 3D mesh representing triangular subchannels and pins.
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
registerMooseObjectRenamed("SubChannelApp", SCMDetailedTriSubChannelMeshGenerator, "06/30/2027 24:00", SCMDetailedTriAssemblyMeshGenerator)
std::vector< std::vector< unsigned int > > _chan_to_pin_map
stores the fuel pins belonging to each subchannel
std::vector< unsigned int > getSubChannelPins(unsigned int i)
returns the index of neighboring pins given subchannel index
MooseUnits pow(const MooseUnits &, int)
static const std::string k
void ErrorVector unsigned int
const Real _unheated_length_exit
unheated length of the fuel Pin at the exit of the assembly
const unsigned int _n_rings
Number of rings in the geometry.
static const std::string center
std::vector< Real > _z_grid
axial location of nodes
std::map< unsigned int, Real > _orientation_map
map inner and outer rings