https://mooseframework.inl.gov
Loading...
Searching...
No Matches
SCMTriAssemblyMeshGenerator.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 "MooseUtils.h"
13#include <cmath>
14#include <memory>
15#include "libmesh/edge_edge2.h"
16#include "libmesh/unstructured_mesh.h"
17
20 SCMTriSubChannelMeshGenerator,
21 "06/30/2027 24:00",
24 TriSubChannelMeshGenerator,
25 "06/30/2027 24:00",
28 SCMTriPinMeshGenerator,
29 "06/30/2027 24:00",
32 TriPinMeshGenerator,
33 "06/30/2027 24:00",
35
38{
41 "Creates a mesh of 1D subchannels and 1D pins in a triangular lattice arrangement");
42 params.addRequiredParam<unsigned int>("n_cells", "The number of cells in the axial direction");
43 params.addRequiredParam<Real>("pitch", "Pitch [m]");
44 params.addRequiredParam<Real>("pin_diameter", "Rod diameter [m]");
45 params.addParam<Real>("unheated_length_entry", 0.0, "Unheated length at entry [m]");
46 params.addRequiredParam<Real>("heated_length", "Heated length [m]");
47 params.addParam<Real>("unheated_length_exit", 0.0, "Unheated length at exit [m]");
48 params.addRequiredParam<unsigned int>(
49 "nrings",
50 "Number of fuel-pin rings per assembly, counting the center pin as the first ring [-]");
51 params.addRequiredParam<Real>("flat_to_flat",
52 "Flat to flat distance for the hexagonal assembly [m]");
53 params.addRequiredParam<Real>("dwire", "Wire diameter [m]");
54 params.addRequiredParam<Real>("hwire", "Wire lead length [m]");
55 params.addParam<std::vector<Real>>(
56 "spacer_z", {}, "Axial location of spacers/vanes/mixing vanes [m]");
57 params.addParam<std::vector<Real>>(
58 "spacer_k", {}, "K-loss coefficient of spacers/vanes/mixing vanes [-]");
59 params.addParam<Real>("Kij", 0.5, "Lateral form loss coefficient [-]");
60 params.addParam<std::vector<Real>>("z_blockage",
61 std::vector<Real>({0.0, 0.0}),
62 "axial location of blockage (inlet, outlet) [m]");
63 params.addParam<std::vector<unsigned int>>("index_blockage",
64 std::vector<unsigned int>({0}),
65 "index of subchannels affected by blockage");
66 params.addParam<std::vector<Real>>(
67 "reduction_blockage",
68 std::vector<Real>({1.0}),
69 "Area reduction of subchannels affected by blockage (number to muliply the area)");
70 params.addParam<std::vector<Real>>("k_blockage",
71 std::vector<Real>({0.0}),
72 "Form loss coefficient of subchannels affected by blockage");
73 params.addParam<unsigned int>("block_id", 0, "Subchannel block id");
74 params.deprecateParam("block_id", "subchannel_block_id", "07/01/2027");
75 params.addParam<unsigned int>("pin_block_id", 1, "Fuel Pin block id");
76 return params;
77}
78
80 : MeshGenerator(params),
81 _unheated_length_entry(getParam<Real>("unheated_length_entry")),
82 _heated_length(getParam<Real>("heated_length")),
83 _unheated_length_exit(getParam<Real>("unheated_length_exit")),
84 _subchannel_block_id(getParam<unsigned int>("subchannel_block_id")),
85 _pin_block_id(getParam<unsigned int>("pin_block_id")),
86 _spacer_z(getParam<std::vector<Real>>("spacer_z")),
87 _spacer_k(getParam<std::vector<Real>>("spacer_k")),
88 _z_blockage(getParam<std::vector<Real>>("z_blockage")),
89 _index_blockage(getParam<std::vector<unsigned int>>("index_blockage")),
90 _reduction_blockage(getParam<std::vector<Real>>("reduction_blockage")),
91 _k_blockage(getParam<std::vector<Real>>("k_blockage")),
92 _pitch(getParam<Real>("pitch")),
93 _kij(getParam<Real>("Kij")),
94 _pin_diameter(getParam<Real>("pin_diameter")),
95 _n_cells(getParam<unsigned int>("n_cells")),
96 _n_rings(getParam<unsigned int>("nrings")),
97 _n_channels(0),
98 _flat_to_flat(getParam<Real>("flat_to_flat")),
99 _dwire(getParam<Real>("dwire")),
100 _hwire(getParam<Real>("hwire")),
101 _duct_to_pin_gap(0.5 *
102 (_flat_to_flat - (_n_rings - 1) * _pitch * std::sqrt(3.0) - _pin_diameter)),
103 _npins(0),
104 _n_gaps(0)
105{
106 const Real total_length = _unheated_length_entry + _heated_length + _unheated_length_exit;
107
108 if (_n_rings < 2)
109 paramError("nrings",
110 "'nrings' must be at least 2. In this mesh generator, the center pin counts as "
111 "the first ring, so a 7-pin bundle uses nrings = 2.");
112
113 if (_n_cells == 0)
114 paramError("n_cells", "The number of axial cells must be greater than zero");
115
116 const Real pin_gap = _pitch - _pin_diameter;
117 if (pin_gap <= 0.0)
118 paramError("pitch", "The pin pitch must be greater than the pin diameter");
119
120 if (MooseUtils::absoluteFuzzyGreaterThan(_dwire, pin_gap))
121 paramError("dwire", "The wire diameter must not exceed the pin-to-pin gap");
122
123 if (total_length <= 0.0)
124 mooseError("Total bundle length must be greater than zero");
125
126 if (_spacer_z.size() != _spacer_k.size())
127 mooseError("Size of vector spacer_z should be equal to size of vector spacer_k");
128
129 for (const auto spacer_z : _spacer_z)
130 if (spacer_z < 0.0 || spacer_z > total_length)
131 paramError("spacer_z", "Location of spacers should be between zero and total bundle length");
132
133 if (_z_blockage.size() != 2)
134 paramError("z_blockage", "Size of vector z_blockage must be 2");
135
136 if (_z_blockage.front() > _z_blockage.back())
137 paramError("z_blockage", "z_blockage inlet location must not exceed outlet location");
138
139 if (*max_element(_reduction_blockage.begin(), _reduction_blockage.end()) > 1)
140 paramError("reduction_blockage",
141 "The area reduction of the blocked subchannels cannot be more than 1");
142
143 if ((_index_blockage.size() != _reduction_blockage.size()) ||
144 (_index_blockage.size() != _k_blockage.size()) ||
145 (_reduction_blockage.size() != _k_blockage.size()))
146 mooseError("Size of vectors: index_blockage, reduction_blockage, k_blockage, must be equal "
147 "to eachother");
148
151
152 // Defining the total length from 3 axial sections
153 Real L = total_length;
154
155 // Defining the position of the spacer grid in the numerical solution array
156 std::vector<int> spacer_cell;
157 for (const auto & elem : _spacer_z)
158 spacer_cell.emplace_back(std::round(elem * _n_cells / L));
159
160 // Defining the array for axial resistances
161 std::vector<Real> kgrid;
162 kgrid.resize(_n_cells + 1, 0.0);
163
164 // Summing the spacer resistance to the grid resistance array
165 for (unsigned int index = 0; index < spacer_cell.size(); index++)
166 kgrid[spacer_cell[index]] += _spacer_k[index];
167
168 // compute the hex mesh variables
169 // -------------------------------------------
170 // x coordinate for the first position
171 Real x0 = 0.0;
172 // y coordinate for the first position
173 Real y0 = 0.0;
174 // x coordinate for the second position
175 Real x1 = 0.0;
176 // y coordinate for the second position dummy variable
177 Real y1 = 0.0;
178 // dummy variable
179 Real a1 = 0.0;
180 // dummy variable
181 Real a2 = 0.0;
182 // average x coordinate
183 Real avg_coor_x = 0.0;
184 // average y coordinate
185 Real avg_coor_y = 0.0;
186 // distance between two points
187 Real dist = 0.0;
188 // distance between two points
189 Real dist0 = 0.0;
190 // integer counter
191 unsigned int kgap = 0;
192 // dummy integer
193 unsigned int icorner = 0;
194 // used to defined global direction of the cross_flow_map coefficients for each subchannel and gap
195 const Real positive_flow = 1.0;
196 // used to defined global direction of the cross_flow_map coefficients for each subchannel and gap
197 const Real negative_flow = -1.0;
198 // the indicator used while setting _gap_to_chan_map array
199 std::vector<std::pair<unsigned int, unsigned int>> gap_fill;
201 _npins = _pin_position.size();
202 // assign the pins to the corresponding rings
203 unsigned int k = 0; // initialize the fuel Pin counter index
204 _pins_in_rings.resize(_n_rings);
205 _pins_in_rings[0].push_back(k++);
206 for (unsigned int i = 1; i < _n_rings; i++)
207 for (unsigned int j = 0; j < i * 6; j++)
208 _pins_in_rings[i].push_back(k++);
209 // Given the number of pins and number of fuel Pin rings, the number of subchannels can be
210 // computed as follows:
211 unsigned int chancount = 0.0;
212 // Summing internal channels
213 for (unsigned int j = 0; j < _n_rings - 1; j++)
214 chancount += j * 6;
215 // Adding external channels to the total count
216 _n_channels = chancount + _npins - 1 + (_n_rings - 1) * 6 + 6;
217
218 if (*max_element(_index_blockage.begin(), _index_blockage.end()) > (_n_channels - 1))
219 paramError("index_blockage",
220 "The index of the blocked subchannel cannot be more than the max index of the "
221 "subchannels");
222
223 if ((_index_blockage.size() > _n_channels) || (_reduction_blockage.size() > _n_channels) ||
224 (_k_blockage.size() > _n_channels))
225 mooseError("Size of vectors: index_blockage, reduction_blockage, k_blockage, cannot be more "
226 "than the total number of subchannels");
227
228 // Defining the 2D array for axial resistances
229 _k_grid.resize(_n_channels, std::vector<Real>(_n_cells + 1));
230 for (unsigned int i = 0; i < _n_channels; i++)
231 _k_grid[i] = kgrid;
232
233 // Add blockage resistance to the 2D grid resistane array
234 Real dz = L / _n_cells;
235 for (unsigned int i = 0; i < _n_cells + 1; i++)
236 {
237 if ((dz * i >= _z_blockage.front() && dz * i <= _z_blockage.back()))
238 {
239 unsigned int index(0);
240 for (const auto & i_ch : _index_blockage)
241 {
242 _k_grid[i_ch][i] += _k_blockage[index];
243 index++;
244 }
245 }
246 }
247
249 _pin_to_chan_map.resize(_npins);
250 _subch_type.resize(_n_channels);
251 _n_gaps = _n_channels + _npins - 1;
253 _gap_to_pin_map.resize(_n_gaps);
254 gap_fill.resize(_n_gaps);
258 _gij_map.resize(_n_cells + 1);
260 _gap_type.resize(_n_gaps);
262
263 for (unsigned int i = 0; i < _n_channels; i++)
264 {
265 _chan_to_pin_map[i].reserve(3);
266 _chan_to_gap_map[i].reserve(3);
267 _sign_id_crossflow_map[i].reserve(3);
268 _subchannel_position[i].reserve(3);
269 for (unsigned int j = 0; j < 3; j++)
270 {
271 _sign_id_crossflow_map.at(i).push_back(positive_flow);
272 _subchannel_position.at(i).push_back(0.0);
273 }
274 }
275
276 for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
277 {
278 _gij_map[iz].reserve(_n_gaps);
279 }
280
281 for (unsigned int i = 0; i < _npins; i++)
282 _pin_to_chan_map[i].reserve(6);
283
284 // create the subchannels
285 k = 0; // initialize the subchannel counter index
286 kgap = 0;
287 // for each ring we trace the subchannels by pairing up to neighbor pins and looking for the third
288 // Pin at inner or outer ring compared to the current ring.
289 for (unsigned int i = 1; i < _n_rings; i++)
290 {
291 // find the closest Pin at back ring
292 for (unsigned int j = 0; j < _pins_in_rings[i].size(); j++)
293 {
294 if (j == _pins_in_rings[i].size() - 1)
295 {
296 _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
297 _chan_to_pin_map[k].push_back(_pins_in_rings[i][0]);
298 avg_coor_x =
299 0.5 * (_pin_position[_pins_in_rings[i][j]](0) + _pin_position[_pins_in_rings[i][0]](0));
300 avg_coor_y =
301 0.5 * (_pin_position[_pins_in_rings[i][j]](1) + _pin_position[_pins_in_rings[i][0]](1));
302 _gap_to_pin_map[kgap].first = _pins_in_rings[i][0];
303 _gap_to_pin_map[kgap].second = _pins_in_rings[i][j];
305 kgap = kgap + 1;
306 }
307 else
308 {
309 _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
310 _chan_to_pin_map[k].push_back(_pins_in_rings[i][j + 1]);
311 avg_coor_x = 0.5 * (_pin_position[_pins_in_rings[i][j]](0) +
312 _pin_position[_pins_in_rings[i][j + 1]](0));
313 avg_coor_y = 0.5 * (_pin_position[_pins_in_rings[i][j]](1) +
314 _pin_position[_pins_in_rings[i][j + 1]](1));
315 _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
316 _gap_to_pin_map[kgap].second = _pins_in_rings[i][j + 1];
318 kgap = kgap + 1;
319 }
320
321 dist0 = 1.0e+5;
322
323 _chan_to_pin_map[k].push_back(_pins_in_rings[i - 1][0]);
324 unsigned int l0 = 0;
325
326 for (unsigned int l = 0; l < _pins_in_rings[i - 1].size(); l++)
327 {
328 dist = std::sqrt(pow(_pin_position[_pins_in_rings[i - 1][l]](0) - avg_coor_x, 2) +
329 pow(_pin_position[_pins_in_rings[i - 1][l]](1) - avg_coor_y, 2));
330
331 if (dist < dist0)
332 {
333 _chan_to_pin_map[k][2] = _pins_in_rings[i - 1][l];
334 l0 = l;
335 dist0 = dist;
336 } // if
337 } // l
338
339 _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
340 _gap_to_pin_map[kgap].second = _pins_in_rings[i - 1][l0];
342 kgap = kgap + 1;
344 k = k + 1;
345 } // for j
346
347 // find the closest Pin at front ring
348 for (unsigned int j = 0; j < _pins_in_rings[i].size(); j++)
349 {
350 if (j == _pins_in_rings[i].size() - 1)
351 {
352 _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
353 _chan_to_pin_map[k].push_back(_pins_in_rings[i][0]);
354 avg_coor_x =
355 0.5 * (_pin_position[_pins_in_rings[i][j]](0) + _pin_position[_pins_in_rings[i][0]](0));
356 avg_coor_y =
357 0.5 * (_pin_position[_pins_in_rings[i][j]](1) + _pin_position[_pins_in_rings[i][0]](1));
358 }
359 else
360 {
361 _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
362 _chan_to_pin_map[k].push_back(_pins_in_rings[i][j + 1]);
363 avg_coor_x = 0.5 * (_pin_position[_pins_in_rings[i][j]](0) +
364 _pin_position[_pins_in_rings[i][j + 1]](0));
365 avg_coor_y = 0.5 * (_pin_position[_pins_in_rings[i][j]](1) +
366 _pin_position[_pins_in_rings[i][j + 1]](1));
367 }
368
369 // if the outermost ring, set the edge subchannels first... then the corner subchannels
370 if (i == _n_rings - 1)
371 {
372 // add edges
373 _subch_type[k] = EChannelType::EDGE; // an edge subchannel is created
374 _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
375 _gap_to_pin_map[kgap].second = _pins_in_rings[i][j];
377 _chan_to_gap_map[k].push_back(kgap);
378 kgap = kgap + 1;
379 k = k + 1;
380
381 if (j % i == 0)
382 {
383 // generate a corner subchannel, generate the additional gap and fix chan_to_gap_map
384 _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
385 _gap_to_pin_map[kgap].second = _pins_in_rings[i][j];
387
388 // corner subchannel
389 _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
390 _chan_to_gap_map[k].push_back(kgap - 1);
391 _chan_to_gap_map[k].push_back(kgap);
393
394 kgap = kgap + 1;
395 k = k + 1;
396 }
397 // if not the outer most ring
398 }
399 else
400 {
401 dist0 = 1.0e+5;
402 unsigned int l0 = 0;
403 _chan_to_pin_map[k].push_back(_pins_in_rings[i + 1][0]);
404 for (unsigned int l = 0; l < _pins_in_rings[i + 1].size(); l++)
405 {
406 dist = std::sqrt(pow(_pin_position[_pins_in_rings[i + 1][l]](0) - avg_coor_x, 2) +
407 pow(_pin_position[_pins_in_rings[i + 1][l]](1) - avg_coor_y, 2));
408 if (dist < dist0)
409 {
410 _chan_to_pin_map[k][2] = _pins_in_rings[i + 1][l];
411 dist0 = dist;
412 l0 = l;
413 } // if
414 } // l
415
416 _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
417 _gap_to_pin_map[kgap].second = _pins_in_rings[i + 1][l0];
419 kgap = kgap + 1;
421 k = k + 1;
422 } // if
423 } // for j
424 } // for i
425
426 // Constructing pins to channels mao
427 for (unsigned int loc_rod = 0; loc_rod < _npins; loc_rod++)
428 {
429 for (unsigned int i = 0; i < _n_channels; i++)
430 {
431 bool rod_in_sc = false;
432 for (unsigned int j : _chan_to_pin_map[i])
433 {
434 if (j == loc_rod)
435 rod_in_sc = true;
436 }
437 if (rod_in_sc)
438 {
439 _pin_to_chan_map[loc_rod].push_back(i);
440 }
441 }
442 }
443
456 for (unsigned int i = 0; i < _n_channels; i++)
457 {
459 {
460 for (unsigned int j = 0; j < _n_gaps; j++)
461 {
463 {
464 if (((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].first) &&
465 (_chan_to_pin_map[i][1] == _gap_to_pin_map[j].second)) ||
466 ((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].second) &&
467 (_chan_to_pin_map[i][1] == _gap_to_pin_map[j].first)))
468 {
469 _chan_to_gap_map[i].push_back(j);
470 }
471
472 if (((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].first) &&
473 (_chan_to_pin_map[i][2] == _gap_to_pin_map[j].second)) ||
474 ((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].second) &&
475 (_chan_to_pin_map[i][2] == _gap_to_pin_map[j].first)))
476 {
477 _chan_to_gap_map[i].push_back(j);
478 }
479
480 if (((_chan_to_pin_map[i][1] == _gap_to_pin_map[j].first) &&
481 (_chan_to_pin_map[i][2] == _gap_to_pin_map[j].second)) ||
482 ((_chan_to_pin_map[i][1] == _gap_to_pin_map[j].second) &&
483 (_chan_to_pin_map[i][2] == _gap_to_pin_map[j].first)))
484 {
485 _chan_to_gap_map[i].push_back(j);
486 }
487 }
488 } // for j
489 }
490 else if (_subch_type[i] == EChannelType::EDGE)
491 {
492 for (unsigned int j = 0; j < _n_gaps; j++)
493 {
495 {
496 if (((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].first) &&
497 (_chan_to_pin_map[i][1] == _gap_to_pin_map[j].second)) ||
498 ((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].second) &&
499 (_chan_to_pin_map[i][1] == _gap_to_pin_map[j].first)))
500 {
501 _chan_to_gap_map[i].push_back(j);
502 }
503 }
504 }
505
506 icorner = 0;
507 for (unsigned int k = 0; k < _n_channels; k++)
508 {
510 _chan_to_pin_map[i][1] == _chan_to_pin_map[k][0])
511 {
512 _chan_to_gap_map[i].push_back(_chan_to_gap_map[k][1]);
513 icorner = 1;
514 break;
515 } // if
516 } // for
517
518 // Check whether the edge channel's first pin is also a corner pin. This second corner lookup
519 // is only needed while the edge channel still needs another perimeter gap.
520 if (_chan_to_gap_map[i].size() < 3)
521 {
522 for (unsigned int k = 0; k < _n_channels; k++)
523 {
525 _chan_to_pin_map[i][0] == _chan_to_pin_map[k][0])
526 {
527 _chan_to_gap_map[i].push_back(_chan_to_gap_map[k][1] + 1);
528 icorner = 1;
529 break;
530 }
531 }
532 }
533
534 if (icorner == 0)
535 {
536 _chan_to_gap_map[i].push_back(_chan_to_gap_map[i][0] + 1);
537 }
538 }
539 }
540
541 // find gap_to_chan_map pair
542 for (unsigned int j = 0; j < _n_gaps; j++)
543 {
544 for (unsigned int i = 0; i < _n_channels; i++)
545 {
547 {
548 if ((j == _chan_to_gap_map[i][0]) || (j == _chan_to_gap_map[i][1]) ||
549 (j == _chan_to_gap_map[i][2]))
550 {
551 if (_gap_to_chan_map[j].first == 0 && gap_fill[j].first == 0)
552 {
553 _gap_to_chan_map[j].first = i;
554 gap_fill[j].first = 1;
555 }
556 else if (_gap_to_chan_map[j].second == 0 && gap_fill[j].second == 0)
557 {
558 _gap_to_chan_map[j].second = i;
559 gap_fill[j].second = 1;
560 }
561 else
562 {
563 }
564 }
565 }
566 else if (_subch_type[i] == EChannelType::CORNER)
567 {
568 if ((j == _chan_to_gap_map[i][0]) || (j == _chan_to_gap_map[i][1]))
569 {
570 if (_gap_to_chan_map[j].first == 0 && gap_fill[j].first == 0)
571 {
572 _gap_to_chan_map[j].first = i;
573 gap_fill[j].first = 1;
574 }
575 else if (_gap_to_chan_map[j].second == 0 && gap_fill[j].second == 0)
576 {
577 _gap_to_chan_map[j].second = i;
578 gap_fill[j].second = 1;
579 }
580 else
581 {
582 }
583 }
584 }
585 } // i
586 } // j
587
588 for (unsigned int k = 0; k < _n_channels; k++)
589 {
591 {
592 _gap_pairs_sf[k].first = _chan_to_gap_map[k][0];
593 _gap_pairs_sf[k].second = _chan_to_gap_map[k][2];
594 auto k1 = _gap_pairs_sf[k].first;
595 auto k2 = _gap_pairs_sf[k].second;
596 if (_gap_to_chan_map[k1].first == k)
597 {
598 _chan_pairs_sf[k].first = _gap_to_chan_map[k1].second;
599 }
600 else
601 {
602 _chan_pairs_sf[k].first = _gap_to_chan_map[k1].first;
603 }
604
605 if (_gap_to_chan_map[k2].first == k)
606 {
607 _chan_pairs_sf[k].second = _gap_to_chan_map[k2].second;
608 }
609 else
610 {
611 _chan_pairs_sf[k].second = _gap_to_chan_map[k2].first;
612 }
613 }
614 else if (_subch_type[k] == EChannelType::CORNER)
615 {
616 _gap_pairs_sf[k].first = _chan_to_gap_map[k][1];
617 _gap_pairs_sf[k].second = _chan_to_gap_map[k][0];
618
619 auto k1 = _gap_pairs_sf[k].first;
620 auto k2 = _gap_pairs_sf[k].second;
621
622 if (_gap_to_chan_map[k1].first == k)
623 {
624 _chan_pairs_sf[k].first = _gap_to_chan_map[k1].second;
625 }
626 else
627 {
628 _chan_pairs_sf[k].first = _gap_to_chan_map[k1].first;
629 }
630
631 if (_gap_to_chan_map[k2].first == k)
632 {
633 _chan_pairs_sf[k].second = _gap_to_chan_map[k2].second;
634 }
635 else
636 {
637 _chan_pairs_sf[k].second = _gap_to_chan_map[k2].first;
638 }
639 }
640 }
641
642 // set the _gij_map
643 for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
644 {
645 for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
646 {
647 if (_gap_type[i_gap] == EChannelType::CENTER)
648 {
649 _gij_map[iz].push_back(_pitch - _pin_diameter);
650 }
651 else if (_gap_type[i_gap] == EChannelType::EDGE || _gap_type[i_gap] == EChannelType::CORNER)
652 {
653 _gij_map[iz].push_back(_duct_to_pin_gap);
654 }
655 }
656 }
657
658 for (unsigned int i = 0; i < _n_channels; i++)
659 {
661 {
662 for (unsigned int k = 0; k < 3; k++)
663 {
664 for (unsigned int j = 0; j < _n_gaps; j++)
665 {
666 if (_chan_to_gap_map[i][k] == j && i == _gap_to_chan_map[j].first)
667 {
668 if (i > _gap_to_chan_map[j].second)
669 {
670 _sign_id_crossflow_map[i][k] = negative_flow;
671 }
672 else
673 {
674 _sign_id_crossflow_map[i][k] = positive_flow;
675 }
676 }
677 else if (_chan_to_gap_map[i][k] == j && i == _gap_to_chan_map[j].second)
678 {
679 if (i > _gap_to_chan_map[j].first)
680 {
681 _sign_id_crossflow_map[i][k] = negative_flow;
682 }
683 else
684 {
685 _sign_id_crossflow_map[i][k] = positive_flow;
686 }
687 }
688 } // j
689 } // k
690 }
691 else if (_subch_type[i] == EChannelType::CORNER)
692 {
693 for (unsigned int k = 0; k < 2; k++)
694 {
695 for (unsigned int j = 0; j < _n_gaps; j++)
696 {
697 if (_chan_to_gap_map[i][k] == j && i == _gap_to_chan_map[j].first)
698 {
699 if (i > _gap_to_chan_map[j].second)
700 {
701 _sign_id_crossflow_map[i][k] = negative_flow;
702 }
703 else
704 {
705 _sign_id_crossflow_map[i][k] = positive_flow;
706 }
707 }
708 else if (_chan_to_gap_map[i][k] == j && i == _gap_to_chan_map[j].second)
709 {
710 if (i > _gap_to_chan_map[j].first)
711 {
712 _sign_id_crossflow_map[i][k] = negative_flow;
713 }
714 else
715 {
716 _sign_id_crossflow_map[i][k] = positive_flow;
717 }
718 }
719 } // j
720 } // k
721 } // subch_type =2
722 } // i
723
724 // set the subchannel positions
725 for (unsigned int i = 0; i < _n_channels; i++)
726 {
728 {
732 3.0;
736 3.0;
737 }
738 else if (_subch_type[i] == EChannelType::EDGE)
739 {
740 for (unsigned int j = 0; j < _n_channels; j++)
741 {
743 ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][0] &&
744 _chan_to_pin_map[i][1] == _chan_to_pin_map[j][1]) ||
745 (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][1] &&
746 _chan_to_pin_map[i][1] == _chan_to_pin_map[j][0])))
747 {
748 x0 = _pin_position[_chan_to_pin_map[j][2]](0);
749 y0 = _pin_position[_chan_to_pin_map[j][2]](1);
750 }
751 else if (_subch_type[j] == EChannelType::CENTER &&
752 ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][0] &&
753 _chan_to_pin_map[i][1] == _chan_to_pin_map[j][2]) ||
754 (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][2] &&
755 _chan_to_pin_map[i][1] == _chan_to_pin_map[j][0])))
756 {
757 x0 = _pin_position[_chan_to_pin_map[j][1]](0);
758 y0 = _pin_position[_chan_to_pin_map[j][1]](1);
759 }
760 else if (_subch_type[j] == EChannelType::CENTER &&
761 ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][1] &&
762 _chan_to_pin_map[i][1] == _chan_to_pin_map[j][2]) ||
763 (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][2] &&
764 _chan_to_pin_map[i][1] == _chan_to_pin_map[j][1])))
765 {
766 x0 = _pin_position[_chan_to_pin_map[j][0]](0);
767 y0 = _pin_position[_chan_to_pin_map[j][0]](1);
768 }
769 x1 = 0.5 *
771 y1 = 0.5 *
773 a1 = _pin_diameter / 2.0 + _duct_to_pin_gap / 2.0;
774 a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
775 _subchannel_position[i][0] = (a2 * x1 - a1 * x0) / (a2 - a1);
776 _subchannel_position[i][1] = (a2 * y1 - a1 * y0) / (a2 - a1);
777 } // j
778 }
779 else if (_subch_type[i] == EChannelType::CORNER)
780 {
781 x0 = _pin_position[0](0);
782 y0 = _pin_position[0](1);
783 x1 = _pin_position[_chan_to_pin_map[i][0]](0);
784 y1 = _pin_position[_chan_to_pin_map[i][0]](1);
785 a1 = _pin_diameter / 2.0 + _duct_to_pin_gap / 2.0;
786 a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
787 _subchannel_position[i][0] = (a2 * x1 - a1 * x0) / (a2 - a1);
788 _subchannel_position[i][1] = (a2 * y1 - a1 * y0) / (a2 - a1);
789 }
790 }
791
792 // Reduce reserved memory in the channel-to-gap map.
793 for (auto & gap : _chan_to_gap_map)
794 {
795 gap.shrink_to_fit();
796 }
797}
798
799void
801{
802 if (_npins == 0)
803 return;
804
805 mesh_base.reserve_elem(_n_cells * _npins);
806 mesh_base.reserve_nodes((_n_cells + 1) * _npins);
807
808 _pin_nodes.clear();
809 _pin_nodes.resize(_npins);
810
811 // Defining the extent of the subchannel mesh to append the pin mesh to the current subchannel
812 // mesh.
813 const unsigned int node_sub = mesh_base.n_nodes();
814 const unsigned int elem_sub = mesh_base.n_elem();
815
816 // Add the points in the shape of a rectilinear grid. The grid is regular on the xy-plane at the
817 // triangular lattice pin positions. The grid along z is also regular. Store pointers in the
818 // _pin_nodes array so we can keep track of which points are in which pins.
819 unsigned int node_id = node_sub;
820 for (unsigned int i = 0; i < _npins; i++)
821 {
822 _pin_nodes[i].reserve(_n_cells + 1);
823 for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
824 _pin_nodes[i].push_back(mesh_base.add_point(
825 Point(_pin_position[i](0), _pin_position[i](1), _z_grid[iz]), node_id++));
826 }
827
828 // Add the elements which in this case are 2-node edges that link each pin's nodes vertically.
829 unsigned int elem_id = elem_sub;
830 for (unsigned int i = 0; i < _npins; i++)
831 for (unsigned int iz = 0; iz < _n_cells; iz++)
832 {
833 Elem * elem = mesh_base.add_elem(std::make_unique<Edge2>());
834 elem->subdomain_id() = _pin_block_id;
835 elem->set_id(elem_id++);
836 const int indx1 = (_n_cells + 1) * i + iz + node_sub;
837 const int indx2 = (_n_cells + 1) * i + (iz + 1) + node_sub;
838 elem->set_node(0, mesh_base.node_ptr(indx1));
839 elem->set_node(1, mesh_base.node_ptr(indx2));
840 }
841
842 mesh_base.set_subdomain_name(_pin_block_id, "fuel_pins", true);
843}
844
845std::unique_ptr<MeshBase>
847{
848 auto mesh_base = buildMeshBaseObject();
849
850 BoundaryInfo & boundary_info = mesh_base->get_boundary_info();
851 mesh_base->set_spatial_dimension(3);
852 mesh_base->reserve_elem(_n_cells * (_n_channels + _npins));
853 mesh_base->reserve_nodes((_n_cells + 1) * (_n_channels + _npins));
854 _nodes.resize(_n_channels);
855 // Add the points for the give x,y subchannel positions. The grid is hexagonal.
856 // The grid along
857 // z is irregular to account for Pin spacers. Store pointers in the _nodes
858 // array so we can keep track of which points are in which channels.
859 unsigned int node_id = 0;
860 for (unsigned int i = 0; i < _n_channels; i++)
861 {
862 _nodes[i].reserve(_n_cells + 1);
863 for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
864 {
865 _nodes[i].push_back(mesh_base->add_point(
866 Point(_subchannel_position[i][0], _subchannel_position[i][1], _z_grid[iz]), node_id++));
867 }
868 }
869
870 // Add the elements which in this case are 2-node edges that link each
871 // subchannel's nodes vertically.
872 unsigned int elem_id = 0;
873 for (unsigned int i = 0; i < _n_channels; i++)
874 {
875 for (unsigned int iz = 0; iz < _n_cells; iz++)
876 {
877 Elem * elem = mesh_base->add_elem(std::make_unique<Edge2>());
878 elem->subdomain_id() = _subchannel_block_id;
879 elem->set_id(elem_id++);
880 const int indx1 = (_n_cells + 1) * i + iz;
881 const int indx2 = (_n_cells + 1) * i + (iz + 1);
882 elem->set_node(0, mesh_base->node_ptr(indx1));
883 elem->set_node(1, mesh_base->node_ptr(indx2));
884
885 if (iz == 0)
886 boundary_info.add_side(elem, 0, 0);
887 if (iz == _n_cells - 1)
888 boundary_info.add_side(elem, 1, 1);
889 }
890 }
891 boundary_info.sideset_name(0) = "inlet";
892 boundary_info.sideset_name(1) = "outlet";
893 boundary_info.nodeset_name(0) = "inlet";
894 boundary_info.nodeset_name(1) = "outlet";
895
896 // Naming the block
897 mesh_base->set_subdomain_name(_subchannel_block_id, "subchannel", true);
898 buildPinMesh(*mesh_base);
899
900 mesh_base->prepare_for_use();
901
902 // move the meta data into TriSubChannelMesh
903 auto & sch_mesh = cast_ref<TriSubChannelMesh &>(*_mesh);
904 sch_mesh._unheated_length_entry = _unheated_length_entry;
905 sch_mesh._heated_length = _heated_length;
906 sch_mesh._unheated_length_exit = _unheated_length_exit;
907 sch_mesh._z_grid = _z_grid;
908 sch_mesh._k_grid = _k_grid;
909 sch_mesh._spacer_z = _spacer_z;
910 sch_mesh._spacer_k = _spacer_k;
911 sch_mesh._z_blockage = _z_blockage;
912 sch_mesh._index_blockage = _index_blockage;
913 sch_mesh._reduction_blockage = _reduction_blockage;
914 sch_mesh._kij = _kij;
915 sch_mesh._pitch = _pitch;
916 sch_mesh._pin_diameter = _pin_diameter;
917 sch_mesh._n_cells = _n_cells;
918 sch_mesh._n_rings = _n_rings;
919 sch_mesh._n_channels = _n_channels;
920 sch_mesh._flat_to_flat = _flat_to_flat;
921 sch_mesh._dwire = _dwire;
922 sch_mesh._hwire = _hwire;
923 sch_mesh._duct_to_pin_gap = _duct_to_pin_gap;
924 sch_mesh._nodes = _nodes;
925 sch_mesh._gap_to_chan_map = _gap_to_chan_map;
926 sch_mesh._gap_to_pin_map = _gap_to_pin_map;
927 sch_mesh._chan_to_gap_map = _chan_to_gap_map;
928 sch_mesh._sign_id_crossflow_map = _sign_id_crossflow_map;
929 sch_mesh._gij_map = _gij_map;
930 sch_mesh._subchannel_position = _subchannel_position;
931 sch_mesh._pin_position = _pin_position;
932 sch_mesh._pins_in_rings = _pins_in_rings;
933 sch_mesh._chan_to_pin_map = _chan_to_pin_map;
934 sch_mesh._npins = _npins;
935 sch_mesh._n_gaps = _n_gaps;
936 sch_mesh._subch_type = _subch_type;
937 sch_mesh._gap_type = _gap_type;
938 sch_mesh._gap_pairs_sf = _gap_pairs_sf;
939 sch_mesh._chan_pairs_sf = _chan_pairs_sf;
940 sch_mesh._pin_to_chan_map = _pin_to_chan_map;
941 sch_mesh._pin_nodes = _pin_nodes;
942 sch_mesh._pin_mesh_exist = (_npins > 0);
943 sch_mesh.computeAssemblyHydraulicParameters();
944
945 return mesh_base;
946}
ExpressionBuilder::EBTerm pow(const ExpressionBuilder::EBTerm &left, T exponent)
registerMooseObject("SubChannelApp", SCMTriAssemblyMeshGenerator)
registerMooseObjectRenamed("SubChannelApp", SCMTriSubChannelMeshGenerator, "06/30/2027 24:00", SCMTriAssemblyMeshGenerator)
void ErrorVector unsigned int
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
void deprecateParam(const std::string &old_name, const std::string &new_name, const std::string &removal_date)
MooseMesh *const _mesh
static InputParameters validParams()
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
Mesh generator that builds a mesh of 1D lines representing subchannels and pins in a triangular assem...
const std::vector< Real > _reduction_blockage
area reduction of subchannels affected by blockage
std::vector< std::vector< Real > > _sign_id_crossflow_map
Defines the global cross-flow direction -1 or 1 for each subchannel and for all gaps that are belongi...
std::unique_ptr< MeshBase > generate() override
const Real _pitch
Distance between the neighbor fuel pins, pitch.
std::vector< std::vector< Node * > > _pin_nodes
pin nodes
const unsigned int _n_rings
number of rings of fuel pins
std::vector< std::vector< unsigned int > > _pin_to_chan_map
stores the map from pins to channels
const std::vector< Real > _k_blockage
form loss coefficient of subchannels affected by blockage
SCMTriAssemblyMeshGenerator(const InputParameters &parameters)
std::vector< std::pair< unsigned int, unsigned int > > _gap_to_pin_map
stores the fuel pin pairs for each gap each gap
std::vector< std::vector< Real > > _subchannel_position
x,y coordinates of the subchannel centroids
const Real _heated_length
heated length of the fuel Pin
std::vector< std::pair< unsigned int, unsigned int > > _gap_pairs_sf
sweeping flow model gap pairs per channel to specify directional edge flow
std::vector< std::vector< Real > > _k_grid
axial form loss coefficient per computational cell
const std::vector< Real > & _spacer_k
form loss coefficient of the spacers
const Real _flat_to_flat
the distance between flat surfaces of the duct facing each other
const unsigned int _pin_block_id
pin block index
void buildPinMesh(MeshBase &mesh_base)
Build the 1D pin elements and append them to the subchannel mesh.
const std::vector< Real > _z_blockage
axial location of blockage (inlet, outlet) [m]
unsigned int _n_channels
number of subchannels
std::vector< std::pair< unsigned int, unsigned int > > _chan_pairs_sf
sweeping flow model channel pairs to specify directional edge flow
const std::vector< Real > & _spacer_z
axial location of the spacers
std::vector< std::vector< Real > > _pins_in_rings
fuel pins that are belonging to each ring
const Real _unheated_length_exit
unheated length of the fuel Pin at the exit of the assembly
std::vector< EChannelType > _subch_type
subchannel type
const unsigned int _subchannel_block_id
subchannel block index
std::vector< std::vector< Node * > > _nodes
nodes
std::vector< std::vector< Real > > _gij_map
gap size
std::vector< std::vector< unsigned int > > _chan_to_gap_map
stores the gaps that forms each subchannel
std::vector< Real > _z_grid
axial location of nodes
unsigned int _npins
number of fuel pins
std::vector< EChannelType > _gap_type
gap type
const Real _pin_diameter
fuel Pin diameter
const Real _duct_to_pin_gap
the gap thickness between the duct and peripheral fuel pins
const std::vector< unsigned int > _index_blockage
index of subchannels affected by blockage
const Real _unheated_length_entry
unheated length of the fuel Pin at the entry of the assembly
const Real & _kij
Lateral form loss coefficient.
std::vector< std::pair< unsigned int, unsigned int > > _gap_to_chan_map
stores the channel pairs for each gap
std::vector< Point > _pin_position
x,y coordinates of the fuel pins
std::vector< std::vector< unsigned int > > _chan_to_pin_map
stores the fuel pins belonging to each subchannel
const unsigned int _n_cells
number of axial cells
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 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 ...