https://mooseframework.inl.gov
SCMTriDuctMeshGenerator.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 "TriSubChannelMesh.h"
12 #include <cmath>
13 #include "libmesh/face_quad4.h"
14 #include "libmesh/unstructured_mesh.h"
15 
17 
20 {
22  params.addClassDescription(
23  "Creates a mesh of 2D duct cells around a triangular lattice subassembly");
24  params.addRequiredParam<MeshGeneratorName>("input", "The corresponding subchannel mesh");
25  params.addParam<unsigned int>("block_id", 2, "Domain Index");
26  params.addRequiredParam<unsigned int>("n_cells", "The number of cells in the axial direction");
27  params.addParam<Real>("unheated_length_entry", 0.0, "Unheated length at entry [m]");
28  params.addRequiredParam<Real>("heated_length", "Heated length [m]");
29  params.addParam<Real>("unheated_length_exit", 0.0, "Unheated length at exit [m]");
30  params.addRequiredParam<Real>("pitch", "Pitch is the distance between adjacent pins [m]");
31  params.addRequiredParam<unsigned int>("nrings", "Number of fuel Pin rings in the assembly [-]");
32  params.addRequiredParam<Real>("flat_to_flat",
33  "Flat to flat distance for the hexagonal assembly [m]");
34  return params;
35 }
36 
38  : MeshGenerator(params),
39  _input(getMesh("input")),
40  _n_cells(getParam<unsigned int>("n_cells")),
41  _unheated_length_entry(getParam<Real>("unheated_length_entry")),
42  _heated_length(getParam<Real>("heated_length")),
43  _unheated_length_exit(getParam<Real>("unheated_length_exit")),
44  _block_id(getParam<unsigned int>("block_id")),
45  _pitch(getParam<Real>("pitch")),
46  _n_rings(getParam<unsigned int>("nrings")),
47  _flat_to_flat(getParam<Real>("flat_to_flat"))
48 {
51 }
52 
53 std::unique_ptr<MeshBase>
55 {
56  std::unique_ptr<MeshBase> mesh_base = std::move(_input);
57  mesh_base->set_mesh_dimension(3);
58 
59  std::vector<Point> corners;
60  ductCorners(corners, _flat_to_flat, Point(0, 0, 0));
61  std::vector<Point> cross_sec;
62  ductCrossSec(cross_sec, corners, _n_rings, _pitch);
63  std::vector<Point> points;
64  ductPoints(points, cross_sec, _z_grid);
65  std::vector<std::vector<size_t>> elem_point_indices;
66  ductElems(elem_point_indices, _z_grid.size(), cross_sec.size());
67  std::vector<Node *> duct_nodes;
68  buildDuct(mesh_base, duct_nodes, points, elem_point_indices, _block_id);
69  mesh_base->subdomain_name(_block_id) = name();
70 
71  mesh_base->prepare_for_use();
72 
73  auto & sch_mesh = static_cast<TriSubChannelMesh &>(*_mesh);
74  sch_mesh.setChannelToDuctMaps(duct_nodes);
75  sch_mesh._duct_mesh_exist = true;
76 
77  return mesh_base;
78 }
79 
80 void
81 SCMTriDuctMeshGenerator::ductCorners(std::vector<Point> & corners,
82  Real flat_to_flat,
83  const Point & center) const
84 {
85  corners.resize(TriSubChannelMesh::N_CORNERS);
86  Real r_corner = flat_to_flat / 2. / std::cos(libMesh::pi / 6.);
87  for (size_t i = 0; i < TriSubChannelMesh::N_CORNERS; i++)
88  {
89  Real theta = i * libMesh::pi / 3.;
90  Point corner = {r_corner * std::cos(theta), r_corner * std::sin(theta)};
91  corners[i] = center + corner;
92  }
93 }
94 
95 void
96 SCMTriDuctMeshGenerator::ductCrossSec(std::vector<Point> & cross_sec,
97  const std::vector<Point> & corners,
98  unsigned int nrings,
99  Real pitch) const
100 {
101  cross_sec.clear();
102 
103  if (pitch <= 0.0)
104  mooseError("The 'pitch' must be > 0.");
105 
106  if (nrings < 2)
107  mooseError("The 'nrings' must be >= 2.");
108 
109  // Side length of the regular hexagon (distance between adjacent corners)
110  const Real side_length = (corners[0] - corners[1]).norm();
111 
112  // start_offset = (hexagon_side - (nrings - 2)*pitch) / 2
113  const Real start_offset = 0.5 * (side_length - (nrings - 2) * pitch);
114 
115  // If this is negative, the requested layout cannot fit.
116  if (start_offset < 0.0)
117  mooseError("SCMTriDuctMeshGenerator: computed start_offset is negative (",
118  start_offset,
119  "). Check 'nrings', 'pitch', and duct side length.");
120 
121  for (size_t i = 0; i < corners.size(); i++)
122  {
123  const Point left = corners[i];
124  const Point right = corners[(i + 1) % corners.size()];
125 
126  // Always include the left corner
127  cross_sec.push_back(left);
128 
129  // Unit direction along this side
130  const Point direc = (right - left).unit();
131 
132  // Add exactly (nrings - 1) points after the left corner
133  // at start_offset + k*pitch, k = 0..(nrings-2)
134  for (const auto k : make_range(nrings - 1))
135  {
136  const Real offset_from_corner = start_offset + k * pitch;
137  cross_sec.push_back(left + direc * offset_from_corner);
138  }
139  }
140 }
141 
142 size_t
143 SCMTriDuctMeshGenerator::ductPointIndex(unsigned int points_per_layer,
144  unsigned int layer,
145  unsigned int point) const
146 {
147  return layer * points_per_layer + point;
148 }
149 
150 void
151 SCMTriDuctMeshGenerator::ductPoints(std::vector<Point> & points,
152  const std::vector<Point> & cross_sec,
153  const std::vector<Real> & z_layers) const
154 {
155  points.resize(cross_sec.size() * z_layers.size());
156  for (size_t i = 0; i < z_layers.size(); i++)
157  for (size_t j = 0; j < cross_sec.size(); j++)
158  points[ductPointIndex(cross_sec.size(), i, j)] =
159  Point(cross_sec[j](0), cross_sec[j](1), z_layers[i]);
160 }
161 
162 void
163 SCMTriDuctMeshGenerator::ductElems(std::vector<std::vector<size_t>> & elem_point_indices,
164  unsigned int n_layers,
165  unsigned int points_per_layer) const
166 {
167  elem_point_indices.clear();
168  for (unsigned int i = 0; i < n_layers - 1; i++)
169  {
170  unsigned int bottom = i;
171  unsigned int top = i + 1;
172  for (unsigned int j = 0; j < points_per_layer; j++)
173  {
174  unsigned int left = j;
175  unsigned int right = (j + 1) % points_per_layer;
176  elem_point_indices.push_back({ductPointIndex(points_per_layer, bottom, left),
177  ductPointIndex(points_per_layer, bottom, right),
178  ductPointIndex(points_per_layer, top, right),
179  ductPointIndex(points_per_layer, top, left)});
180  }
181  }
182 }
183 
184 void
185 SCMTriDuctMeshGenerator::buildDuct(std::unique_ptr<MeshBase> & mesh,
186  std::vector<Node *> & duct_nodes,
187  const std::vector<Point> & points,
188  const std::vector<std::vector<size_t>> & elem_point_indices,
189  SubdomainID block) const
190 {
191  // Create mesh nodes for all duct points and keep a local index -> Node* map
192  duct_nodes.clear();
193  duct_nodes.reserve(points.size());
194  for (const auto & p : points)
195  duct_nodes.push_back(mesh->add_point(p));
196 
197  // Create QUAD4 surface elements using libMesh factory style
198  for (const auto & elem_indices : elem_point_indices)
199  {
200  mooseAssert(elem_indices.size() == 4,
201  "Expected 4 node indices per element when building QUAD4 elements.");
202 
203  auto elem = Elem::build(ElemType::QUAD4);
204  elem->subdomain_id() = block;
205 
206  // Set the 4 nodes of the QUAD4
207  for (unsigned int i = 0; i < 4; ++i)
208  {
209  const auto idx = elem_indices[i];
210  mooseAssert(idx < duct_nodes.size(), "Element node index out of range.");
211  elem->set_node(i, duct_nodes[idx]);
212  }
213 
214  // Hand ownership to the mesh
215  mesh->add_elem(elem.release());
216  }
217 }
void ductElems(std::vector< std::vector< size_t >> &elem_point_indices, unsigned int n_layers, unsigned int points_per_layer) const
Determines element connectivity for the duct mesh.
void ductPoints(std::vector< Point > &points, const std::vector< Point > &cross_sec, const std::vector< Real > &z_layers) const
Computes all 3D point locations used to construct the duct mesh.
const Real _unheated_length_entry
unheated length of the fuel Pin at the entry of the assembly
T & getMesh(MooseMesh &mesh)
function to cast mesh
Definition: SCM.h:35
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
MeshBase & mesh
void ductCrossSec(std::vector< Point > &cross_sec, const std::vector< Point > &corners, unsigned int nrings, Real pitch) const
Generates the points along the duct cross-section sides.
static const unsigned int N_CORNERS
number of corners in the duct x-sec
std::unique_ptr< MeshBase > generate() override
void addRequiredParam(const std::string &name, const std::string &doc_string)
const Real _flat_to_flat
the distance between flat surfaces of the duct facing each other
const Real _unheated_length_exit
unheated length of the fuel Pin at the exit of the assembly
static void generateZGrid(Real unheated_length_entry, Real heated_length, Real unheated_length_exit, unsigned int n_cells, std::vector< Real > &z_grid)
Generate the spacing in z-direction using heated and unteaded lengths.
static InputParameters validParams()
const unsigned int _n_cells
number of axial cells
const std::string & name() const
static const std::string pitch
const unsigned int _block_id
block index
registerMooseObject("SubChannelApp", SCMTriDuctMeshGenerator)
std::unique_ptr< MeshBase > & _input
Mesh that comes from another generator.
static InputParameters validParams()
void buildDuct(std::unique_ptr< MeshBase > &mesh, std::vector< Node *> &duct_nodes, const std::vector< Point > &points, const std::vector< std::vector< size_t >> &elem_point_indices, SubdomainID block) const
Builds duct mesh nodes and elements and inserts them into the mesh.
const Real _pitch
Distance between the neighbor fuel pins, pitch.
Mesh class for triangular, edge and corner subchannels for hexagonal lattice fuel assemblies...
const unsigned int _n_rings
number of rings of fuel pins
SCMTriDuctMeshGenerator(const InputParameters &parameters)
void ductCorners(std::vector< Point > &corners, Real flat_to_flat, const Point &center) const
Computes the x-y corner coordinates of the duct cross-section.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< Real > _z_grid
axial location of nodes
auto norm(const T &a)
const Real p
void setChannelToDuctMaps(const std::vector< Node *> &duct_nodes)
Function that sets the channel-to-duct maps.
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
size_t ductPointIndex(unsigned int points_per_layer, unsigned int layer, unsigned int point) const
Maps a duct cross-section point and axial layer to a linear point index.
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
const Real _heated_length
heated length of the fuel Pin
static const std::string k
Definition: NS.h:134
void ErrorVector unsigned int
static const std::string center
Definition: NS.h:29
unsigned int idx(const ElemType type, const unsigned int nx, const unsigned int i, const unsigned int j)
const Real pi
Mesh generator for hexagonal duct.