https://mooseframework.inl.gov
SCMTriSubChannelMeshGenerator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
11 #include "TriSubChannelMesh.h"
12 #include <cmath>
13 #include "libmesh/edge_edge2.h"
14 #include "libmesh/unstructured_mesh.h"
15 
17 registerMooseObjectRenamed("SubChannelApp",
18  TriSubChannelMeshGenerator,
19  "06/30/2025 24:00",
21 
24 {
26  params.addClassDescription(
27  "Creates a mesh of 1D subchannels in a triangular lattice arrangement");
28  params.addRequiredParam<unsigned int>("n_cells", "The number of cells in the axial direction");
29  params.addRequiredParam<Real>("pitch", "Pitch [m]");
30  params.addRequiredParam<Real>("pin_diameter", "Rod diameter [m]");
31  params.addParam<Real>("unheated_length_entry", 0.0, "Unheated length at entry [m]");
32  params.addRequiredParam<Real>("heated_length", "Heated length [m]");
33  params.addParam<Real>("unheated_length_exit", 0.0, "Unheated length at exit [m]");
34  params.addRequiredParam<unsigned int>("nrings", "Number of fuel Pin rings per assembly [-]");
35  params.addRequiredParam<Real>("flat_to_flat",
36  "Flat to flat distance for the hexagonal assembly [m]");
37  params.addRequiredParam<Real>("dwire", "Wire diameter [m]");
38  params.addRequiredParam<Real>("hwire", "Wire lead length [m]");
39  params.addParam<std::vector<Real>>("spacer_z",
40  "Axial location of spacers/vanes/mixing_vanes [m]");
41  params.addParam<std::vector<Real>>("spacer_k",
42  "K-loss coefficient of spacers/vanes/mixing_vanes [-]");
43  params.addParam<Real>("Kij", 0.5, "Lateral form loss coefficient [-]");
44  params.addParam<std::vector<Real>>("z_blockage",
45  std::vector<Real>({0.0, 0.0}),
46  "axial location of blockage (inlet, outlet) [m]");
47  params.addParam<std::vector<unsigned int>>("index_blockage",
48  std::vector<unsigned int>({0}),
49  "index of subchannels affected by blockage");
50  params.addParam<std::vector<Real>>(
51  "reduction_blockage",
52  std::vector<Real>({1.0}),
53  "Area reduction of subchannels affected by blockage (number to muliply the area)");
54  params.addParam<std::vector<Real>>("k_blockage",
55  std::vector<Real>({0.0}),
56  "Form loss coefficient of subchannels affected by blockage");
57  params.addParam<unsigned int>("block_id", 0, "Domain Index");
58  return params;
59 }
60 
62  : MeshGenerator(params),
63  _unheated_length_entry(getParam<Real>("unheated_length_entry")),
64  _heated_length(getParam<Real>("heated_length")),
65  _unheated_length_exit(getParam<Real>("unheated_length_exit")),
66  _block_id(getParam<unsigned int>("block_id")),
67  _spacer_z(getParam<std::vector<Real>>("spacer_z")),
68  _spacer_k(getParam<std::vector<Real>>("spacer_k")),
69  _z_blockage(getParam<std::vector<Real>>("z_blockage")),
70  _index_blockage(getParam<std::vector<unsigned int>>("index_blockage")),
71  _reduction_blockage(getParam<std::vector<Real>>("reduction_blockage")),
72  _k_blockage(getParam<std::vector<Real>>("k_blockage")),
73  _pitch(getParam<Real>("pitch")),
74  _kij(getParam<Real>("Kij")),
75  _pin_diameter(getParam<Real>("pin_diameter")),
76  _n_cells(getParam<unsigned int>("n_cells")),
77  _n_rings(getParam<unsigned int>("nrings")),
78  _flat_to_flat(getParam<Real>("flat_to_flat")),
79  _dwire(getParam<Real>("dwire")),
80  _hwire(getParam<Real>("hwire")),
81  _duct_to_pin_gap(0.5 *
82  (_flat_to_flat - (_n_rings - 1) * _pitch * std::sqrt(3.0) - _pin_diameter))
83 {
84  if (_spacer_z.size() != _spacer_k.size())
85  mooseError(name(), ": Size of vector spacer_z should be equal to size of vector spacer_k");
86 
88  mooseError(name(), ": Location of spacers should be less than the total bundle length");
89 
90  if (_z_blockage.size() != 2)
91  mooseError(name(), ": Size of vector z_blockage must be 2");
92 
93  if (*max_element(_reduction_blockage.begin(), _reduction_blockage.end()) > 1)
94  mooseError(name(), ": The area reduction of the blocked subchannels cannot be more than 1");
95 
96  if ((_index_blockage.size() != _reduction_blockage.size()) ||
97  (_index_blockage.size() != _k_blockage.size()) ||
98  (_reduction_blockage.size() != _k_blockage.size()))
99  mooseError(name(),
100  ": Size of vectors: index_blockage, reduction_blockage, k_blockage, must be equal "
101  "to eachother");
102 
105 
106  // Defining the total length from 3 axial sections
108 
109  // Defining the position of the spacer grid in the numerical solution array
110  std::vector<int> spacer_cell;
111  for (const auto & elem : _spacer_z)
112  spacer_cell.emplace_back(std::round(elem * _n_cells / L));
113 
114  // Defining the array for axial resistances
115  std::vector<Real> kgrid;
116  kgrid.resize(_n_cells + 1, 0.0);
117 
118  // Summing the spacer resistance to the grid resistance array
119  for (unsigned int index = 0; index < spacer_cell.size(); index++)
120  kgrid[spacer_cell[index]] += _spacer_k[index];
121 
122  // compute the hex mesh variables
123  // -------------------------------------------
124 
125  // x coordinate for the first position
126  Real x0 = 0.0;
127  // y coordinate for the first position
128  Real y0 = 0.0;
129  // x coordinate for the second position
130  Real x1 = 0.0;
131  // y coordinate for the second position dummy variable
132  Real y1 = 0.0;
133  // dummy variable
134  Real a1 = 0.0;
135  // dummy variable
136  Real a2 = 0.0;
137  // average x coordinate
138  Real avg_coor_x = 0.0;
139  // average y coordinate
140  Real avg_coor_y = 0.0;
141  // distance between two points
142  Real dist = 0.0;
143  // distance between two points
144  Real dist0 = 0.0;
145  // integer counter
146  unsigned int kgap = 0;
147  // dummy integer
148  unsigned int icorner = 0;
149  // used to defined global direction of the cross_flow_map coefficients for each subchannel and gap
150  const Real positive_flow = 1.0;
151  // used to defined global direction of the cross_flow_map coefficients for each subchannel and gap
152  const Real negative_flow = -1.0;
153  // the indicator used while setting _gap_to_chan_map array
154  std::vector<std::pair<unsigned int, unsigned int>> gap_fill;
156  _npins = _pin_position.size();
157  // assign the pins to the corresponding rings
158  unsigned int k = 0; // initialize the fuel Pin counter index
159  _pins_in_rings.resize(_n_rings);
160  _pins_in_rings[0].push_back(k++);
161  for (unsigned int i = 1; i < _n_rings; i++)
162  for (unsigned int j = 0; j < i * 6; j++)
163  _pins_in_rings[i].push_back(k++);
164  // Given the number of pins and number of fuel Pin rings, the number of subchannels can be
165  // computed as follows:
166  unsigned int chancount = 0.0;
167  // Summing internal channels
168  for (unsigned int j = 0; j < _n_rings - 1; j++)
169  chancount += j * 6;
170  // Adding external channels to the total count
171  _n_channels = chancount + _npins - 1 + (_n_rings - 1) * 6 + 6;
172 
173  if (*max_element(_index_blockage.begin(), _index_blockage.end()) > (_n_channels - 1))
174  mooseError(name(),
175  ": The index of the blocked subchannel cannot be more than the max index of the "
176  "subchannels");
177 
178  if ((_index_blockage.size() > _n_channels) || (_reduction_blockage.size() > _n_channels) ||
179  (_k_blockage.size() > _n_channels))
180  mooseError(name(),
181  ": Size of vectors: index_blockage, reduction_blockage, k_blockage, cannot be more "
182  "than the total number of subchannels");
183 
184  // Defining the 2D array for axial resistances
185  _k_grid.resize(_n_channels, std::vector<Real>(_n_cells + 1));
186  for (unsigned int i = 0; i < _n_channels; i++)
187  _k_grid[i] = kgrid;
188 
189  // Add blockage resistance to the 2D grid resistane array
190  Real dz = L / _n_cells;
191  for (unsigned int i = 0; i < _n_cells + 1; i++)
192  {
193  if ((dz * i >= _z_blockage.front() && dz * i <= _z_blockage.back()))
194  {
195  unsigned int index(0);
196  for (const auto & i_ch : _index_blockage)
197  {
198  _k_grid[i_ch][i] += _k_blockage[index];
199  index++;
200  }
201  }
202  }
203 
205  _pin_to_chan_map.resize(_npins);
206  _subch_type.resize(_n_channels);
207  _n_gaps = _n_channels + _npins - 1;
208  _gap_to_chan_map.resize(_n_gaps);
209  _gap_to_pin_map.resize(_n_gaps);
210  gap_fill.resize(_n_gaps);
212  _gap_pairs_sf.resize(_n_channels);
213  _chan_pairs_sf.resize(_n_channels);
214  _gij_map.resize(_n_cells + 1);
216  _gap_type.resize(_n_gaps);
218 
219  for (unsigned int i = 0; i < _n_channels; i++)
220  {
221  _chan_to_pin_map[i].reserve(3);
222  _chan_to_gap_map[i].reserve(3);
223  _sign_id_crossflow_map[i].reserve(3);
224  _subchannel_position[i].reserve(3);
225  for (unsigned int j = 0; j < 3; j++)
226  {
227  _sign_id_crossflow_map.at(i).push_back(positive_flow);
228  _subchannel_position.at(i).push_back(0.0);
229  }
230  }
231 
232  for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
233  {
234  _gij_map[iz].reserve(_n_gaps);
235  }
236 
237  for (unsigned int i = 0; i < _npins; i++)
238  _pin_to_chan_map[i].reserve(6);
239 
240  // create the subchannels
241  k = 0; // initialize the subchannel counter index
242  kgap = 0;
243  // for each ring we trace the subchannels by pairing up to neighbor pins and looking for the third
244  // Pin at inner or outer ring compared to the current ring.
245  for (unsigned int i = 1; i < _n_rings; i++)
246  {
247  // find the closest Pin at back ring
248  for (unsigned int j = 0; j < _pins_in_rings[i].size(); j++)
249  {
250  if (j == _pins_in_rings[i].size() - 1)
251  {
252  _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
253  _chan_to_pin_map[k].push_back(_pins_in_rings[i][0]);
254  avg_coor_x =
255  0.5 * (_pin_position[_pins_in_rings[i][j]](0) + _pin_position[_pins_in_rings[i][0]](0));
256  avg_coor_y =
257  0.5 * (_pin_position[_pins_in_rings[i][j]](1) + _pin_position[_pins_in_rings[i][0]](1));
258  _gap_to_pin_map[kgap].first = _pins_in_rings[i][0];
259  _gap_to_pin_map[kgap].second = _pins_in_rings[i][j];
261  kgap = kgap + 1;
262  }
263  else
264  {
265  _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
266  _chan_to_pin_map[k].push_back(_pins_in_rings[i][j + 1]);
267  avg_coor_x = 0.5 * (_pin_position[_pins_in_rings[i][j]](0) +
268  _pin_position[_pins_in_rings[i][j + 1]](0));
269  avg_coor_y = 0.5 * (_pin_position[_pins_in_rings[i][j]](1) +
270  _pin_position[_pins_in_rings[i][j + 1]](1));
271  _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
272  _gap_to_pin_map[kgap].second = _pins_in_rings[i][j + 1];
274  kgap = kgap + 1;
275  }
276 
277  dist0 = 1.0e+5;
278 
279  _chan_to_pin_map[k].push_back(_pins_in_rings[i - 1][0]);
280  unsigned int l0 = 0;
281 
282  for (unsigned int l = 0; l < _pins_in_rings[i - 1].size(); l++)
283  {
284  dist = std::sqrt(pow(_pin_position[_pins_in_rings[i - 1][l]](0) - avg_coor_x, 2) +
285  pow(_pin_position[_pins_in_rings[i - 1][l]](1) - avg_coor_y, 2));
286 
287  if (dist < dist0)
288  {
289  _chan_to_pin_map[k][2] = _pins_in_rings[i - 1][l];
290  l0 = l;
291  dist0 = dist;
292  } // if
293  } // l
294 
295  _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
296  _gap_to_pin_map[kgap].second = _pins_in_rings[i - 1][l0];
298  kgap = kgap + 1;
300  k = k + 1;
301 
302  } // for j
303 
304  // find the closest Pin at front ring
305 
306  for (unsigned int j = 0; j < _pins_in_rings[i].size(); j++)
307  {
308  if (j == _pins_in_rings[i].size() - 1)
309  {
310  _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
311  _chan_to_pin_map[k].push_back(_pins_in_rings[i][0]);
312  avg_coor_x =
313  0.5 * (_pin_position[_pins_in_rings[i][j]](0) + _pin_position[_pins_in_rings[i][0]](0));
314  avg_coor_y =
315  0.5 * (_pin_position[_pins_in_rings[i][j]](1) + _pin_position[_pins_in_rings[i][0]](1));
316  }
317  else
318  {
319  _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
320  _chan_to_pin_map[k].push_back(_pins_in_rings[i][j + 1]);
321  avg_coor_x = 0.5 * (_pin_position[_pins_in_rings[i][j]](0) +
322  _pin_position[_pins_in_rings[i][j + 1]](0));
323  avg_coor_y = 0.5 * (_pin_position[_pins_in_rings[i][j]](1) +
324  _pin_position[_pins_in_rings[i][j + 1]](1));
325  }
326 
327  // if the outermost ring, set the edge subchannels first... then the corner subchannels
328  if (i == _n_rings - 1)
329  {
330  // add edges
331  _subch_type[k] = EChannelType::EDGE; // an edge subchannel is created
332  _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
333  _gap_to_pin_map[kgap].second = _pins_in_rings[i][j];
335  _chan_to_gap_map[k].push_back(kgap);
336  kgap = kgap + 1;
337  k = k + 1;
338 
339  if (j % i == 0)
340  {
341  // generate a corner subchannel, generate the additional gap and fix chan_to_gap_map
342  _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
343  _gap_to_pin_map[kgap].second = _pins_in_rings[i][j];
345 
346  // corner subchannel
347  _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
348  _chan_to_gap_map[k].push_back(kgap - 1);
349  _chan_to_gap_map[k].push_back(kgap);
351 
352  kgap = kgap + 1;
353  k = k + 1;
354  }
355  // if not the outer most ring
356  }
357  else
358  {
359  dist0 = 1.0e+5;
360  unsigned int l0 = 0;
361  _chan_to_pin_map[k].push_back(_pins_in_rings[i + 1][0]);
362  for (unsigned int l = 0; l < _pins_in_rings[i + 1].size(); l++)
363  {
364  dist = std::sqrt(pow(_pin_position[_pins_in_rings[i + 1][l]](0) - avg_coor_x, 2) +
365  pow(_pin_position[_pins_in_rings[i + 1][l]](1) - avg_coor_y, 2));
366  if (dist < dist0)
367  {
368  _chan_to_pin_map[k][2] = _pins_in_rings[i + 1][l];
369  dist0 = dist;
370  l0 = l;
371  } // if
372  } // l
373 
374  _gap_to_pin_map[kgap].first = _pins_in_rings[i][j];
375  _gap_to_pin_map[kgap].second = _pins_in_rings[i + 1][l0];
377  kgap = kgap + 1;
379  k = k + 1;
380  } // if
381  } // for j
382  } // for i
383 
384  // Constructing pins to channels mao
385  for (unsigned int loc_rod = 0; loc_rod < _npins; loc_rod++)
386  {
387  for (unsigned int i = 0; i < _n_channels; i++)
388  {
389  bool rod_in_sc = false;
390  for (unsigned int j : _chan_to_pin_map[i])
391  {
392  if (j == loc_rod)
393  rod_in_sc = true;
394  }
395  if (rod_in_sc)
396  {
397  _pin_to_chan_map[loc_rod].push_back(i);
398  }
399  }
400  }
401 
402  // find the _gap_to_chan_map and _chan_to_gap_map using the gap_to_rod and subchannel_to_rod_maps
403 
404  for (unsigned int i = 0; i < _n_channels; i++)
405  {
407  {
408  for (unsigned int j = 0; j < _n_gaps; j++)
409  {
411  {
412  if (((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].first) &&
413  (_chan_to_pin_map[i][1] == _gap_to_pin_map[j].second)) ||
414  ((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].second) &&
415  (_chan_to_pin_map[i][1] == _gap_to_pin_map[j].first)))
416  {
417  _chan_to_gap_map[i].push_back(j);
418  }
419 
420  if (((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].first) &&
421  (_chan_to_pin_map[i][2] == _gap_to_pin_map[j].second)) ||
422  ((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].second) &&
423  (_chan_to_pin_map[i][2] == _gap_to_pin_map[j].first)))
424  {
425  _chan_to_gap_map[i].push_back(j);
426  }
427 
428  if (((_chan_to_pin_map[i][1] == _gap_to_pin_map[j].first) &&
429  (_chan_to_pin_map[i][2] == _gap_to_pin_map[j].second)) ||
430  ((_chan_to_pin_map[i][1] == _gap_to_pin_map[j].second) &&
431  (_chan_to_pin_map[i][2] == _gap_to_pin_map[j].first)))
432  {
433  _chan_to_gap_map[i].push_back(j);
434  }
435  }
436  } // for j
437  }
438  else if (_subch_type[i] == EChannelType::EDGE)
439  {
440  for (unsigned int j = 0; j < _n_gaps; j++)
441  {
443  {
444  if (((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].first) &&
445  (_chan_to_pin_map[i][1] == _gap_to_pin_map[j].second)) ||
446  ((_chan_to_pin_map[i][0] == _gap_to_pin_map[j].second) &&
447  (_chan_to_pin_map[i][1] == _gap_to_pin_map[j].first)))
448  {
449  _chan_to_gap_map[i].push_back(j);
450  }
451  }
452  }
453 
454  icorner = 0;
455  for (unsigned int k = 0; k < _n_channels; k++)
456  {
458  _chan_to_pin_map[i][1] == _chan_to_pin_map[k][0])
459  {
460  _chan_to_gap_map[i].push_back(_chan_to_gap_map[k][1]);
461  icorner = 1;
462  break;
463  } // if
464  } // for
465 
466  for (unsigned int k = 0; k < _n_channels; k++)
467  {
469  _chan_to_pin_map[i][0] == _chan_to_pin_map[k][0])
470  {
471  _chan_to_gap_map[i].push_back(_chan_to_gap_map[k][1] + 1);
472  icorner = 1;
473  break;
474  }
475  }
476 
477  if (icorner == 0)
478  {
479  _chan_to_gap_map[i].push_back(_chan_to_gap_map[i][0] + 1);
480  }
481  }
482  }
483 
484  // find gap_to_chan_map pair
485 
486  for (unsigned int j = 0; j < _n_gaps; j++)
487  {
488  for (unsigned int i = 0; i < _n_channels; i++)
489  {
491  {
492  if ((j == _chan_to_gap_map[i][0]) || (j == _chan_to_gap_map[i][1]) ||
493  (j == _chan_to_gap_map[i][2]))
494  {
495  if (_gap_to_chan_map[j].first == 0 && gap_fill[j].first == 0)
496  {
497  _gap_to_chan_map[j].first = i;
498  gap_fill[j].first = 1;
499  }
500  else if (_gap_to_chan_map[j].second == 0 && gap_fill[j].second == 0)
501  {
502  _gap_to_chan_map[j].second = i;
503  gap_fill[j].second = 1;
504  }
505  else
506  {
507  }
508  }
509  }
510  else if (_subch_type[i] == EChannelType::CORNER)
511  {
512  if ((j == _chan_to_gap_map[i][0]) || (j == _chan_to_gap_map[i][1]))
513  {
514  if (_gap_to_chan_map[j].first == 0 && gap_fill[j].first == 0)
515  {
516  _gap_to_chan_map[j].first = i;
517  gap_fill[j].first = 1;
518  }
519  else if (_gap_to_chan_map[j].second == 0 && gap_fill[j].second == 0)
520  {
521  _gap_to_chan_map[j].second = i;
522  gap_fill[j].second = 1;
523  }
524  else
525  {
526  }
527  }
528  }
529  } // i
530  } // j
531 
532  for (unsigned int k = 0; k < _n_channels; k++)
533  {
535  {
536  _gap_pairs_sf[k].first = _chan_to_gap_map[k][0];
537  _gap_pairs_sf[k].second = _chan_to_gap_map[k][2];
538  auto k1 = _gap_pairs_sf[k].first;
539  auto k2 = _gap_pairs_sf[k].second;
540  if (_gap_to_chan_map[k1].first == k)
541  {
542  _chan_pairs_sf[k].first = _gap_to_chan_map[k1].second;
543  }
544  else
545  {
546  _chan_pairs_sf[k].first = _gap_to_chan_map[k1].first;
547  }
548 
549  if (_gap_to_chan_map[k2].first == k)
550  {
551  _chan_pairs_sf[k].second = _gap_to_chan_map[k2].second;
552  }
553  else
554  {
555  _chan_pairs_sf[k].second = _gap_to_chan_map[k2].first;
556  }
557  }
558  else if (_subch_type[k] == EChannelType::CORNER)
559  {
560  _gap_pairs_sf[k].first = _chan_to_gap_map[k][1];
561  _gap_pairs_sf[k].second = _chan_to_gap_map[k][0];
562 
563  auto k1 = _gap_pairs_sf[k].first;
564  auto k2 = _gap_pairs_sf[k].second;
565 
566  if (_gap_to_chan_map[k1].first == k)
567  {
568  _chan_pairs_sf[k].first = _gap_to_chan_map[k1].second;
569  }
570  else
571  {
572  _chan_pairs_sf[k].first = _gap_to_chan_map[k1].first;
573  }
574 
575  if (_gap_to_chan_map[k2].first == k)
576  {
577  _chan_pairs_sf[k].second = _gap_to_chan_map[k2].second;
578  }
579  else
580  {
581  _chan_pairs_sf[k].second = _gap_to_chan_map[k2].first;
582  }
583  }
584  }
585 
586  // set the _gij_map
587  for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
588  {
589  for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
590  {
591  if (_gap_type[i_gap] == EChannelType::CENTER)
592  {
593  _gij_map[iz].push_back(_pitch - _pin_diameter);
594  }
595  else if (_gap_type[i_gap] == EChannelType::EDGE || _gap_type[i_gap] == EChannelType::CORNER)
596  {
597  _gij_map[iz].push_back(_duct_to_pin_gap);
598  }
599  }
600  }
601 
602  for (unsigned int i = 0; i < _n_channels; i++)
603  {
605  {
606  for (unsigned int k = 0; k < 3; k++)
607  {
608  for (unsigned int j = 0; j < _n_gaps; j++)
609  {
610  if (_chan_to_gap_map[i][k] == j && i == _gap_to_chan_map[j].first)
611  {
612  if (i > _gap_to_chan_map[j].second)
613  {
614  _sign_id_crossflow_map[i][k] = negative_flow;
615  }
616  else
617  {
618  _sign_id_crossflow_map[i][k] = positive_flow;
619  }
620  }
621  else if (_chan_to_gap_map[i][k] == j && i == _gap_to_chan_map[j].second)
622  {
623  if (i > _gap_to_chan_map[j].first)
624  {
625  _sign_id_crossflow_map[i][k] = negative_flow;
626  }
627  else
628  {
629  _sign_id_crossflow_map[i][k] = positive_flow;
630  }
631  }
632  } // j
633  } // k
634  }
635  else if (_subch_type[i] == EChannelType::CORNER)
636  {
637  for (unsigned int k = 0; k < 2; k++)
638  {
639  for (unsigned int j = 0; j < _n_gaps; j++)
640  {
641  if (_chan_to_gap_map[i][k] == j && i == _gap_to_chan_map[j].first)
642  {
643  if (i > _gap_to_chan_map[j].second)
644  {
645  _sign_id_crossflow_map[i][k] = negative_flow;
646  }
647  else
648  {
649  _sign_id_crossflow_map[i][k] = positive_flow;
650  }
651  }
652  else if (_chan_to_gap_map[i][k] == j && i == _gap_to_chan_map[j].second)
653  {
654  if (i > _gap_to_chan_map[j].first)
655  {
656  _sign_id_crossflow_map[i][k] = negative_flow;
657  }
658  else
659  {
660  _sign_id_crossflow_map[i][k] = positive_flow;
661  }
662  }
663  } // j
664  } // k
665  } // subch_type =2
666  } // i
667 
668  // set the subchannel positions
669  for (unsigned int i = 0; i < _n_channels; i++)
670  {
672  {
673  _subchannel_position[i][0] =
675  _pin_position[_chan_to_pin_map[i][2]](0)) /
676  3.0;
677  _subchannel_position[i][1] =
679  _pin_position[_chan_to_pin_map[i][2]](1)) /
680  3.0;
681  }
682  else if (_subch_type[i] == EChannelType::EDGE)
683  {
684  for (unsigned int j = 0; j < _n_channels; j++)
685  {
687  ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][0] &&
688  _chan_to_pin_map[i][1] == _chan_to_pin_map[j][1]) ||
689  (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][1] &&
690  _chan_to_pin_map[i][1] == _chan_to_pin_map[j][0])))
691  {
692  x0 = _pin_position[_chan_to_pin_map[j][2]](0);
693  y0 = _pin_position[_chan_to_pin_map[j][2]](1);
694  }
695  else if (_subch_type[j] == EChannelType::CENTER &&
696  ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][0] &&
697  _chan_to_pin_map[i][1] == _chan_to_pin_map[j][2]) ||
698  (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][2] &&
699  _chan_to_pin_map[i][1] == _chan_to_pin_map[j][0])))
700  {
701  x0 = _pin_position[_chan_to_pin_map[j][1]](0);
702  y0 = _pin_position[_chan_to_pin_map[j][1]](1);
703  }
704  else if (_subch_type[j] == EChannelType::CENTER &&
705  ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][1] &&
706  _chan_to_pin_map[i][1] == _chan_to_pin_map[j][2]) ||
707  (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][2] &&
708  _chan_to_pin_map[i][1] == _chan_to_pin_map[j][1])))
709  {
710  x0 = _pin_position[_chan_to_pin_map[j][0]](0);
711  y0 = _pin_position[_chan_to_pin_map[j][0]](1);
712  }
713  x1 = 0.5 *
715  y1 = 0.5 *
717  a1 = _pin_diameter / 2.0 + _duct_to_pin_gap / 2.0;
718  a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
719  _subchannel_position[i][0] = (a2 * x1 - a1 * x0) / (a2 - a1);
720  _subchannel_position[i][1] = (a2 * y1 - a1 * y0) / (a2 - a1);
721  } // j
722  }
723  else if (_subch_type[i] == EChannelType::CORNER)
724  {
725  x0 = _pin_position[0](0);
726  y0 = _pin_position[0](1);
727  x1 = _pin_position[_chan_to_pin_map[i][0]](0);
728  y1 = _pin_position[_chan_to_pin_map[i][0]](1);
729  a1 = _pin_diameter / 2.0 + _duct_to_pin_gap / 2.0;
730  a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
731  _subchannel_position[i][0] = (a2 * x1 - a1 * x0) / (a2 - a1);
732  _subchannel_position[i][1] = (a2 * y1 - a1 * y0) / (a2 - a1);
733  }
734  }
735 
737  if (_n_rings == 1)
738  {
739  for (unsigned int i = 0; i < _n_channels; i++)
740  {
741  Real angle = (2 * i + 1) * libMesh::pi / 6.0;
743  _subchannel_position[i][0] = std::cos(angle) * _flat_to_flat / 2.0;
744  _subchannel_position[i][1] = std::sin(angle) * _flat_to_flat / 2.0;
745  }
746  }
747 
748  // Reduce reserved memory in the channel-to-gap map.
749  for (auto & gap : _chan_to_gap_map)
750  {
751  gap.shrink_to_fit();
752  }
753 }
754 
755 std::unique_ptr<MeshBase>
757 {
758  auto mesh_base = buildMeshBaseObject();
759 
760  BoundaryInfo & boundary_info = mesh_base->get_boundary_info();
761  mesh_base->set_spatial_dimension(3);
762  mesh_base->reserve_elem(_n_cells * _n_channels);
763  mesh_base->reserve_nodes((_n_cells + 1) * _n_channels);
764  _nodes.resize(_n_channels);
765  // Add the points for the give x,y subchannel positions. The grid is hexagonal.
766  // The grid along
767  // z is irregular to account for Pin spacers. Store pointers in the _nodes
768  // array so we can keep track of which points are in which channels.
769  unsigned int node_id = 0;
770  for (unsigned int i = 0; i < _n_channels; i++)
771  {
772  _nodes[i].reserve(_n_cells + 1);
773  for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
774  {
775  _nodes[i].push_back(mesh_base->add_point(
776  Point(_subchannel_position[i][0], _subchannel_position[i][1], _z_grid[iz]), node_id++));
777  }
778  }
779 
780  // Add the elements which in this case are 2-node edges that link each
781  // subchannel's nodes vertically.
782  unsigned int elem_id = 0;
783  for (unsigned int i = 0; i < _n_channels; i++)
784  {
785  for (unsigned int iz = 0; iz < _n_cells; iz++)
786  {
787  Elem * elem = new Edge2;
788  elem->set_id(elem_id++);
789  elem = mesh_base->add_elem(elem);
790  const int indx1 = (_n_cells + 1) * i + iz;
791  const int indx2 = (_n_cells + 1) * i + (iz + 1);
792  elem->set_node(0, mesh_base->node_ptr(indx1));
793  elem->set_node(1, mesh_base->node_ptr(indx2));
794 
795  if (iz == 0)
796  boundary_info.add_side(elem, 0, 0);
797  if (iz == _n_cells - 1)
798  boundary_info.add_side(elem, 1, 1);
799  }
800  }
801  boundary_info.sideset_name(0) = "inlet";
802  boundary_info.sideset_name(1) = "outlet";
803  boundary_info.nodeset_name(0) = "inlet";
804  boundary_info.nodeset_name(1) = "outlet";
805 
806  // Naming the block
807  mesh_base->subdomain_name(_block_id) = name();
808 
809  mesh_base->prepare_for_use();
810 
811  // move the meta data into TriSubChannelMesh
812  auto & sch_mesh = static_cast<TriSubChannelMesh &>(*_mesh);
814  sch_mesh._heated_length = _heated_length;
815  sch_mesh._unheated_length_exit = _unheated_length_exit;
816  sch_mesh._z_grid = _z_grid;
817  sch_mesh._k_grid = _k_grid;
818  sch_mesh._spacer_z = _spacer_z;
819  sch_mesh._spacer_k = _spacer_k;
820  sch_mesh._z_blockage = _z_blockage;
821  sch_mesh._index_blockage = _index_blockage;
822  sch_mesh._reduction_blockage = _reduction_blockage;
823  sch_mesh._kij = _kij;
824  sch_mesh._pitch = _pitch;
825  sch_mesh._pin_diameter = _pin_diameter;
826  sch_mesh._n_cells = _n_cells;
827  sch_mesh._n_rings = _n_rings;
828  sch_mesh._n_channels = _n_channels;
829  sch_mesh._flat_to_flat = _flat_to_flat;
830  sch_mesh._dwire = _dwire;
831  sch_mesh._hwire = _hwire;
832  sch_mesh._duct_to_pin_gap = _duct_to_pin_gap;
833  sch_mesh._nodes = _nodes;
834  sch_mesh._gap_to_chan_map = _gap_to_chan_map;
835  sch_mesh._gap_to_pin_map = _gap_to_pin_map;
836  sch_mesh._chan_to_gap_map = _chan_to_gap_map;
837  sch_mesh._sign_id_crossflow_map = _sign_id_crossflow_map;
838  sch_mesh._gij_map = _gij_map;
839  sch_mesh._subchannel_position = _subchannel_position;
840  sch_mesh._pin_position = _pin_position;
841  sch_mesh._pins_in_rings = _pins_in_rings;
842  sch_mesh._chan_to_pin_map = _chan_to_pin_map;
843  sch_mesh._npins = _npins;
844  sch_mesh._n_gaps = _n_gaps;
845  sch_mesh._subch_type = _subch_type;
846  sch_mesh._gap_type = _gap_type;
847  sch_mesh._gap_pairs_sf = _gap_pairs_sf;
848  sch_mesh._chan_pairs_sf = _chan_pairs_sf;
849  sch_mesh._pin_to_chan_map = _pin_to_chan_map;
850 
851  return mesh_base;
852 }
unsigned int _npins
number of fuel pins
std::vector< EChannelType > _gap_type
gap type
std::vector< std::vector< Real > > _pins_in_rings
fuel pins that are belonging to each ring
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
std::vector< std::pair< unsigned int, unsigned int > > _chan_pairs_sf
sweeping flow model channel pairs to specify directional edge flow
std::vector< std::pair< unsigned int, unsigned int > > _gap_to_chan_map
stores the channel pairs for each gap
std::vector< EChannelType > _subch_type
subchannel type
std::vector< std::pair< unsigned int, unsigned int > > _gap_pairs_sf
sweeping flow model gap pairs per channel to specify directional edge flow
const unsigned int _n_cells
number of axial cells
std::vector< Real > _z_grid
axial location of nodes
Real _unheated_length_entry
unheated length of the fuel Pin at the entry of the assembly
const std::vector< unsigned int > _index_blockage
index of subchannels affected by blockage
const std::vector< Real > _z_blockage
axial location of blockage (inlet, outlet) [m]
const std::vector< Real > & _spacer_z
axial location of the spacers
const unsigned int _block_id
block index
virtual const std::string & name() const
const Real _flat_to_flat
the distance between flat surfaces of the duct facing each other
void addRequiredParam(const std::string &name, const std::string &doc_string)
unsigned int _n_channels
number of subchannels
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...
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.
const Real _pitch
Distance between the neighbor fuel pins, pitch.
const unsigned int _n_rings
number of rings of fuel pins
const std::vector< Real > _k_blockage
form loss coefficient of subchannels affected by blockage
registerMooseObjectRenamed("SubChannelApp", TriSubChannelMeshGenerator, "06/30/2025 24:00", SCMTriSubChannelMeshGenerator)
std::vector< std::vector< Real > > _subchannel_position
x,y coordinates of the subchannel centroids
Mesh class for triangular, edge and corner subchannels for hexagonal lattice fuel assemblies...
std::vector< Point > _pin_position
x,y coordinates of the fuel pins
std::vector< std::vector< Real > > _gij_map
gap size
const Real _pin_diameter
fuel Pin diameter
static InputParameters validParams()
registerMooseObject("SubChannelApp", SCMTriSubChannelMeshGenerator)
const Real _heated_length
heated length of the fuel Pin
Mesh class for triangular, edge and corner subchannels for hexagonal lattice fuel assemblies...
std::vector< std::vector< unsigned int > > _chan_to_pin_map
stores the fuel pins belonging to each subchannel
const std::vector< Real > _reduction_blockage
area reduction of subchannels affected by blockage
static void rodPositions(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 ...
const Real _unheated_length_entry
unheated length of the fuel Pin at the entry of the assembly
ExpressionBuilder::EBTerm pow(const ExpressionBuilder::EBTerm &left, T exponent)
std::unique_ptr< MeshBase > generate() override
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< std::vector< Real > > _k_grid
axial form loss coefficient per computational cell
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
std::vector< std::pair< unsigned int, unsigned int > > _gap_to_pin_map
stores the fuel pin pairs for each gap each gap
SCMTriSubChannelMeshGenerator(const InputParameters &parameters)
void mooseError(Args &&... args) const
const Real _duct_to_pin_gap
the gap thickness between the duct and peripheral fuel pins
void addClassDescription(const std::string &doc_string)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
const Real & _kij
Lateral form loss coefficient.
std::vector< std::vector< unsigned int > > _pin_to_chan_map
stores the map from pins to channels
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
const std::vector< Real > & _spacer_k
form loss coefficient of the spacers
std::vector< std::vector< Node * > > _nodes
nodes
const Real _unheated_length_exit
unheated length of the fuel Pin at the exit of the assembly
std::vector< std::vector< unsigned int > > _chan_to_gap_map
stores the gaps that forms each subchannel
static const std::string k
Definition: NS.h:130
void ErrorVector unsigned int
const Real pi