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