https://mooseframework.inl.gov
SCMQuadDuctMeshGenerator.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 "QuadSubChannelMesh.h"
12 #include <cmath>
13 #include "libmesh/unstructured_mesh.h"
14 #include "libmesh/face_quad4.h"
15 
17 
20 {
22  params.addClassDescription("Creates a mesh of 2D duct cells around a square-lattice subassembly");
23  params.addRequiredParam<MeshGeneratorName>("input", "The corresponding subchannel mesh");
24  params.addParam<unsigned int>("block_id", 2, "Subdomain id for the duct mesh cells");
25  params.addRequiredParam<unsigned int>("n_cells", "The number of cells in the axial direction");
26  params.addParam<Real>("unheated_length_entry", 0.0, "Unheated length at entry [m]");
27  params.addRequiredParam<Real>("heated_length", "Heated length [m]");
28  params.addParam<Real>("unheated_length_exit", 0.0, "Unheated length at exit [m]");
29  params.addRangeCheckedParam<Real>("pitch", "pitch > 0", "Lattice pitch (must be positive)");
30  params.addRangeCheckedParam<unsigned int>(
31  "nx",
32  "nx > 1",
33  "Number of channels in the x direction for the subchannel assembly. Must be more than 1 to "
34  "built a duct[-]");
35  params.addRangeCheckedParam<unsigned int>(
36  "ny",
37  "ny > 1",
38  "Number of channels in the y direction for the subchannel assembly. Must be more than 1 to "
39  "built a duct[-]");
40  params.addRequiredParam<Real>("side_gap",
41  "Gap between duct wall and outer pin lattice: distance(edge pin "
42  "center, duct wall) = pitch/2 + side_gap [m]");
43  return params;
44 }
45 
47  : MeshGenerator(params),
48  _input(getMesh("input")),
49  _n_cells(getParam<unsigned int>("n_cells")),
50  _unheated_length_entry(getParam<Real>("unheated_length_entry")),
51  _heated_length(getParam<Real>("heated_length")),
52  _unheated_length_exit(getParam<Real>("unheated_length_exit")),
53  _block_id(getParam<unsigned int>("block_id")),
54  _pitch(getParam<Real>("pitch")),
55  _nx(getParam<unsigned int>("nx")),
56  _ny(getParam<unsigned int>("ny")),
57  _side_gap(getParam<Real>("side_gap"))
58 {
61 }
62 
63 std::unique_ptr<MeshBase>
65 {
66  std::unique_ptr<MeshBase> mesh_base = std::move(_input);
67  mesh_base->set_mesh_dimension(3);
68 
69  std::vector<Point> cross_sec;
70  ductCrossSec(cross_sec, _nx, _ny, _pitch, _side_gap);
71  std::vector<Point> points;
72  ductPoints(points, cross_sec, _z_grid);
73  std::vector<std::vector<size_t>> elem_point_indices;
74  ductElems(elem_point_indices, _z_grid.size(), cross_sec.size());
75  std::vector<Node *> duct_nodes;
76  buildDuct(mesh_base, duct_nodes, points, elem_point_indices, _block_id);
77  mesh_base->subdomain_name(_block_id) = name();
78 
79  mesh_base->prepare_for_use();
80 
81  // Mirror the Tri variant: provide mapping hooks into the subchannel mesh
82  auto & sch_mesh = static_cast<QuadSubChannelMesh &>(*_mesh);
83  sch_mesh.setChannelToDuctMaps(duct_nodes);
84 
85  return mesh_base;
86 }
87 
88 size_t
89 SCMQuadDuctMeshGenerator::ductPointIndex(unsigned int points_per_layer,
90  unsigned int layer,
91  unsigned int point) const
92 {
93  return layer * points_per_layer + point;
94 }
95 
96 void
97 SCMQuadDuctMeshGenerator::ductCorners(std::vector<Point> & corners,
98  Real half_x,
99  Real half_y,
100  const Point & center) const
101 {
102  corners.resize(4);
103  corners[0] = center + Point(-half_x, -half_y, 0);
104  corners[1] = center + Point(half_x, -half_y, 0);
105  corners[2] = center + Point(half_x, half_y, 0);
106  corners[3] = center + Point(-half_x, half_y, 0);
107 }
108 
109 void
110 SCMQuadDuctMeshGenerator::ductCrossSec(std::vector<Point> & cross_sec,
111  unsigned int nx,
112  unsigned int ny,
113  Real pitch,
114  Real side_gap) const
115 {
116  cross_sec.clear();
117 
118  const Real half_x = 0.5 * (nx - 1) * pitch + side_gap;
119  const Real half_y = 0.5 * (ny - 1) * pitch + side_gap;
120 
121  // Subchannel-aligned grid extents (no side_gap)
122  const Real x0 = -0.5 * (nx - 1) * pitch;
123  const Real y0 = -0.5 * (ny - 1) * pitch;
124 
125  // ---- Bottom edge (y = -half_y): nx points, left -> right ----
126  for (unsigned int i = 0; i < nx; ++i)
127  {
128  Real x;
129  if (i == 0)
130  x = -half_x;
131  else if (i == nx - 1)
132  x = half_x;
133  else
134  x = x0 + i * pitch; // aligned with subchannel x grid
135 
136  cross_sec.emplace_back(x, -half_y, 0.0);
137  }
138 
139  // ---- Right edge (x = +half_x): (ny-2) points, bottom -> top (exclude corners) ----
140  for (unsigned int j = 1; j + 1 < ny; ++j)
141  {
142  const Real y = y0 + j * pitch; // aligned with subchannel y grid
143  cross_sec.emplace_back(half_x, y, 0.0);
144  }
145 
146  // ---- Top edge (y = +half_y): nx points, right -> left ----
147  for (unsigned int i = 0; i < nx; ++i)
148  {
149  const unsigned int ii = nx - 1 - i;
150 
151  Real x;
152  if (ii == 0)
153  x = -half_x;
154  else if (ii == nx - 1)
155  x = half_x;
156  else
157  x = x0 + ii * pitch;
158 
159  cross_sec.emplace_back(x, half_y, 0.0);
160  }
161 
162  // ---- Left edge (x = -half_x): (ny-2) points, top -> bottom (exclude corners) ----
163  for (unsigned int j = 1; j + 1 < ny; ++j)
164  {
165  const unsigned int jj = ny - 1 - j;
166  const Real y = y0 + jj * pitch; // aligned with subchannel y grid
167  cross_sec.emplace_back(-half_x, y, 0.0);
168  }
169 
170  // Total points: 2*nx + 2*ny - 4
171 }
172 
173 void
174 SCMQuadDuctMeshGenerator::ductPoints(std::vector<Point> & points,
175  const std::vector<Point> & cross_sec,
176  const std::vector<Real> & z_layers) const
177 {
178  points.resize(cross_sec.size() * z_layers.size());
179  for (size_t i = 0; i < z_layers.size(); i++)
180  for (size_t j = 0; j < cross_sec.size(); j++)
181  points[ductPointIndex(cross_sec.size(), i, j)] =
182  Point(cross_sec[j](0), cross_sec[j](1), z_layers[i]);
183 }
184 
185 void
186 SCMQuadDuctMeshGenerator::ductElems(std::vector<std::vector<size_t>> & elem_point_indices,
187  unsigned int n_layers,
188  unsigned int points_per_layer) const
189 {
190  elem_point_indices.clear();
191  for (unsigned int i = 0; i < n_layers - 1; i++)
192  {
193  const unsigned int bottom = i;
194  const unsigned int top = i + 1;
195  for (unsigned int j = 0; j < points_per_layer; j++)
196  {
197  const unsigned int left = j;
198  const unsigned int right = (j + 1) % points_per_layer;
199  elem_point_indices.push_back({ductPointIndex(points_per_layer, bottom, left),
200  ductPointIndex(points_per_layer, bottom, right),
201  ductPointIndex(points_per_layer, top, right),
202  ductPointIndex(points_per_layer, top, left)});
203  }
204  }
205 }
206 
207 void
208 SCMQuadDuctMeshGenerator::buildDuct(std::unique_ptr<MeshBase> & mesh,
209  std::vector<Node *> & duct_nodes,
210  const std::vector<Point> & points,
211  const std::vector<std::vector<size_t>> & elem_point_indices,
212  SubdomainID block) const
213 {
214  // Create mesh nodes for all duct points and keep a local index -> Node* map
215  duct_nodes.clear();
216  duct_nodes.reserve(points.size());
217  for (const auto & p : points)
218  duct_nodes.push_back(mesh->add_point(p));
219 
220  // Create QUAD4 surface elements using libMesh factory style
221  for (const auto & elem_indices : elem_point_indices)
222  {
223  mooseAssert(elem_indices.size() == 4,
224  "Expected 4 node indices per element when building QUAD4 elements.");
225 
226  auto elem = Elem::build(ElemType::QUAD4);
227  elem->subdomain_id() = block;
228 
229  // Set the 4 nodes of the QUAD4
230  for (unsigned int i = 0; i < 4; ++i)
231  {
232  const auto idx = elem_indices[i];
233  mooseAssert(idx < duct_nodes.size(), "Element node index out of range.");
234  elem->set_node(i, duct_nodes[idx]);
235  }
236 
237  // Hand ownership to the mesh
238  mesh->add_elem(elem.release());
239  }
240 }
Mesh generator for a square/rectangular duct around a square-lattice subassembly. ...
std::unique_ptr< MeshBase > generate() override
registerMooseObject("SubChannelApp", SCMQuadDuctMeshGenerator)
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)
std::vector< Real > _z_grid
axial location of nodes
const unsigned int _n_cells
number of axial cells
MeshBase & mesh
std::unique_ptr< MeshBase > & _input
Mesh that comes from another generator.
const std::vector< double > y
Creates the mesh of subchannels in a quadrilateral lattice.
void addRequiredParam(const std::string &name, const std::string &doc_string)
static InputParameters validParams()
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.
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.
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.
SCMQuadDuctMeshGenerator(const InputParameters &parameters)
const std::string & name() const
void ductCorners(std::vector< Point > &corners, Real half_x, Real half_y, const Point &center) const
Computes the x-y corner coordinates of a rectangular duct cross-section.
const std::vector< double > x
static const std::string pitch
static InputParameters validParams()
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Real p
void setChannelToDuctMaps(const std::vector< Node *> &duct_nodes)
Function that sets the channel-to-duct maps.
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.
void addClassDescription(const std::string &doc_string)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
const Real _pitch
lattice geometry
void ductCrossSec(std::vector< Point > &cross_sec, unsigned int nx, unsigned int ny, Real pitch, Real side_gap) const
Generates the points along the rectangular duct cross-section boundary.
void ErrorVector unsigned int
const unsigned int _block_id
block index
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.
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)