https://mooseframework.inl.gov
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 
18 registerMooseObjectRenamed("SubChannelApp",
19  SCMTriSubChannelMeshGenerator,
20  "06/30/2027 24:00",
22 registerMooseObjectRenamed("SubChannelApp",
23  TriSubChannelMeshGenerator,
24  "06/30/2027 24:00",
26 registerMooseObjectRenamed("SubChannelApp",
27  SCMTriPinMeshGenerator,
28  "06/30/2027 24:00",
30 registerMooseObjectRenamed("SubChannelApp",
31  TriPinMeshGenerator,
32  "06/30/2027 24:00",
34 
37 {
39  params.addClassDescription(
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 {
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;
244  _gap_to_chan_map.resize(_n_gaps);
245  _gap_to_pin_map.resize(_n_gaps);
246  gap_fill.resize(_n_gaps);
248  _gap_pairs_sf.resize(_n_channels);
249  _chan_pairs_sf.resize(_n_channels);
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  {
721  _subchannel_position[i][0] =
723  _pin_position[_chan_to_pin_map[i][2]](0)) /
724  3.0;
725  _subchannel_position[i][1] =
727  _pin_position[_chan_to_pin_map[i][2]](1)) /
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 
791 void
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.subdomain_name(_pin_block_id) = "fuel_pins";
835 }
836 
837 std::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->subdomain_name(_subchannel_block_id) = "subchannel";
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 }
SCMTriAssemblyMeshGenerator(const InputParameters &parameters)
const Real _flat_to_flat
the distance between flat surfaces of the duct facing each other
const std::vector< Real > & _spacer_k
form loss coefficient of the spacers
const unsigned int _subchannel_block_id
subchannel block index
void paramError(const std::string &param, Args... args) const
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
std::vector< std::vector< unsigned int > > _chan_to_gap_map
stores the gaps that forms each subchannel
const std::vector< Real > & _spacer_z
axial location of the spacers
std::vector< std::pair< unsigned int, unsigned int > > _chan_pairs_sf
sweeping flow model channel pairs to specify directional edge flow
std::vector< std::vector< Real > > _pins_in_rings
fuel pins that are belonging to each ring
const Real _unheated_length_entry
unheated length of the fuel Pin at the entry of the assembly
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 ...
const unsigned int _n_rings
number of rings of fuel pins
std::vector< std::vector< unsigned int > > _chan_to_pin_map
stores the fuel pins belonging to each subchannel
Real _unheated_length_entry
unheated length of the fuel Pin at the entry of the assembly
std::vector< EChannelType > _subch_type
subchannel type
std::vector< std::vector< unsigned int > > _pin_to_chan_map
stores the map from pins to channels
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]
const Real _hwire
wire lead length
void addRequiredParam(const std::string &name, const std::string &doc_string)
const Real _pitch
Distance between the neighbor fuel pins, pitch.
registerMooseObjectRenamed("SubChannelApp", SCMTriSubChannelMeshGenerator, "06/30/2027 24:00", SCMTriAssemblyMeshGenerator)
const std::vector< unsigned int > _index_blockage
index of subchannels affected by blockage
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 unsigned int _n_cells
number of axial cells
void deprecateParam(const std::string &old_name, const std::string &new_name, const std::string &removal_date)
const std::vector< Real > _reduction_blockage
area reduction of subchannels affected by blockage
unsigned int _n_channels
number of subchannels
registerMooseObject("SubChannelApp", SCMTriAssemblyMeshGenerator)
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::vector< EChannelType > _gap_type
gap type
const Real _duct_to_pin_gap
the gap thickness between the duct and peripheral fuel pins
static InputParameters validParams()
std::unique_ptr< MeshBase > generate() override
Mesh class for triangular, edge and corner subchannels for hexagonal lattice fuel assemblies...
ExpressionBuilder::EBTerm pow(const ExpressionBuilder::EBTerm &left, T exponent)
const unsigned int _pin_block_id
pin block index
const std::vector< Real > _k_blockage
form loss coefficient of subchannels affected by blockage
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< std::vector< Real > > _gij_map
gap size
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
const Real _unheated_length_exit
unheated length of the fuel Pin at the exit of the assembly
std::vector< Real > _z_grid
axial location of nodes
std::vector< std::vector< Node * > > _pin_nodes
pin nodes
void mooseError(Args &&... args) const
Mesh generator that builds a mesh of 1D lines representing subchannels and pins in a triangular assem...
void addClassDescription(const std::string &doc_string)
const Real _pin_diameter
fuel Pin diameter
std::vector< std::pair< unsigned int, unsigned int > > _gap_to_chan_map
stores the channel pairs for each gap
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
std::vector< std::vector< Node * > > _nodes
nodes
const Real & _kij
Lateral form loss coefficient.
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
std::vector< Point > _pin_position
x,y coordinates of the fuel pins
static InputParameters validParams()
unsigned int _n_gaps
number of gaps
static const std::string k
Definition: NS.h:134
std::vector< std::vector< Real > > _subchannel_position
x,y coordinates of the subchannel centroids
void ErrorVector unsigned int
std::vector< std::pair< unsigned int, unsigned int > > _gap_pairs_sf
sweeping flow model gap pairs per channel to specify directional edge flow
const Real _heated_length
heated length of the fuel Pin
std::vector< std::pair< unsigned int, unsigned int > > _gap_to_pin_map
stores the fuel pin pairs for each gap each gap
unsigned int _npins
number of fuel pins
std::vector< std::vector< Real > > _k_grid
axial form loss coefficient per computational cell