14 #include "libmesh/cell_prism6.h" 15 #include "libmesh/unstructured_mesh.h" 19 DetailedTriSubChannelMeshGenerator,
28 "Creates a detailed mesh of subchannels in a triangular lattice arrangement");
31 params.
addParam<
Real>(
"unheated_length_entry", 0.0,
"Unheated length at entry [m]");
33 params.
addParam<
Real>(
"unheated_length_exit", 0.0,
"Unheated length at exit [m]");
34 params.
addRequiredParam<
unsigned int>(
"nrings",
"Number of fuel Pin rings per assembly [-]");
36 "Flat to flat distance for the hexagonal assembly [m]");
37 params.
addParam<
unsigned int>(
"block_id", 0,
"Block ID used for the mesh subdomain.");
38 params.
addRequiredParam<
unsigned int>(
"n_cells",
"The number of cells in the axial direction");
39 params.
addParam<
bool>(
"verbose_flag",
false,
"Flag to print out the mesh coordinates.");
46 _unheated_length_entry(getParam<
Real>(
"unheated_length_entry")),
47 _heated_length(getParam<
Real>(
"heated_length")),
48 _unheated_length_exit(getParam<
Real>(
"unheated_length_exit")),
49 _pitch(getParam<
Real>(
"pitch")),
50 _pin_diameter(getParam<
Real>(
"pin_diameter")),
51 _n_rings(getParam<unsigned
int>(
"nrings")),
52 _flat_to_flat(getParam<
Real>(
"flat_to_flat")),
53 _block_id(getParam<unsigned
int>(
"block_id")),
54 _n_cells(getParam<unsigned
int>(
"n_cells")),
55 _verbose(getParam<bool>(
"verbose_flag"))
59 for (
unsigned int i = 0; i <
_n_cells + 1; i++)
75 Real avg_coor_x = 0.0;
77 Real avg_coor_y = 0.0;
83 std::vector<std::pair<unsigned int, unsigned int>> gap_fill;
90 for (
unsigned int i = 1; i <
_n_rings; i++)
91 for (
unsigned int j = 0;
j < i * 6;
j++)
95 unsigned int chancount = 0.0;
110 for (
unsigned int j = 0;
j < 3;
j++)
118 for (
unsigned int i = 1; i <
_n_rings; i++)
220 Real _duct_to_pin_gap =
271 a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
283 a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
302 std::unique_ptr<MeshBase>
306 BoundaryInfo & boundary_info = mesh_base->get_boundary_info();
307 mesh_base->set_spatial_dimension(3);
310 const unsigned int theta_res_triangle = 24;
311 const unsigned int theta_res_square = 24;
313 const unsigned int points_per_sixth = theta_res_triangle / 6 + 1;
314 const unsigned int points_per_quadrant = theta_res_square / 4 + 1;
319 const unsigned int points_per_center = points_per_sixth * 3 + 1;
322 const unsigned int points_per_corner = points_per_sixth * 1 + 1 + 3;
325 const unsigned int points_per_side = points_per_quadrant * 2 + 1 + 2;
329 _console <<
"Points per center: " << points_per_center << std::endl;
330 _console <<
"Points per side: " << points_per_side << std::endl;
331 _console <<
"Points per corner: " << points_per_corner << std::endl;
336 const unsigned int elems_per_center = theta_res_triangle * 3 / 6 + 3;
337 const unsigned int elems_per_corner = theta_res_triangle / 6 + 4;
338 const unsigned int elems_per_side = 2 * theta_res_square / 4 + 4;
341 _console <<
"Elems per center: " << elems_per_center << std::endl;
342 _console <<
"Elems per side: " << elems_per_side << std::endl;
343 _console <<
"Elems per corner: " << elems_per_corner << std::endl;
347 unsigned int n_center, n_side, n_corner;
362 _console <<
"Centers: " << n_center << std::endl;
363 _console <<
"Sides: " << n_side << std::endl;
364 _console <<
"Corners: " << n_corner << std::endl;
368 const unsigned int points_per_level =
369 n_corner * points_per_corner + n_side * points_per_side + n_center * points_per_center;
370 const unsigned int elems_per_level =
371 n_corner * elems_per_corner + n_side * elems_per_side + n_center * elems_per_center;
374 _console <<
"Points per level: " << points_per_level << std::endl;
375 _console <<
"Elements per level: " << elems_per_level << std::endl;
377 const unsigned int n_points = points_per_level * (
_n_cells + 1);
378 const unsigned int n_elems = elems_per_level *
_n_cells;
381 _console <<
"Number of points: " << n_points << std::endl;
382 _console <<
"Number of elements: " << n_elems << std::endl;
384 mesh_base->reserve_nodes(n_points);
385 mesh_base->reserve_elem(n_elems);
390 std::array<Point, theta_res_square + 1> circle_points_square;
393 for (
unsigned int i = 0; i < theta_res_square + 1; i++)
395 circle_points_square[i](0) =
radius * std::cos(theta);
396 circle_points_square[i](1) =
radius * std::sin(theta);
400 std::array<Point, theta_res_triangle + 1> circle_points_triangle;
403 for (
unsigned int i = 0; i < theta_res_triangle + 1; i++)
405 circle_points_triangle[i](0) =
radius * std::cos(theta);
406 circle_points_triangle[i](1) =
radius * std::sin(theta);
415 const Real shrink_factor = 0.99999;
417 Real _duct_to_pin_gap =
419 std::array<Point, 2> quadrant_centers_sides;
420 quadrant_centers_sides[0] = Point(
421 -
_pitch * 0.5 * shrink_factor, -(_duct_to_pin_gap +
_pin_diameter) * 0.5 * shrink_factor, 0);
422 quadrant_centers_sides[1] = Point(
423 _pitch * 0.5 * shrink_factor, -(_duct_to_pin_gap +
_pin_diameter) * 0.5 * shrink_factor, 0);
424 std::array<Point, 1> quadrant_centers_corner;
425 quadrant_centers_corner[0] =
430 std::array<Point, 3> triangle_centers;
431 triangle_centers[0] = Point(0,
_pitch * std::cos(
libMesh::pi / 6) * 2 / 3 * shrink_factor, 0);
432 triangle_centers[1] = Point(-
_pitch * 0.5 * shrink_factor,
435 triangle_centers[2] = Point(
438 const unsigned int m_sixth = theta_res_triangle / 6;
439 const unsigned int m_quarter = theta_res_square / 4;
447 std::array<Point, points_per_center> center_points;
450 for (
unsigned int i = 0; i < 3; i++)
453 start = 5 * (m_sixth);
455 start = 1 * (m_sixth);
457 start = 3 * (m_sixth);
458 for (
unsigned int ii = 0; ii < points_per_sixth; ii++)
460 auto c_pt = circle_points_triangle[start - ii];
461 center_points[i * points_per_sixth + ii + 1] = triangle_centers[i] + c_pt;
474 std::array<Point, points_per_corner> corner_points;
476 for (
unsigned int ii = 0; ii < points_per_sixth; ii++)
478 auto c_pt = circle_points_triangle[1 * m_quarter - ii];
479 corner_points[ii + 1] = quadrant_centers_corner[0] + c_pt;
484 2 * side_short * side_long * std::cos(
libMesh::pi / 6));
488 (2 * side_short * side_length));
489 corner_points[points_per_sixth + 1] = Point(side_length * std::cos(angle) * shrink_factor,
490 -side_length * std::sin(angle) * shrink_factor,
492 corner_points[points_per_sixth + 2] =
497 corner_points[points_per_sixth + 3] =
510 std::array<Point, points_per_side> side_points;
512 for (
unsigned int ii = 0; ii < points_per_quadrant; ii++)
514 auto c_pt = circle_points_square[m_quarter - ii];
515 side_points[ii + 1] = quadrant_centers_sides[0] + c_pt;
517 for (
unsigned int ii = 0; ii < points_per_quadrant; ii++)
519 auto c_pt = circle_points_square[2 * m_quarter - ii];
520 side_points[points_per_quadrant + ii + 1] = quadrant_centers_sides[1] + c_pt;
522 side_points[2 * points_per_quadrant + 1] =
523 Point(
_pitch * 0.5 * shrink_factor, 0.5 * _duct_to_pin_gap * shrink_factor, 0);
524 side_points[2 * points_per_quadrant + 2] =
525 Point(-
_pitch * 0.5 * shrink_factor, 0.5 * _duct_to_pin_gap * shrink_factor, 0);
528 int point_counter = 0;
529 unsigned int node_id = 0;
544 Point p0{loc_position[0], loc_position[1], 0};
548 Point subchannel_side =
550 Point base_center_orientation = {0, -1};
554 for (
unsigned int lp = 0; lp < 2; lp++)
555 dot_prod += base_center_orientation(lp) * subchannel_side(lp);
557 std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
558 if (subchannel_side(0) < 0)
574 _console <<
"Subchannel Position: " << p0 << std::endl;
584 for (
unsigned int i = 0; i < points_per_center; i++)
586 auto new_point =
rotatePoint(center_points[i], theta) + p0 + z0;
590 _console << i <<
" - " << new_point << std::endl;
592 mesh_base->add_point(new_point, node_id++);
606 Point p0{loc_position[0], loc_position[1], 0};
610 Point subchannel_side =
612 Point base_center_orientation = {0, 1};
616 for (
unsigned int lp = 0; lp < 2; lp++)
617 dot_prod += base_center_orientation(lp) * subchannel_side(lp);
619 std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
620 if (subchannel_side(0) > 0)
628 _console <<
"Subchannel Position: " << p0 << std::endl;
638 for (
unsigned int i = 0; i < points_per_side; i++)
640 auto new_point =
rotatePoint(side_points[i], theta) + p0 + z0;
644 _console << i <<
" - " << new_point << std::endl;
646 mesh_base->add_point(new_point, node_id++);
660 Point p0{loc_position[0], loc_position[1], 0};
665 Point base_center_orientation = {1, 1};
669 for (
unsigned int lp = 0; lp < 2; lp++)
670 dot_prod += base_center_orientation(lp) * subchannel_side(lp);
672 std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
673 if (subchannel_side(0) > 0)
681 _console <<
"Subchannel Position: " << p0 << std::endl;
691 for (
unsigned int i = 0; i < points_per_corner; i++)
693 auto new_point =
rotatePoint(corner_points[i], theta) + p0 + z0;
697 _console << i <<
" - " << new_point << std::endl;
699 mesh_base->add_point(new_point, node_id++);
706 _console <<
"Point counter: " << point_counter << std::endl;
708 int element_counter = 0;
709 unsigned int elem_id = 0;
710 unsigned int number_of_corner = 0;
711 unsigned int number_of_side = 0;
712 unsigned int number_of_center = 0;
713 unsigned int elems_per_channel = 0;
714 unsigned int points_per_channel = 0;
721 elems_per_channel = elems_per_corner;
722 points_per_channel = points_per_corner;
729 elems_per_channel = elems_per_side;
730 points_per_channel = points_per_side;
737 elems_per_channel = elems_per_center;
738 points_per_channel = points_per_center;
742 for (
unsigned int iz = 0; iz <
_n_cells; iz++)
744 unsigned int elapsed_points = number_of_corner * points_per_corner * (
_n_cells + 1) +
745 number_of_side * points_per_side * (
_n_cells + 1) +
746 number_of_center * points_per_center * (
_n_cells + 1) -
747 points_per_channel * (
_n_cells + 1);
749 unsigned int indx1 = iz * points_per_channel + elapsed_points;
751 unsigned int indx2 = (iz + 1) * points_per_channel + elapsed_points;
753 for (
unsigned int i = 0; i < elems_per_channel; i++)
755 Elem * elem =
new Prism6;
757 elem->set_id(elem_id++);
758 elem = mesh_base->add_elem(elem);
761 _console <<
"Node 0: " << *mesh_base->node_ptr(indx1) << std::endl;
763 elem->set_node(0, mesh_base->node_ptr(indx1));
764 elem->set_node(1, mesh_base->node_ptr(indx1 + i + 1));
765 if (i != elems_per_channel - 1)
766 elem->set_node(2, mesh_base->node_ptr(indx1 + i + 2));
768 elem->set_node(2, mesh_base->node_ptr(indx1 + 1));
770 elem->set_node(3, mesh_base->node_ptr(indx2));
771 elem->set_node(4, mesh_base->node_ptr(indx2 + i + 1));
772 if (i != elems_per_channel - 1)
773 elem->set_node(5, mesh_base->node_ptr(indx2 + i + 2));
775 elem->set_node(5, mesh_base->node_ptr(indx2 + 1));
778 boundary_info.add_side(elem, 0, 0);
780 boundary_info.add_side(elem, 4, 1);
782 element_counter += 1;
787 _console <<
"Element counter: " << element_counter << std::endl;
788 boundary_info.sideset_name(0) =
"inlet";
789 boundary_info.sideset_name(1) =
"outlet";
792 _console <<
"Mesh assembly done" << std::endl;
793 mesh_base->prepare_for_use();
803 std::vector<std::vector<Real>>
A;
805 for (std::vector<Real>
a :
A)
810 A[0] = {std::cos(theta), -std::sin(theta), 0.0};
811 A[1] = {std::sin(theta), std::cos(theta), 0.0};
812 A[2] = {0.0, 0.0, 1.0};
815 Point rotated_vector = Point(0.0, 0.0, 0.0);
816 for (
unsigned int i = 0; i < 3; i++)
818 for (
unsigned int j = 0;
j < 3;
j++)
820 rotated_vector(i) +=
A[i][
j] *
b(
j);
824 return rotated_vector;
831 Point translated_vector = Point(0.0, 0.0, 0.0);
832 for (
unsigned int i = 0; i < 3; i++)
834 translated_vector(i) =
b(i) + translation_vector(i);
837 return translated_vector;
static InputParameters validParams()
const unsigned int _n_rings
Number of rings in the geometry.
const unsigned int & _block_id
Subdomain ID used for the mesh block.
std::vector< std::vector< Real > > _subchannel_position
x,y coordinates of the subchannels
const Real _pitch
Distance between the neighbor fuel pins, pitch.
virtual std::unique_ptr< MeshBase > generate() override
Point getPinPosition(unsigned int i)
returns the position of pin given pin index
Mesh generator that builds a 3D mesh representing triangular subchannels.
EChannelType getSubchannelType(unsigned int index) const
returns the type of the subchannel given the index
std::vector< Real > _z_grid
axial location of nodes
SCMDetailedTriSubChannelMeshGenerator(const InputParameters ¶meters)
registerMooseObjectRenamed("SubChannelApp", DetailedTriSubChannelMeshGenerator, "06/30/2025 24:00", SCMDetailedTriSubChannelMeshGenerator)
virtual const std::string & name() const
unsigned int _nrods
Number of pins.
registerMooseObject("SubChannelApp", SCMDetailedTriSubChannelMeshGenerator)
std::vector< unsigned int > getSubChannelPins(unsigned int i)
returns the index of neighboring pins given subchannel index
Point translatePoint(Point &b, Point &translation_vector)
static InputParameters validParams()
const Real _unheated_length_exit
unheated length of the fuel Pin at the exit of the assembly
static void rodPositions(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 ...
ExpressionBuilder::EBTerm pow(const ExpressionBuilder::EBTerm &left, T exponent)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Real _flat_to_flat
Half of gap between adjacent assemblies.
std::vector< Point > _pin_position
x,y coordinates of the fuel pins
Point rotatePoint(Point b, Real theta)
std::vector< EChannelType > _subch_type
Subchannel type.
std::vector< std::vector< unsigned int > > _pins_in_rings
fuel pins that are belonging to each ring
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
std::vector< Real > getSubchannelPosition(unsigned int i)
returns the position of subchannel given pin index
const Real _unheated_length_entry
unheated length of the fuel Pin at the entry of the assembly
const ConsoleStream _console
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
const unsigned int _n_cells
Number of cells in the axial direction.
unsigned int _n_channels
number of subchannels
std::vector< std::vector< unsigned int > > _chan_to_pin_map
stores the fuel pins belonging to each subchannel
const bool _verbose
Flag to print out the detailed mesh assembly and coordinates.
const Real _heated_length
heated length of the fuel Pin
MooseUnits pow(const MooseUnits &, int)
static const std::string k
const Real _pin_diameter
fuel Pin diameter
void ErrorVector unsigned int
std::map< unsigned int, Real > _orientation_map
map inner and outer rings