Line data Source code
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 :
10 : #include "SCMDetailedTriSubChannelMeshGenerator.h"
11 : #include "TriSubChannelMesh.h"
12 : #include <array>
13 : #include <cmath>
14 : #include "libmesh/cell_prism6.h"
15 : #include "libmesh/unstructured_mesh.h"
16 :
17 : registerMooseObject("SubChannelApp", SCMDetailedTriSubChannelMeshGenerator);
18 : registerMooseObjectRenamed("SubChannelApp",
19 : DetailedTriSubChannelMeshGenerator,
20 : "06/30/2025 24:00",
21 : SCMDetailedTriSubChannelMeshGenerator);
22 :
23 : InputParameters
24 84 : SCMDetailedTriSubChannelMeshGenerator::validParams()
25 : {
26 84 : InputParameters params = MeshGenerator::validParams();
27 84 : params.addClassDescription(
28 : "Creates a detailed mesh of subchannels in a triangular lattice arrangement");
29 168 : params.addRequiredParam<Real>("pitch", "Pitch [m]");
30 168 : params.addRequiredParam<Real>("pin_diameter", "Rod diameter [m]");
31 168 : params.addParam<Real>("unheated_length_entry", 0.0, "Unheated length at entry [m]");
32 168 : params.addRequiredParam<Real>("heated_length", "Heated length [m]");
33 168 : params.addParam<Real>("unheated_length_exit", 0.0, "Unheated length at exit [m]");
34 168 : params.addRequiredParam<unsigned int>("nrings", "Number of fuel Pin rings per assembly [-]");
35 168 : params.addRequiredParam<Real>("flat_to_flat",
36 : "Flat to flat distance for the hexagonal assembly [m]");
37 168 : params.addParam<unsigned int>("block_id", 0, "Block ID used for the mesh subdomain.");
38 168 : params.addRequiredParam<unsigned int>("n_cells", "The number of cells in the axial direction");
39 168 : params.addParam<bool>("verbose_flag", false, "Flag to print out the mesh coordinates.");
40 84 : return params;
41 0 : }
42 :
43 42 : SCMDetailedTriSubChannelMeshGenerator::SCMDetailedTriSubChannelMeshGenerator(
44 42 : const InputParameters & parameters)
45 : : MeshGenerator(parameters),
46 42 : _unheated_length_entry(getParam<Real>("unheated_length_entry")),
47 84 : _heated_length(getParam<Real>("heated_length")),
48 84 : _unheated_length_exit(getParam<Real>("unheated_length_exit")),
49 84 : _pitch(getParam<Real>("pitch")),
50 84 : _pin_diameter(getParam<Real>("pin_diameter")),
51 84 : _n_rings(getParam<unsigned int>("nrings")),
52 84 : _flat_to_flat(getParam<Real>("flat_to_flat")),
53 84 : _block_id(getParam<unsigned int>("block_id")),
54 84 : _n_cells(getParam<unsigned int>("n_cells")),
55 126 : _verbose(getParam<bool>("verbose_flag"))
56 : {
57 42 : Real L = _unheated_length_entry + _heated_length + _unheated_length_exit;
58 42 : Real dz = L / _n_cells;
59 1404 : for (unsigned int i = 0; i < _n_cells + 1; i++)
60 1362 : _z_grid.push_back(dz * i);
61 :
62 : // x coordinate for the first position
63 : Real x0 = 0.0;
64 : // y coordinate for the first position
65 : Real y0 = 0.0;
66 : // x coordinate for the second position
67 : Real x1 = 0.0;
68 : // y coordinate for the second position dummy variable
69 : Real y1 = 0.0;
70 : // dummy variable
71 : Real a1 = 0.0;
72 : // dummy variable
73 : Real a2 = 0.0;
74 : // average x coordinate
75 : Real avg_coor_x = 0.0;
76 : // average y coordinate
77 : Real avg_coor_y = 0.0;
78 : // distance between two points
79 : Real dist = 0.0;
80 : // distance between two points
81 : Real dist0 = 0.0;
82 : // the indicator used while setting _gap_to_chan_map array
83 : std::vector<std::pair<unsigned int, unsigned int>> gap_fill;
84 42 : TriSubChannelMesh::rodPositions(_pin_position, _n_rings, _pitch, Point(0, 0));
85 42 : _nrods = _pin_position.size();
86 : // assign the pins to the corresponding rings
87 : unsigned int k = 0; // initialize the fuel Pin counter index
88 42 : _pins_in_rings.resize(_n_rings);
89 42 : _pins_in_rings[0].push_back(k++);
90 132 : for (unsigned int i = 1; i < _n_rings; i++)
91 954 : for (unsigned int j = 0; j < i * 6; j++)
92 864 : _pins_in_rings[i].push_back(k++);
93 : // Given the number of pins and number of fuel Pin rings, the number of subchannels can be
94 : // computed as follows:
95 : unsigned int chancount = 0.0;
96 : // Summing internal channels
97 132 : for (unsigned int j = 0; j < _n_rings - 1; j++)
98 90 : chancount += j * 6;
99 : // Adding external channels to the total count
100 42 : _n_channels = chancount + _nrods - 1 + (_n_rings - 1) * 6 + 6;
101 :
102 : // Utils for building the mesh
103 42 : _chan_to_pin_map.resize(_n_channels);
104 42 : _subch_type.resize(_n_channels);
105 42 : _subchannel_position.resize(_n_channels);
106 :
107 2022 : for (unsigned int i = 0; i < _n_channels; i++)
108 : {
109 1980 : _subchannel_position[i].reserve(3);
110 7920 : for (unsigned int j = 0; j < 3; j++)
111 : {
112 5940 : _subchannel_position.at(i).push_back(0.0);
113 : }
114 : }
115 :
116 : // create the subchannels
117 : k = 0; // initialize the subchannel counter index
118 132 : for (unsigned int i = 1; i < _n_rings; i++)
119 : {
120 : // find the closest Pin at back ring
121 954 : for (unsigned int j = 0; j < _pins_in_rings[i].size(); j++)
122 : {
123 864 : if (j == _pins_in_rings[i].size() - 1)
124 : {
125 90 : _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
126 90 : _chan_to_pin_map[k].push_back(_pins_in_rings[i][0]);
127 90 : avg_coor_x =
128 90 : 0.5 * (_pin_position[_pins_in_rings[i][j]](0) + _pin_position[_pins_in_rings[i][0]](0));
129 90 : avg_coor_y =
130 90 : 0.5 * (_pin_position[_pins_in_rings[i][j]](1) + _pin_position[_pins_in_rings[i][0]](1));
131 : }
132 : else
133 : {
134 774 : _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
135 774 : _chan_to_pin_map[k].push_back(_pins_in_rings[i][j + 1]);
136 774 : avg_coor_x = 0.5 * (_pin_position[_pins_in_rings[i][j]](0) +
137 774 : _pin_position[_pins_in_rings[i][j + 1]](0));
138 774 : avg_coor_y = 0.5 * (_pin_position[_pins_in_rings[i][j]](1) +
139 774 : _pin_position[_pins_in_rings[i][j + 1]](1));
140 : }
141 : dist0 = 1.0e+5;
142 864 : _chan_to_pin_map[k].push_back(_pins_in_rings[i - 1][0]);
143 5436 : for (unsigned int l = 0; l < _pins_in_rings[i - 1].size(); l++)
144 : {
145 4572 : dist = std::sqrt(pow(_pin_position[_pins_in_rings[i - 1][l]](0) - avg_coor_x, 2) +
146 4572 : pow(_pin_position[_pins_in_rings[i - 1][l]](1) - avg_coor_y, 2));
147 :
148 4572 : if (dist < dist0)
149 : {
150 2052 : _chan_to_pin_map[k][2] = _pins_in_rings[i - 1][l];
151 : dist0 = dist;
152 : }
153 : }
154 864 : _subch_type[k] = EChannelType::CENTER;
155 864 : _orientation_map.insert(std::make_pair(k, 0.0));
156 864 : k = k + 1;
157 : }
158 :
159 : // find the closest Pin at front ring
160 954 : for (unsigned int j = 0; j < _pins_in_rings[i].size(); j++)
161 : {
162 864 : if (j == _pins_in_rings[i].size() - 1)
163 : {
164 90 : _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
165 90 : _chan_to_pin_map[k].push_back(_pins_in_rings[i][0]);
166 90 : avg_coor_x =
167 90 : 0.5 * (_pin_position[_pins_in_rings[i][j]](0) + _pin_position[_pins_in_rings[i][0]](0));
168 90 : avg_coor_y =
169 90 : 0.5 * (_pin_position[_pins_in_rings[i][j]](1) + _pin_position[_pins_in_rings[i][0]](1));
170 : }
171 : else
172 : {
173 774 : _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
174 774 : _chan_to_pin_map[k].push_back(_pins_in_rings[i][j + 1]);
175 774 : avg_coor_x = 0.5 * (_pin_position[_pins_in_rings[i][j]](0) +
176 774 : _pin_position[_pins_in_rings[i][j + 1]](0));
177 774 : avg_coor_y = 0.5 * (_pin_position[_pins_in_rings[i][j]](1) +
178 774 : _pin_position[_pins_in_rings[i][j + 1]](1));
179 : }
180 : // if the outermost ring, set the edge subchannels first... then the corner subchannels
181 864 : if (i == _n_rings - 1)
182 : {
183 : // add edges
184 540 : _subch_type[k] = EChannelType::EDGE; // an edge subchannel is created
185 540 : k = k + 1;
186 540 : if (j % i == 0)
187 : {
188 : // corner subchannel
189 252 : _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
190 252 : _subch_type[k] = EChannelType::CORNER;
191 252 : k = k + 1;
192 : }
193 : // if not the outer most ring
194 : }
195 : else
196 : {
197 : dist0 = 1.0e+5;
198 324 : _chan_to_pin_map[k].push_back(_pins_in_rings[i + 1][0]);
199 4644 : for (unsigned int l = 0; l < _pins_in_rings[i + 1].size(); l++)
200 : {
201 4320 : dist = std::sqrt(pow(_pin_position[_pins_in_rings[i + 1][l]](0) - avg_coor_x, 2) +
202 4320 : pow(_pin_position[_pins_in_rings[i + 1][l]](1) - avg_coor_y, 2));
203 4320 : if (dist < dist0)
204 : {
205 1566 : _chan_to_pin_map[k][2] = _pins_in_rings[i + 1][l];
206 : dist0 = dist;
207 : }
208 : }
209 324 : _subch_type[k] = EChannelType::CENTER;
210 324 : _orientation_map.insert(std::make_pair(k, libMesh::pi));
211 324 : k = k + 1;
212 : }
213 : }
214 : }
215 :
216 2022 : for (auto & pin : _chan_to_pin_map)
217 : pin.shrink_to_fit();
218 :
219 : // set the subchannel positions
220 42 : Real _duct_to_pin_gap =
221 42 : 0.5 * (_flat_to_flat - (_n_rings - 1) * _pitch * std::sqrt(3.0) - _pin_diameter);
222 2022 : for (unsigned int i = 0; i < _n_channels; i++)
223 : {
224 1980 : if (_subch_type[i] == EChannelType::CENTER)
225 : {
226 1188 : _subchannel_position[i][0] =
227 1188 : (_pin_position[_chan_to_pin_map[i][0]](0) + _pin_position[_chan_to_pin_map[i][1]](0) +
228 1188 : _pin_position[_chan_to_pin_map[i][2]](0)) /
229 : 3.0;
230 1188 : _subchannel_position[i][1] =
231 1188 : (_pin_position[_chan_to_pin_map[i][0]](1) + _pin_position[_chan_to_pin_map[i][1]](1) +
232 1188 : _pin_position[_chan_to_pin_map[i][2]](1)) /
233 : 3.0;
234 : }
235 792 : else if (_subch_type[i] == EChannelType::EDGE)
236 : {
237 27108 : for (unsigned int j = 0; j < _n_channels; j++)
238 : {
239 26568 : if (_subch_type[j] == EChannelType::CENTER &&
240 16200 : ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][0] &&
241 540 : _chan_to_pin_map[i][1] == _chan_to_pin_map[j][1]) ||
242 15660 : (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][1] &&
243 540 : _chan_to_pin_map[i][1] == _chan_to_pin_map[j][0])))
244 : {
245 540 : x0 = _pin_position[_chan_to_pin_map[j][2]](0);
246 540 : y0 = _pin_position[_chan_to_pin_map[j][2]](1);
247 : }
248 26028 : else if (_subch_type[j] == EChannelType::CENTER &&
249 15660 : ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][0] &&
250 0 : _chan_to_pin_map[i][1] == _chan_to_pin_map[j][2]) ||
251 15660 : (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][2] &&
252 288 : _chan_to_pin_map[i][1] == _chan_to_pin_map[j][0])))
253 : {
254 0 : x0 = _pin_position[_chan_to_pin_map[j][1]](0);
255 0 : y0 = _pin_position[_chan_to_pin_map[j][1]](1);
256 : }
257 26028 : else if (_subch_type[j] == EChannelType::CENTER &&
258 15660 : ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][1] &&
259 540 : _chan_to_pin_map[i][1] == _chan_to_pin_map[j][2]) ||
260 15660 : (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][2] &&
261 288 : _chan_to_pin_map[i][1] == _chan_to_pin_map[j][1])))
262 : {
263 0 : x0 = _pin_position[_chan_to_pin_map[j][0]](0);
264 0 : y0 = _pin_position[_chan_to_pin_map[j][0]](1);
265 : }
266 26568 : x1 = 0.5 *
267 26568 : (_pin_position[_chan_to_pin_map[i][0]](0) + _pin_position[_chan_to_pin_map[i][1]](0));
268 26568 : y1 = 0.5 *
269 26568 : (_pin_position[_chan_to_pin_map[i][0]](1) + _pin_position[_chan_to_pin_map[i][1]](1));
270 26568 : a1 = _pin_diameter / 2.0 + _duct_to_pin_gap / 2.0;
271 26568 : a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
272 26568 : _subchannel_position[i][0] = (a2 * x1 - a1 * x0) / (a2 - a1);
273 26568 : _subchannel_position[i][1] = (a2 * y1 - a1 * y0) / (a2 - a1);
274 : } // j
275 : }
276 252 : else if (_subch_type[i] == EChannelType::CORNER)
277 : {
278 252 : x0 = _pin_position[0](0);
279 252 : y0 = _pin_position[0](1);
280 252 : x1 = _pin_position[_chan_to_pin_map[i][0]](0);
281 252 : y1 = _pin_position[_chan_to_pin_map[i][0]](1);
282 252 : a1 = _pin_diameter / 2.0 + _duct_to_pin_gap / 2.0;
283 252 : a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
284 252 : _subchannel_position[i][0] = (a2 * x1 - a1 * x0) / (a2 - a1);
285 252 : _subchannel_position[i][1] = (a2 * y1 - a1 * y0) / (a2 - a1);
286 : }
287 :
288 : /// Special case _n_rings == 1
289 1980 : if (_n_rings == 1)
290 : {
291 0 : for (unsigned int i = 0; i < _n_channels; i++)
292 : {
293 0 : Real angle = (2 * i + 1) * libMesh::pi / 6.0;
294 0 : _subch_type[i] = EChannelType::CORNER;
295 0 : _subchannel_position[i][0] = std::cos(angle) * _flat_to_flat / 2.0;
296 0 : _subchannel_position[i][1] = std::sin(angle) * _flat_to_flat / 2.0;
297 : }
298 : }
299 : }
300 42 : }
301 :
302 : std::unique_ptr<MeshBase>
303 42 : SCMDetailedTriSubChannelMeshGenerator::generate()
304 : {
305 42 : auto mesh_base = buildMeshBaseObject();
306 : BoundaryInfo & boundary_info = mesh_base->get_boundary_info();
307 42 : mesh_base->set_spatial_dimension(3);
308 : // Define the resolution (the number of points used to represent a circle).
309 : // This must be divisible by 4.
310 : const unsigned int theta_res_triangle = 24; // TODO: parameterize
311 : const unsigned int theta_res_square = 24; // TODO: parameterize
312 : // Compute the number of points needed to represent one sixth and quadrant of a circle.
313 : const unsigned int points_per_sixth = theta_res_triangle / 6 + 1;
314 : const unsigned int points_per_quadrant = theta_res_square / 4 + 1;
315 :
316 : // Compute the points needed to represent one axial cross-flow of a subchannel.
317 : // For the center subchannel (sc) there is one center point plus the points from 3 side
318 : // circles.
319 42 : const unsigned int points_per_center = points_per_sixth * 3 + 1;
320 : // For the corner sc there is one center point plus the points from 1 side circle plus 3
321 : // corners
322 42 : const unsigned int points_per_corner = points_per_sixth * 1 + 1 + 3;
323 : // For the side sc there is one center point plus the points from 2 intersecting circles plus 2
324 : // corners
325 42 : const unsigned int points_per_side = points_per_quadrant * 2 + 1 + 2;
326 :
327 42 : if (_verbose)
328 : {
329 9 : _console << "Points per center: " << points_per_center << std::endl;
330 9 : _console << "Points per side: " << points_per_side << std::endl;
331 9 : _console << "Points per corner: " << points_per_corner << std::endl;
332 : }
333 :
334 : // Compute the number of elements (Prism6) which combined base creates the sub-channel
335 : // cross-section
336 42 : const unsigned int elems_per_center = theta_res_triangle * 3 / 6 + 3; // TODO: check
337 42 : const unsigned int elems_per_corner = theta_res_triangle / 6 + 4;
338 42 : const unsigned int elems_per_side = 2 * theta_res_square / 4 + 4;
339 42 : if (_verbose)
340 : {
341 9 : _console << "Elems per center: " << elems_per_center << std::endl;
342 9 : _console << "Elems per side: " << elems_per_side << std::endl;
343 9 : _console << "Elems per corner: " << elems_per_corner << std::endl;
344 : }
345 :
346 : // specify number and type of sub-channel
347 : unsigned int n_center, n_side, n_corner;
348 42 : if (_n_rings == 1)
349 : {
350 0 : n_corner = 6;
351 0 : n_side = 0;
352 0 : n_center = _n_channels - n_side - n_corner;
353 : }
354 : else
355 : {
356 42 : n_corner = 6;
357 42 : n_side = (_n_rings - 1) * 6;
358 42 : n_center = _n_channels - n_side - n_corner;
359 : }
360 42 : if (_verbose)
361 : {
362 9 : _console << "Centers: " << n_center << std::endl;
363 9 : _console << "Sides: " << n_side << std::endl;
364 9 : _console << "Corners: " << n_corner << std::endl;
365 : }
366 :
367 : // Compute the total number of points and elements.
368 42 : const unsigned int points_per_level =
369 42 : n_corner * points_per_corner + n_side * points_per_side + n_center * points_per_center;
370 42 : const unsigned int elems_per_level =
371 42 : n_corner * elems_per_corner + n_side * elems_per_side + n_center * elems_per_center;
372 42 : if (_verbose)
373 : {
374 9 : _console << "Points per level: " << points_per_level << std::endl;
375 9 : _console << "Elements per level: " << elems_per_level << std::endl;
376 : }
377 42 : const unsigned int n_points = points_per_level * (_n_cells + 1);
378 42 : const unsigned int n_elems = elems_per_level * _n_cells;
379 42 : if (_verbose)
380 : {
381 9 : _console << "Number of points: " << n_points << std::endl;
382 9 : _console << "Number of elements: " << n_elems << std::endl;
383 : }
384 42 : mesh_base->reserve_nodes(n_points);
385 42 : mesh_base->reserve_elem(n_elems);
386 : // Build an array of points arranged in a circle on the xy-plane. (last and first node overlap)
387 : // We build for both the square discretization in the edges and the triangular discretization
388 : // within the mesh
389 42 : const Real radius = _pin_diameter / 2.0;
390 : std::array<Point, theta_res_square + 1> circle_points_square;
391 : {
392 : Real theta = 0;
393 1092 : for (unsigned int i = 0; i < theta_res_square + 1; i++)
394 : {
395 1050 : circle_points_square[i](0) = radius * std::cos(theta);
396 1050 : circle_points_square[i](1) = radius * std::sin(theta);
397 1050 : theta += 2.0 * libMesh::pi / theta_res_square;
398 : }
399 : }
400 : std::array<Point, theta_res_triangle + 1> circle_points_triangle;
401 : {
402 : Real theta = 0;
403 1092 : for (unsigned int i = 0; i < theta_res_triangle + 1; i++)
404 : {
405 1050 : circle_points_triangle[i](0) = radius * std::cos(theta);
406 1050 : circle_points_triangle[i](1) = radius * std::sin(theta);
407 1050 : theta += 2.0 * libMesh::pi / theta_res_triangle;
408 : }
409 : }
410 : // Define "quadrant center" reference points. These will be the centers of
411 : // the 3 circles that represent the fuel pins. These centers are
412 : // offset a little bit so that in the final mesh, there is a tiny gap between
413 : // neighboring subchannel cells. That allows us to easily map a solution to
414 : // this detailed mesh with a nearest-neighbor search.
415 : const Real shrink_factor = 0.99999;
416 : // Quadrants are used only for the side and corner subchannels
417 42 : Real _duct_to_pin_gap =
418 42 : 0.5 * (_flat_to_flat - (_n_rings - 1) * _pitch * std::sqrt(3.0) - _pin_diameter);
419 : std::array<Point, 2> quadrant_centers_sides;
420 42 : quadrant_centers_sides[0] = Point(
421 42 : -_pitch * 0.5 * shrink_factor, -(_duct_to_pin_gap + _pin_diameter) * 0.5 * shrink_factor, 0);
422 42 : 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 42 : quadrant_centers_corner[0] =
426 42 : Point(-(_duct_to_pin_gap + _pin_diameter) * 0.5 * std::sin(libMesh::pi / 6) * shrink_factor,
427 42 : -(_duct_to_pin_gap + _pin_diameter) * 0.5 * std::cos(libMesh::pi / 6) * shrink_factor,
428 : 0);
429 : // Triangles are used for all center subchannels
430 : std::array<Point, 3> triangle_centers;
431 42 : triangle_centers[0] = Point(0, _pitch * std::cos(libMesh::pi / 6) * 2 / 3 * shrink_factor, 0);
432 42 : triangle_centers[1] = Point(-_pitch * 0.5 * shrink_factor,
433 42 : -_pitch * std::cos(libMesh::pi / 6) * 1 / 3 * shrink_factor,
434 : 0);
435 42 : triangle_centers[2] = Point(
436 : _pitch * 0.5 * shrink_factor, -_pitch * std::cos(libMesh::pi / 6) * 1 / 3 * shrink_factor, 0);
437 :
438 : const unsigned int m_sixth = theta_res_triangle / 6;
439 : const unsigned int m_quarter = theta_res_square / 4;
440 : // Build an array of points that represent a cross section of a center subchannel
441 : // cell. The points are ordered in this fashion:
442 : // 3 1
443 : // 2
444 : // 0
445 : // 4 5 8 9
446 : // 6 7
447 : std::array<Point, points_per_center> center_points;
448 : {
449 : unsigned int start;
450 168 : for (unsigned int i = 0; i < 3; i++)
451 : {
452 126 : if (i == 0)
453 : start = 5 * (m_sixth);
454 84 : if (i == 1)
455 : start = 1 * (m_sixth);
456 84 : if (i == 2)
457 : start = 3 * (m_sixth);
458 756 : for (unsigned int ii = 0; ii < points_per_sixth; ii++)
459 : {
460 630 : auto c_pt = circle_points_triangle[start - ii];
461 630 : center_points[i * points_per_sixth + ii + 1] = triangle_centers[i] + c_pt;
462 : }
463 : }
464 : }
465 :
466 : // Build an array of points that represent a cross section of a top left corner subchannel
467 : // cell. The points are ordered in this fashion (x direction towards point 4, y direction towards
468 : // point 6):
469 : // 5 6
470 : //
471 : // 0
472 : // 4 2 3
473 : // 1
474 : std::array<Point, points_per_corner> corner_points;
475 : {
476 252 : for (unsigned int ii = 0; ii < points_per_sixth; ii++)
477 : {
478 210 : auto c_pt = circle_points_triangle[1 * m_quarter - ii];
479 210 : corner_points[ii + 1] = quadrant_centers_corner[0] + c_pt;
480 : }
481 : Real side_short = (_duct_to_pin_gap + _pin_diameter) * 0.5;
482 42 : Real side_long = (2.0 * _duct_to_pin_gap + _pin_diameter) * 0.5;
483 42 : Real side_length = std::sqrt(std::pow(side_short, 2) + std::pow(side_long, 2) -
484 42 : 2 * side_short * side_long * std::cos(libMesh::pi / 6));
485 : Real angle =
486 : libMesh::pi - libMesh::pi / 3 -
487 42 : std::acos((-std::pow(side_long, 2) + std::pow(side_short, 2) + std::pow(side_length, 2)) /
488 42 : (2 * side_short * side_length));
489 42 : corner_points[points_per_sixth + 1] = Point(side_length * std::cos(angle) * shrink_factor,
490 42 : -side_length * std::sin(angle) * shrink_factor,
491 : 0);
492 42 : corner_points[points_per_sixth + 2] =
493 42 : Point(side_length * std::sin(libMesh::pi / 2 - angle - libMesh::pi / 6) * shrink_factor *
494 : std::tan(libMesh::pi / 6),
495 42 : side_length * std::sin(libMesh::pi / 2 - angle - libMesh::pi / 6) * shrink_factor,
496 : 0);
497 42 : corner_points[points_per_sixth + 3] =
498 42 : Point(-side_length * std::cos(libMesh::pi / 2 - angle - libMesh::pi / 6) * shrink_factor,
499 : side_length * std::sin(libMesh::pi / 2 - angle - libMesh::pi / 6) * shrink_factor,
500 : 0);
501 : }
502 :
503 : // Build an array of points that represent a cross-section of a top side subchannel
504 : // cell. The points are ordered in this fashion:
505 : // 8 7
506 : //
507 : // 0
508 : // 1 2 5 6
509 : // 3 4
510 : std::array<Point, points_per_side> side_points;
511 : {
512 336 : for (unsigned int ii = 0; ii < points_per_quadrant; ii++)
513 : {
514 294 : auto c_pt = circle_points_square[m_quarter - ii];
515 294 : side_points[ii + 1] = quadrant_centers_sides[0] + c_pt;
516 : }
517 336 : for (unsigned int ii = 0; ii < points_per_quadrant; ii++)
518 : {
519 294 : auto c_pt = circle_points_square[2 * m_quarter - ii];
520 294 : side_points[points_per_quadrant + ii + 1] = quadrant_centers_sides[1] + c_pt;
521 : }
522 42 : side_points[2 * points_per_quadrant + 1] =
523 42 : Point(_pitch * 0.5 * shrink_factor, 0.5 * _duct_to_pin_gap * shrink_factor, 0);
524 42 : side_points[2 * points_per_quadrant + 2] =
525 : Point(-_pitch * 0.5 * shrink_factor, 0.5 * _duct_to_pin_gap * shrink_factor, 0);
526 : }
527 :
528 42 : int point_counter = 0;
529 : unsigned int node_id = 0;
530 2022 : for (unsigned int i = 0; i < _n_channels; i++)
531 : {
532 : // Real offset_x = _flat_to_flat / 2.0;
533 : // Real offset_y = _flat_to_flat / 2.0;
534 :
535 1980 : if (getSubchannelType(i) == EChannelType::CENTER)
536 : {
537 37656 : for (auto z : _z_grid)
538 : {
539 : // Get height
540 : Point z0{0, 0, z};
541 :
542 : // Get suchannel position and assign to point
543 36468 : auto loc_position = getSubchannelPosition(i);
544 36468 : Point p0{loc_position[0], loc_position[1], 0};
545 :
546 : // Determine orientation of current subchannel
547 36468 : auto subchannel_pins = getSubChannelPins(i);
548 : Point subchannel_side =
549 36468 : getPinPosition(subchannel_pins[0]) + getPinPosition(subchannel_pins[1]);
550 : Point base_center_orientation = {0, -1};
551 :
552 : // Get rotation angle for current subchannel
553 : Real dot_prod = 0;
554 109404 : for (unsigned int lp = 0; lp < 2; lp++)
555 72936 : dot_prod += base_center_orientation(lp) * subchannel_side(lp);
556 : auto theta =
557 36468 : std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
558 36468 : if (subchannel_side(0) < 0)
559 18234 : theta = 2.0 * libMesh::pi - theta;
560 :
561 : // Real distance_side = subchannel_side.norm();
562 : // Real distance_top = getPinPosition(subchannel_pins[2]).norm();
563 : // if (distance_top > distance_side)
564 : // theta += libMesh::pi * 0.0;
565 :
566 36468 : theta += _orientation_map[i];
567 :
568 36468 : theta = trunc((theta + (libMesh::pi / 6.0)) / (libMesh::pi / 3.0)) * libMesh::pi / 3.0;
569 :
570 36468 : if (_verbose)
571 : {
572 4536 : if (z == 0)
573 : {
574 216 : _console << "Subchannel Position: " << p0 << std::endl;
575 216 : auto pins = getSubChannelPins(i);
576 864 : for (auto r : pins)
577 648 : _console << r << " ";
578 216 : _console << std::endl;
579 216 : _console << "Theta: " << theta / libMesh::pi * 180. << std::endl;
580 216 : }
581 : }
582 :
583 : // Assigning points for center channels
584 619956 : for (unsigned int i = 0; i < points_per_center; i++)
585 : {
586 583488 : auto new_point = rotatePoint(center_points[i], theta) + p0 + z0;
587 583488 : if (_verbose)
588 : {
589 72576 : if (z == 0)
590 3456 : _console << i << " - " << new_point << std::endl;
591 : }
592 583488 : mesh_base->add_point(new_point, node_id++);
593 583488 : point_counter += 1;
594 : }
595 36468 : }
596 : }
597 792 : else if (getSubchannelType(i) == EChannelType::EDGE)
598 : {
599 17640 : for (auto z : _z_grid)
600 : {
601 : // Get height
602 : Point z0{0, 0, z};
603 :
604 : // Get suchannel position and assign to point
605 17100 : auto loc_position = getSubchannelPosition(i);
606 17100 : Point p0{loc_position[0], loc_position[1], 0};
607 :
608 : // Determine orientation of current subchannel
609 17100 : auto subchannel_pins = getSubChannelPins(i);
610 : Point subchannel_side =
611 17100 : getPinPosition(subchannel_pins[0]) + getPinPosition(subchannel_pins[1]);
612 : Point base_center_orientation = {0, 1};
613 :
614 : // Get rotation angle for current subchannel
615 : Real dot_prod = 0;
616 51300 : for (unsigned int lp = 0; lp < 2; lp++)
617 34200 : dot_prod += base_center_orientation(lp) * subchannel_side(lp);
618 : auto theta =
619 17100 : std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
620 17100 : if (subchannel_side(0) > 0)
621 8550 : theta = 2. * libMesh::pi - theta;
622 17100 : theta = trunc((theta + (libMesh::pi / 6.0)) / (libMesh::pi / 3.0)) * libMesh::pi / 3.0;
623 :
624 17100 : if (_verbose)
625 : {
626 2268 : if (z == 0)
627 : {
628 108 : _console << "Subchannel Position: " << p0 << std::endl;
629 108 : auto pins = getSubChannelPins(i);
630 324 : for (auto r : pins)
631 216 : _console << r << " ";
632 108 : _console << std::endl;
633 108 : _console << "Theta: " << theta * 180 / libMesh::pi << std::endl;
634 108 : }
635 : }
636 :
637 : // Assigning points for center channels
638 307800 : for (unsigned int i = 0; i < points_per_side; i++)
639 : {
640 290700 : auto new_point = rotatePoint(side_points[i], theta) + p0 + z0;
641 290700 : if (_verbose)
642 : {
643 38556 : if (z == 0)
644 1836 : _console << i << " - " << new_point << std::endl;
645 : }
646 290700 : mesh_base->add_point(new_point, node_id++);
647 290700 : point_counter += 1;
648 : }
649 17100 : }
650 : }
651 : else // getSubchannelType(i) == EChannelType::CORNER
652 : {
653 8424 : for (auto z : _z_grid)
654 : {
655 : // Get height
656 : Point z0{0, 0, z};
657 :
658 : // Get suchannel position and assign to point
659 8172 : auto loc_position = getSubchannelPosition(i);
660 8172 : Point p0{loc_position[0], loc_position[1], 0};
661 :
662 : // Determine orientation of current subchannel
663 8172 : auto subchannel_pins = getSubChannelPins(i);
664 8172 : Point subchannel_side = getPinPosition(subchannel_pins[0]);
665 : Point base_center_orientation = {1, 1};
666 :
667 : // Get rotation angle for current subchannel
668 : Real dot_prod = 0;
669 24516 : for (unsigned int lp = 0; lp < 2; lp++)
670 16344 : dot_prod += base_center_orientation(lp) * subchannel_side(lp);
671 : auto theta =
672 8172 : std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
673 8172 : if (subchannel_side(0) > 0)
674 4086 : theta = 2. * libMesh::pi - theta;
675 8172 : theta = trunc((theta + (libMesh::pi / 6.0)) / (libMesh::pi / 3.0)) * libMesh::pi / 3.0;
676 :
677 8172 : if (_verbose)
678 : {
679 1134 : if (z == 0)
680 : {
681 54 : _console << "Subchannel Position: " << p0 << std::endl;
682 54 : auto pins = getSubChannelPins(i);
683 108 : for (auto r : pins)
684 54 : _console << r << " ";
685 54 : _console << std::endl;
686 54 : _console << "Theta: " << theta * 180 / libMesh::pi << std::endl;
687 54 : }
688 : }
689 :
690 : // Assigning points for center channels
691 81720 : for (unsigned int i = 0; i < points_per_corner; i++)
692 : {
693 73548 : auto new_point = rotatePoint(corner_points[i], theta) + p0 + z0;
694 73548 : if (_verbose)
695 : {
696 10206 : if (z == 0)
697 486 : _console << i << " - " << new_point << std::endl;
698 : }
699 73548 : mesh_base->add_point(new_point, node_id++);
700 73548 : point_counter += 1;
701 : }
702 8172 : }
703 : }
704 : } // i
705 42 : if (_verbose)
706 9 : _console << "Point counter: " << point_counter << std::endl;
707 :
708 42 : 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;
715 2022 : for (unsigned int i = 0; i < _n_channels; i++)
716 : {
717 : auto subch_type = getSubchannelType(i);
718 1980 : if (subch_type == EChannelType::CORNER)
719 : {
720 252 : number_of_corner++;
721 : elems_per_channel = elems_per_corner;
722 : points_per_channel = points_per_corner;
723 252 : if (_verbose)
724 54 : _console << "Corner" << std::endl;
725 : }
726 1728 : else if (subch_type == EChannelType::EDGE)
727 : {
728 540 : number_of_side++;
729 : elems_per_channel = elems_per_side;
730 : points_per_channel = points_per_side;
731 540 : if (_verbose)
732 108 : _console << "Edge" << std::endl;
733 : }
734 1188 : else if (subch_type == EChannelType::CENTER)
735 : {
736 1188 : number_of_center++;
737 : elems_per_channel = elems_per_center;
738 : points_per_channel = points_per_center;
739 1188 : if (_verbose)
740 216 : _console << "Center" << std::endl;
741 : }
742 61740 : for (unsigned int iz = 0; iz < _n_cells; iz++)
743 : {
744 59760 : unsigned int elapsed_points = number_of_corner * points_per_corner * (_n_cells + 1) +
745 59760 : number_of_side * points_per_side * (_n_cells + 1) +
746 59760 : number_of_center * points_per_center * (_n_cells + 1) -
747 59760 : points_per_channel * (_n_cells + 1);
748 : // index of the central node at base of cell
749 59760 : unsigned int indx1 = iz * points_per_channel + elapsed_points;
750 : // index of the central node at top of cell
751 59760 : unsigned int indx2 = (iz + 1) * points_per_channel + elapsed_points;
752 :
753 917280 : for (unsigned int i = 0; i < elems_per_channel; i++)
754 : {
755 857520 : Elem * elem = new Prism6;
756 857520 : elem->subdomain_id() = _block_id;
757 857520 : elem->set_id(elem_id++);
758 857520 : elem = mesh_base->add_elem(elem);
759 :
760 857520 : if (_verbose)
761 108000 : _console << "Node 0: " << *mesh_base->node_ptr(indx1) << std::endl;
762 :
763 857520 : elem->set_node(0, mesh_base->node_ptr(indx1));
764 857520 : elem->set_node(1, mesh_base->node_ptr(indx1 + i + 1));
765 857520 : if (i != elems_per_channel - 1)
766 797760 : elem->set_node(2, mesh_base->node_ptr(indx1 + i + 2));
767 : else
768 59760 : elem->set_node(2, mesh_base->node_ptr(indx1 + 1));
769 :
770 857520 : elem->set_node(3, mesh_base->node_ptr(indx2));
771 857520 : elem->set_node(4, mesh_base->node_ptr(indx2 + i + 1));
772 857520 : if (i != elems_per_channel - 1)
773 797760 : elem->set_node(5, mesh_base->node_ptr(indx2 + i + 2));
774 : else
775 59760 : elem->set_node(5, mesh_base->node_ptr(indx2 + 1));
776 :
777 857520 : if (iz == 0)
778 28476 : boundary_info.add_side(elem, 0, 0);
779 857520 : if (iz == _n_cells - 1)
780 28476 : boundary_info.add_side(elem, 4, 1);
781 :
782 857520 : element_counter += 1;
783 : }
784 : }
785 : }
786 42 : if (_verbose)
787 9 : _console << "Element counter: " << element_counter << std::endl;
788 42 : boundary_info.sideset_name(0) = "inlet";
789 42 : boundary_info.sideset_name(1) = "outlet";
790 42 : mesh_base->subdomain_name(_block_id) = name();
791 42 : if (_verbose)
792 9 : _console << "Mesh assembly done" << std::endl;
793 42 : mesh_base->prepare_for_use();
794 :
795 42 : return mesh_base;
796 0 : }
797 :
798 : Point
799 947736 : SCMDetailedTriSubChannelMeshGenerator::rotatePoint(Point b, Real theta)
800 : {
801 :
802 : // Building rotation matrix
803 : std::vector<std::vector<Real>> A;
804 947736 : A.resize(3);
805 3790944 : for (std::vector<Real> a : A)
806 : {
807 2843208 : a.resize(3);
808 2843208 : }
809 :
810 947736 : A[0] = {std::cos(theta), -std::sin(theta), 0.0};
811 947736 : A[1] = {std::sin(theta), std::cos(theta), 0.0};
812 947736 : A[2] = {0.0, 0.0, 1.0};
813 :
814 : // Rotating vector
815 : Point rotated_vector = Point(0.0, 0.0, 0.0);
816 3790944 : for (unsigned int i = 0; i < 3; i++)
817 : {
818 11372832 : for (unsigned int j = 0; j < 3; j++)
819 : {
820 8529624 : rotated_vector(i) += A[i][j] * b(j);
821 : }
822 : }
823 :
824 947736 : return rotated_vector;
825 947736 : }
826 :
827 : Point
828 0 : SCMDetailedTriSubChannelMeshGenerator::translatePoint(Point & b, Point & translation_vector)
829 : {
830 : // Translating point
831 : Point translated_vector = Point(0.0, 0.0, 0.0);
832 0 : for (unsigned int i = 0; i < 3; i++)
833 : {
834 0 : translated_vector(i) = b(i) + translation_vector(i);
835 : }
836 :
837 0 : return translated_vector;
838 : }
|