https://mooseframework.inl.gov
TriSubChannel1PhaseProblem.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 "AuxiliarySystem.h"
12 #include "TriSubChannelMesh.h"
13 #include "SCM.h"
14 
16 
19 {
21  params.addClassDescription("Solver class for subchannels in a triangular lattice assembly and "
22  "bare/wire-wrapped fuel pins");
23  return params;
24 }
25 
27  : SubChannel1PhaseProblem(params),
28  _tri_sch_mesh(SCM::getMesh<TriSubChannelMesh>(_subchannel_mesh))
29 {
30  // Initializing heat conduction system
31  LibmeshPetscCall(createPetscMatrix(
34  LibmeshPetscCall(createPetscMatrix(
37  LibmeshPetscCall(createPetscMatrix(
40 }
41 
43 {
44  PetscErrorCode ierr = cleanUp();
45  if (ierr)
46  mooseError(name(), ": Error in memory cleanup");
47 }
48 
49 PetscErrorCode
51 {
53  // Clean up heat conduction system
54  LibmeshPetscCall(MatDestroy(&_hc_axial_heat_conduction_mat));
55  LibmeshPetscCall(VecDestroy(&_hc_axial_heat_conduction_rhs));
56  LibmeshPetscCall(MatDestroy(&_hc_radial_heat_conduction_mat));
57  LibmeshPetscCall(VecDestroy(&_hc_radial_heat_conduction_rhs));
58  LibmeshPetscCall(MatDestroy(&_hc_sweep_enthalpy_mat));
59  LibmeshPetscCall(VecDestroy(&_hc_sweep_enthalpy_rhs));
60  PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
61 }
62 
63 void
65 {
66  if (_deformation)
67  {
69  Real standard_area, wire_area, additional_area, wetted_perimeter, displaced_area;
70  auto flat_to_flat = _tri_sch_mesh.getFlatToFlat();
71  auto n_rings = _tri_sch_mesh.getNumOfRings();
73  auto pin_diameter = _subchannel_mesh.getPinDiameter();
74  auto wire_diameter = _tri_sch_mesh.getWireDiameter();
75  auto wire_lead_length = _tri_sch_mesh.getWireLeadLength();
76  auto gap = _tri_sch_mesh.getDuctToPinGap();
77  auto z_blockage = _subchannel_mesh.getZBlockage();
78  auto index_blockage = _subchannel_mesh.getIndexBlockage();
79  auto reduction_blockage = _subchannel_mesh.getReductionBlockage();
80  auto theta = std::acos(wire_lead_length /
81  std::sqrt(std::pow(wire_lead_length, 2) +
82  std::pow(libMesh::pi * (pin_diameter + wire_diameter), 2)));
83  for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
84  {
85  for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
86  {
87  auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
88  auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
89  auto Z = _z_grid[iz];
90  Real rod_area = 0.0;
91  Real rod_perimeter = 0.0;
92  for (auto i_pin : _subchannel_mesh.getChannelPins(i_ch))
93  {
94  auto * pin_node = _subchannel_mesh.getPinNode(i_pin, iz);
95  if (subch_type == EChannelType::CENTER || subch_type == EChannelType::CORNER)
96  {
97  rod_area +=
98  (1.0 / 6.0) * 0.25 * M_PI * (*_Dpin_soln)(pin_node) * (*_Dpin_soln)(pin_node);
99  rod_perimeter += (1.0 / 6.0) * M_PI * (*_Dpin_soln)(pin_node);
100  }
101  else
102  {
103  rod_area +=
104  (1.0 / 4.0) * 0.25 * M_PI * (*_Dpin_soln)(pin_node) * (*_Dpin_soln)(pin_node);
105  rod_perimeter += (1.0 / 4.0) * M_PI * (*_Dpin_soln)(pin_node);
106  }
107  }
108 
109  if (subch_type == EChannelType::CENTER)
110  {
111  standard_area = std::pow(pitch, 2.0) * std::sqrt(3.0) / 4.0;
112  additional_area = 0.0;
113  displaced_area = 0.0;
114  wire_area = libMesh::pi * std::pow(wire_diameter, 2.0) / 8.0 / std::cos(theta);
115  wetted_perimeter = rod_perimeter + 0.5 * libMesh::pi * wire_diameter / std::cos(theta);
116  }
117  else if (subch_type == EChannelType::EDGE)
118  {
119  standard_area = pitch * (pin_diameter / 2.0 + gap);
120  additional_area = 0.0;
121  displaced_area = (*_displacement_soln)(node)*pitch;
122  wire_area = libMesh::pi * std::pow(wire_diameter, 2.0) / 8.0 / std::cos(theta);
123  wetted_perimeter =
124  rod_perimeter + 0.5 * libMesh::pi * wire_diameter / std::cos(theta) + pitch;
125  }
126  else
127  {
128  standard_area = 1.0 / std::sqrt(3.0) * std::pow((pin_diameter / 2.0 + gap), 2.0);
129  additional_area = 0.0;
130  displaced_area = 1.0 / std::sqrt(3.0) *
131  (pin_diameter + 2.0 * gap + (*_displacement_soln)(node)) *
132  (*_displacement_soln)(node);
133  wire_area = libMesh::pi / 24.0 * std::pow(wire_diameter, 2.0) / std::cos(theta);
134  wetted_perimeter =
135  rod_perimeter + libMesh::pi * wire_diameter / std::cos(theta) / 6.0 +
136  2.0 / std::sqrt(3.0) * (pin_diameter / 2.0 + gap + (*_displacement_soln)(node));
137  }
138 
140  auto subchannel_area =
141  standard_area + additional_area + displaced_area - rod_area - wire_area;
142 
144  auto overlapping_pin_area = 0.0;
145  auto overlapping_wetted_perimeter = 0.0;
146  for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
147  {
148  auto gap_pins = _subchannel_mesh.getGapPins(i_gap);
149  auto pin_1 = gap_pins.first;
150  auto pin_2 = gap_pins.second;
151  auto * pin_node_1 = _subchannel_mesh.getPinNode(pin_1, iz);
152  auto * pin_node_2 = _subchannel_mesh.getPinNode(pin_2, iz);
153  auto Diameter1 = (*_Dpin_soln)(pin_node_1);
154  auto Radius1 = Diameter1 / 2.0;
155  auto Diameter2 = (*_Dpin_soln)(pin_node_2);
156  auto Radius2 = Diameter2 / 2.0;
158 
159  if (pitch < (Radius1 + Radius2)) // overlapping pins
160  {
161  mooseWarning(" The gap of index : '", i_gap, " at axial cell ", iz, " ' is blocked.");
162  auto cos1 =
163  (pitch * pitch + Radius1 * Radius1 - Radius2 * Radius2) / (2 * pitch * Radius1);
164  auto cos2 =
165  (pitch * pitch + Radius2 * Radius2 - Radius1 * Radius1) / (2 * pitch * Radius2);
166  auto angle1 = 2.0 * acos(cos1);
167  auto angle2 = 2.0 * acos(cos2);
168  // half of the intersecting arc-length
169  overlapping_wetted_perimeter += 0.5 * angle1 * Radius1 + 0.5 * angle2 * Radius2;
170  // Half of the overlapping area
171  overlapping_pin_area +=
172  0.5 * Radius1 * Radius1 * acos(cos1) + 0.5 * Radius2 * Radius2 * acos(cos2) -
173  0.25 * sqrt((-pitch + Radius1 + Radius2) * (pitch + Radius1 - Radius2) *
174  (pitch - Radius1 + Radius2) * (pitch + Radius1 + Radius2));
175  }
176  }
177  subchannel_area += overlapping_pin_area; // correct surface area
178  wetted_perimeter += -overlapping_wetted_perimeter; // correct wetted perimeter
179 
181  auto index = 0;
182  for (const auto & i_blockage : index_blockage)
183  {
184  if (i_ch == i_blockage && (Z >= z_blockage.front() && Z <= z_blockage.back()))
185  {
186  subchannel_area *= reduction_blockage[index];
187  }
188  index++;
189  }
190  _S_flow_soln->set(node, subchannel_area);
191  _w_perim_soln->set(node, wetted_perimeter);
192  }
193  }
195  for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
196  {
197  for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
198  {
199  auto gap_pins = _subchannel_mesh.getGapPins(i_gap);
200  auto pin_1 = gap_pins.first;
201  auto pin_2 = gap_pins.second;
202  auto * pin_node_1 = _subchannel_mesh.getPinNode(pin_1, iz);
203  auto * pin_node_2 = _subchannel_mesh.getPinNode(pin_2, iz);
204 
205  if (pin_1 == pin_2) // Corner or edge gap
206  {
207  auto displacement = 0.0;
208  auto counter = 0.0;
209  for (auto i_ch : _subchannel_mesh.getPinChannels(pin_1))
210  {
211  auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
212  auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
213  if (subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER)
214  {
215  displacement += (*_displacement_soln)(node);
216  counter += 1.0;
217  }
218  }
219  displacement = displacement / counter;
220  _tri_sch_mesh._gij_map[iz][i_gap] =
221  0.5 * (flat_to_flat - (n_rings - 1) * pitch * std::sqrt(3.0) -
222  (*_Dpin_soln)(pin_node_1)) +
223  displacement;
224  }
225  else // center gap
226  {
227  _tri_sch_mesh._gij_map[iz][i_gap] =
228  pitch - (*_Dpin_soln)(pin_node_1) / 2.0 - (*_Dpin_soln)(pin_node_2) / 2.0;
229  }
230  // if pins come in contact, the gap is zero
231  if (_tri_sch_mesh._gij_map[iz][i_gap] <= 0.0)
232  _tri_sch_mesh._gij_map[iz][i_gap] = 0.0;
233  }
234  }
235  }
236 
237  for (unsigned int iz = 1; iz < _n_cells + 1; iz++)
238  {
239  for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
240  {
241  auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
242  auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
243  _mdot_soln->set(node_out, (*_mdot_soln)(node_in));
244  }
245  }
246 
247  // We must do a global assembly to make sure data is parallel consistent before we do things
248  // like compute L2 norms
249  _aux->solution().close();
250 }
251 
252 Real
254 {
256  auto Re = friction_args.Re;
257  auto i_ch = friction_args.i_ch;
258  auto S = friction_args.S;
259  auto w_perim = friction_args.w_perim;
260  auto Dh_i = 4.0 * S / w_perim;
261  Real aL, b1L, b2L, cL;
262  Real aT, b1T, b2T, cT;
263  const Real & pitch = _subchannel_mesh.getPitch();
264  const Real & pin_diameter = _subchannel_mesh.getPinDiameter();
265  const Real & wire_lead_length = _tri_sch_mesh.getWireLeadLength();
266  const Real & wire_diameter = _tri_sch_mesh.getWireDiameter();
267  auto p_over_d = pitch / pin_diameter;
268  auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
269  // This gap is a constant value for the whole assembly. Might want to make it
270  // subchannel specific in the future if we have duct deformation.
271  auto gap = _tri_sch_mesh.getDuctToPinGap();
272  auto w_over_d = (pin_diameter + gap) / pin_diameter;
273  auto ReL = std::pow(10, (p_over_d - 1)) * 320.0;
274  auto ReT = std::pow(10, 0.7 * (p_over_d - 1)) * 1.0E+4;
275  auto psi = std::log(Re / ReL) / std::log(ReT / ReL);
276  const Real lambda = 7.0;
277  auto theta = std::acos(wire_lead_length /
278  std::sqrt(std::pow(wire_lead_length, 2) +
279  std::pow(libMesh::pi * (pin_diameter + wire_diameter), 2)));
280  auto wd_t = (19.56 - 98.71 * (wire_diameter / pin_diameter) +
281  303.47 * std::pow((wire_diameter / pin_diameter), 2.0)) *
282  std::pow((wire_lead_length / pin_diameter), -0.541);
283  auto wd_l = 1.4 * wd_t;
284  auto ws_t = -11.0 * std::log(wire_lead_length / pin_diameter) + 19.0;
285  auto ws_l = ws_t;
286  Real pw_p = 0.0;
287  Real ar = 0.0;
288  Real a_p = 0.0;
289 
290  // Find the coefficients of bare Pin bundle friction factor
291  // correlations for turbulent and laminar flow regimes. Todreas & Kazimi, Nuclear Systems
292  // second edition, Volume 1, Chapter 9.6
293  if (subch_type == EChannelType::CENTER)
294  {
295  if (p_over_d < 1.1)
296  {
297  aL = 26.0;
298  b1L = 888.2;
299  b2L = -3334.0;
300  aT = 0.09378;
301  b1T = 1.398;
302  b2T = -8.664;
303  }
304  else
305  {
306  aL = 62.97;
307  b1L = 216.9;
308  b2L = -190.2;
309  aT = 0.1458;
310  b1T = 0.03632;
311  b2T = -0.03333;
312  }
313  // laminar flow friction factor for bare Pin bundle - Center subchannel
314  cL = aL + b1L * (p_over_d - 1) + b2L * std::pow((p_over_d - 1), 2.0);
315  // turbulent flow friction factor for bare Pin bundle - Center subchannel
316  cT = aT + b1T * (p_over_d - 1) + b2T * std::pow((p_over_d - 1), 2.0);
317  }
318  else if (subch_type == EChannelType::EDGE)
319  {
320  if (w_over_d < 1.1)
321  {
322  aL = 26.18;
323  b1L = 554.5;
324  b2L = -1480.0;
325  aT = 0.09377;
326  b1T = 0.8732;
327  b2T = -3.341;
328  }
329  else
330  {
331  aL = 44.4;
332  b1L = 256.7;
333  b2L = -267.6;
334  aT = 0.1430;
335  b1T = 0.04199;
336  b2T = -0.04428;
337  }
338  // laminar flow friction factor for bare Pin bundle - Edge subchannel
339  cL = aL + b1L * (w_over_d - 1) + b2L * std::pow((w_over_d - 1), 2.0);
340  // turbulent flow friction factor for bare Pin bundle - Edge subchannel
341  cT = aT + b1T * (w_over_d - 1) + b2T * std::pow((w_over_d - 1), 2.0);
342  }
343  else
344  {
345  if (w_over_d < 1.1)
346  {
347  aL = 26.98;
348  b1L = 1636.0;
349  b2L = -10050.0;
350  aT = 0.1004;
351  b1T = 1.625;
352  b2T = -11.85;
353  }
354  else
355  {
356  aL = 87.26;
357  b1L = 38.59;
358  b2L = -55.12;
359  aT = 0.1499;
360  b1T = 0.006706;
361  b2T = -0.009567;
362  }
363  // laminar flow friction factor for bare Pin bundle - Corner subchannel
364  cL = aL + b1L * (w_over_d - 1) + b2L * std::pow((w_over_d - 1), 2.0);
365  // turbulent flow friction factor for bare Pin bundle - Corner subchannel
366  cT = aT + b1T * (w_over_d - 1) + b2T * std::pow((w_over_d - 1), 2.0);
367  }
368 
369  // Find the coefficients of wire-wrapped Pin bundle friction factor
370  // correlations for turbulent and laminar flow regimes. Todreas & Kazimi, Nuclear Systems
371  // Volume 1 Chapter 9-6 also Chen and Todreas (2018).
372  if ((wire_diameter != 0.0) && (wire_lead_length != 0.0))
373  {
374  if (subch_type == EChannelType::CENTER)
375  {
376  // wetted perimeter for center subchannel and bare Pin bundle
377  pw_p = libMesh::pi * pin_diameter / 2.0;
378  // wire projected area - center subchannel wire-wrapped bundle
379  ar = libMesh::pi * (pin_diameter + wire_diameter) * wire_diameter / 6.0;
380  // bare Pin bundle center subchannel flow area (normal area + wire area)
381  a_p = S + libMesh::pi * std::pow(wire_diameter, 2.0) / 8.0 / std::cos(theta);
382  // turbulent friction factor equation constant - Center subchannel
383  cT *= (pw_p / w_perim);
384  cT += wd_t * (3.0 * ar / a_p) * (Dh_i / wire_lead_length) *
385  std::pow((Dh_i / wire_diameter), 0.18);
386  // laminar friction factor equation constant - Center subchannel
387  cL *= (pw_p / w_perim);
388  cL += wd_l * (3.0 * ar / a_p) * (Dh_i / wire_lead_length) * (Dh_i / wire_diameter);
389  }
390  else if (subch_type == EChannelType::EDGE)
391  {
392  // wire projected area - edge subchannel wire-wrapped bundle
393  ar = libMesh::pi * (pin_diameter + wire_diameter) * wire_diameter / 4.0;
394  // bare Pin bundle edge subchannel flow area (normal area + wire area)
395  a_p = S + libMesh::pi * std::pow(wire_diameter, 2.0) / 8.0 / std::cos(theta);
396  // turbulent friction factor equation constant - Edge subchannel
397  cT *= std::pow((1 + ws_t * (ar / a_p) * std::pow(std::tan(theta), 2.0)), 1.41);
398  // laminar friction factor equation constant - Edge subchannel
399  cL *= (1 + ws_l * (ar / a_p) * std::pow(std::tan(theta), 2.0));
400  }
401  else
402  {
403  // wire projected area - corner subchannel wire-wrapped bundle
404  ar = libMesh::pi * (pin_diameter + wire_diameter) * wire_diameter / 6.0;
405  // bare Pin bundle corner subchannel flow area (normal area + wire area)
406  a_p = S + libMesh::pi * std::pow(wire_diameter, 2.0) / 24.0 / std::cos(theta);
407  // turbulent friction factor equation constant - Corner subchannel
408  cT *= std::pow((1 + ws_t * (ar / a_p) * std::pow(std::tan(theta), 2.0)), 1.41);
409  // laminar friction factor equation constant - Corner subchannel
410  cL *= (1 + ws_l * (ar / a_p) * std::pow(std::tan(theta), 2.0));
411  }
412  }
413 
414  // laminar friction factor
415  auto fL = cL / Re;
416  // turbulent friction factor
417  auto fT = cT / std::pow(Re, 0.18);
418 
419  if (Re < ReL)
420  {
421  // laminar flow
422  return fL;
423  }
424  else if (Re > ReT)
425  {
426  // turbulent flow
427  return fT;
428  }
429  else
430  {
431  // transient flow: psi definition uses a Bulk ReT/ReL number, same for all channels
432  return fL * std::pow((1 - psi), 1.0 / 3.0) * (1 - std::pow(psi, lambda)) +
433  fT * std::pow(psi, 1.0 / 3.0);
434  }
435 }
436 
437 Real
438 TriSubChannel1PhaseProblem::computeBeta(unsigned int i_gap, unsigned int iz)
439 {
440  auto beta = 0.0;
441  const Real & pitch = _subchannel_mesh.getPitch();
442  const Real & pin_diameter = _subchannel_mesh.getPinDiameter();
443  const Real & wire_lead_length = _tri_sch_mesh.getWireLeadLength();
444  const Real & wire_diameter = _tri_sch_mesh.getWireDiameter();
445  auto chans = _subchannel_mesh.getGapChannels(i_gap);
446  unsigned int i_ch = chans.first;
447  unsigned int j_ch = chans.second;
448  auto subch_type1 = _subchannel_mesh.getSubchannelType(i_ch);
449  auto subch_type2 = _subchannel_mesh.getSubchannelType(j_ch);
450  auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
451  auto * node_out_i = _subchannel_mesh.getChannelNode(i_ch, iz);
452  auto * node_in_j = _subchannel_mesh.getChannelNode(j_ch, iz - 1);
453  auto * node_out_j = _subchannel_mesh.getChannelNode(j_ch, iz);
454  auto Si_in = (*_S_flow_soln)(node_in_i);
455  auto Sj_in = (*_S_flow_soln)(node_in_j);
456  auto Si_out = (*_S_flow_soln)(node_out_i);
457  auto Sj_out = (*_S_flow_soln)(node_out_j);
458  auto S_total = Si_in + Sj_in + Si_out + Sj_out;
459  auto Si = 0.5 * (Si_in + Si_out);
460  auto Sj = 0.5 * (Sj_in + Sj_out);
461  auto w_perim_i = 0.5 * ((*_w_perim_soln)(node_in_i) + (*_w_perim_soln)(node_out_i));
462  auto w_perim_j = 0.5 * ((*_w_perim_soln)(node_in_j) + (*_w_perim_soln)(node_out_j));
463  auto avg_mu = (1 / S_total) * ((*_mu_soln)(node_out_i)*Si_out + (*_mu_soln)(node_in_i)*Si_in +
464  (*_mu_soln)(node_out_j)*Sj_out + (*_mu_soln)(node_in_j)*Sj_in);
465  auto avg_hD = 4.0 * (Si + Sj) / (w_perim_i + w_perim_j);
466  auto avg_massflux =
467  0.5 * (((*_mdot_soln)(node_in_i) + (*_mdot_soln)(node_in_j)) / (Si_in + Sj_in) +
468  ((*_mdot_soln)(node_out_i) + (*_mdot_soln)(node_out_j)) / (Si_out + Sj_out));
469  auto Re = avg_massflux * avg_hD / avg_mu;
470  // crossflow area between channels i,j (dz*gap_width)
471  auto gap = _subchannel_mesh.getGapWidth(iz, i_gap);
472  // Calculation of flow regime
473  auto ReL = 320.0 * std::pow(10.0, pitch / pin_diameter - 1);
474  auto ReT = 10000.0 * std::pow(10.0, 0.7 * (pitch / pin_diameter - 1));
475  // Calculation of Turbulent Crossflow for wire-wrapped triangular assemblies. Cheng &
476  // Todreas (1986)
477  if ((subch_type1 == EChannelType::CENTER || subch_type2 == EChannelType::CENTER) &&
478  (wire_lead_length != 0) && (wire_diameter != 0))
479  {
480  // Calculation of geometric parameters
481  auto theta = std::acos(wire_lead_length /
482  std::sqrt(std::pow(wire_lead_length, 2) +
483  std::pow(libMesh::pi * (pin_diameter + wire_diameter), 2)));
484  auto Ar1 = libMesh::pi * (pin_diameter + wire_diameter) * wire_diameter / 6.0;
485  auto A1prime =
486  (std::sqrt(3.0) / 4.0) * std::pow(pitch, 2) - libMesh::pi * std::pow(pin_diameter, 2) / 8.0;
487  auto A1 = A1prime - libMesh::pi * std::pow(wire_diameter, 2) / 8.0 / std::cos(theta);
488  auto Cm = 0.0;
489  if (Re < ReL)
490  {
491  Cm = 0.077 * std::pow((pitch - pin_diameter) / pin_diameter, -0.5);
492  }
493  else if (Re > ReT)
494  {
495  Cm = 0.14 * std::pow((pitch - pin_diameter) / pin_diameter, -0.5);
496  }
497  else
498  {
499  auto psi = (std::log(Re) - std::log(ReL)) / (std::log(ReT) - std::log(ReL));
500  auto gamma = 2.0 / 3.0;
501  Cm = 0.14 * std::pow((pitch - pin_diameter) / pin_diameter, -0.5) +
502  (0.14 * std::pow((pitch - pin_diameter) / pin_diameter, -0.5) -
503  0.077 * std::pow((pitch - pin_diameter) / pin_diameter, -0.5)) *
504  std::pow(psi, gamma);
505  }
506  // Calculation of turbulent mixing parameter
507  beta = Cm * std::pow(Ar1 / A1, 0.5) * std::tan(theta);
508  }
509  // Calculation of Turbulent Crossflow for bare assemblies, from Kim and Chung (2001).
510  else if ((wire_lead_length == 0) && (wire_diameter == 0))
511  {
512  Real gamma = 20.0; // empirical constant
513  Real sf = 2.0 / 3.0; // shape factor
514  Real a = 0.18;
515  Real b = 0.2;
516  auto f = a * std::pow(Re, -b); // Rehme 1992 circular tube friction factor
517  auto k = (1 / S_total) *
518  (_fp->k_from_p_T((*_P_soln)(node_out_i) + _P_out, (*_T_soln)(node_out_i)) * Si_out +
519  _fp->k_from_p_T((*_P_soln)(node_in_i) + _P_out, (*_T_soln)(node_in_i)) * Si_in +
520  _fp->k_from_p_T((*_P_soln)(node_out_j) + _P_out, (*_T_soln)(node_out_j)) * Sj_out +
521  _fp->k_from_p_T((*_P_soln)(node_in_j) + _P_out, (*_T_soln)(node_in_j)) * Sj_in);
522  auto cp = (1 / S_total) *
523  (_fp->cp_from_p_T((*_P_soln)(node_out_i) + _P_out, (*_T_soln)(node_out_i)) * Si_out +
524  _fp->cp_from_p_T((*_P_soln)(node_in_i) + _P_out, (*_T_soln)(node_in_i)) * Si_in +
525  _fp->cp_from_p_T((*_P_soln)(node_out_j) + _P_out, (*_T_soln)(node_out_j)) * Sj_out +
526  _fp->cp_from_p_T((*_P_soln)(node_in_j) + _P_out, (*_T_soln)(node_in_j)) * Sj_in);
527  auto Pr = avg_mu * cp / k; // Prandtl number
528  auto Pr_t = Pr * (Re / gamma) * std::sqrt(f / 8.0); // Turbulent Prandtl number
529  auto delta = pitch / std::sqrt(3.0); // centroid to centroid distance
530  auto L_x = sf * delta; // axial length scale (gap is the lateral length scale)
531  auto lamda = gap / L_x; // aspect ratio
532  auto a_x = 1.0 - 2.0 * lamda * lamda / libMesh::pi; // velocity coefficient
533  auto z_FP_over_D = (2.0 * L_x / pin_diameter) *
534  (1 + (-0.5 * std::log(lamda) + 0.5 * std::log(4.0) - 0.25) * lamda * lamda);
535  auto Str = 1.0 / (0.822 * (gap / pin_diameter) + 0.144); // Strouhal number (Wu & Trupp 1994)
536  auto freq_factor = 2.0 / std::pow(gamma, 2) * std::sqrt(a / 8.0) * (avg_hD / gap);
537  auto rod_mixing = (1 / Pr_t) * lamda;
538  auto axial_mixing = a_x * z_FP_over_D * Str;
539  // Mixing Stanton number: Stg (eq 25,Kim and Chung (2001), eq 19 (Jeong et. al 2005)
540  beta = freq_factor * (rod_mixing + axial_mixing) * std::pow(Re, -b / 2.0);
541  }
542  mooseAssert(beta >= 0,
543  "beta should be positive for the inner gaps, or zero for the edge gaps, because this "
544  "case is covered "
545  "explicitly in the computeh method.");
546  return beta;
547 }
548 
549 Real
550 TriSubChannel1PhaseProblem::computeAddedHeatPin(unsigned int i_ch, unsigned int iz)
551 {
552  auto dz = _z_grid[iz] - _z_grid[iz - 1];
553  auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
554 
555  if (_pin_mesh_exist)
556  {
557  double factor;
558  switch (subch_type)
559  {
561  factor = 1.0 / 6.0;
562  break;
563  case EChannelType::EDGE:
564  factor = 1.0 / 4.0;
565  break;
567  factor = 1.0 / 6.0;
568  break;
569  default:
570  return 0.0; // handle invalid subch_type if needed
571  }
572  double heat_rate_in = 0.0;
573  double heat_rate_out = 0.0;
574  for (auto i_pin : _subchannel_mesh.getChannelPins(i_ch))
575  {
576  auto * node_in = _subchannel_mesh.getPinNode(i_pin, iz - 1);
577  auto * node_out = _subchannel_mesh.getPinNode(i_pin, iz);
578  heat_rate_out += factor * (*_q_prime_soln)(node_out);
579  heat_rate_in += factor * (*_q_prime_soln)(node_in);
580  }
581  return (heat_rate_in + heat_rate_out) * dz / 2.0;
582  }
583  else
584  {
585  auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
586  auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
587  return ((*_q_prime_soln)(node_out) + (*_q_prime_soln)(node_in)) * dz / 2.0;
588  }
589 }
590 
591 void
593 {
594  unsigned int last_node = (iblock + 1) * _block_size;
595  unsigned int first_node = iblock * _block_size + 1;
596  auto heated_length = _subchannel_mesh.getHeatedLength();
597  auto unheated_length_entry = _subchannel_mesh.getHeatedLengthEntry();
598  const Real & wire_lead_length = _tri_sch_mesh.getWireLeadLength();
599  const Real & wire_diameter = _tri_sch_mesh.getWireDiameter();
600  const Real & pitch = _subchannel_mesh.getPitch();
601  const Real & pin_diameter = _subchannel_mesh.getPinDiameter();
602 
603  if (iblock == 0)
604  {
605  for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
606  {
607  auto * node = _subchannel_mesh.getChannelNode(i_ch, 0);
608  auto h_out = _fp->h_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
609  if (h_out < 0)
610  {
611  mooseError(
612  name(), " : Calculation of negative Enthalpy h_out = : ", h_out, " Axial Level= : ", 0);
613  }
614  _h_soln->set(node, h_out);
615  }
616  }
617 
618  if (!_implicit_bool)
619  {
620  for (unsigned int iz = first_node; iz < last_node + 1; iz++)
621  {
622  auto z_grid = _subchannel_mesh.getZGrid();
623  auto dz = z_grid[iz] - z_grid[iz - 1];
624  Real gedge_ave = 0.0;
625  Real mdot_sum = 0.0;
626  Real si_sum = 0.0;
627  for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
628  {
629  auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
630  if (subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER)
631  {
632  auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
633  auto Si = (*_S_flow_soln)(node_in);
634  auto mdot_in = (*_mdot_soln)(node_in);
635  mdot_sum = mdot_sum + mdot_in;
636  si_sum = si_sum + Si;
637  }
638  }
639  gedge_ave = mdot_sum / si_sum;
640 
641  for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
642  {
643  auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
644  auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
645  auto mdot_in = (*_mdot_soln)(node_in);
646  auto h_in = (*_h_soln)(node_in); // J/kg
647  auto volume = dz * (*_S_flow_soln)(node_in);
648  auto mdot_out = (*_mdot_soln)(node_out);
649  auto h_out = 0.0;
650  Real sumWijh = 0.0;
651  Real sumWijPrimeDhij = 0.0;
652  Real e_cond = 0.0;
653 
654  Real added_enthalpy;
655  if (z_grid[iz] > unheated_length_entry &&
656  z_grid[iz] <= unheated_length_entry + heated_length)
657  {
658  added_enthalpy = computeAddedHeatPin(i_ch, iz);
659  }
660  else
661  added_enthalpy = 0.0;
662 
663  added_enthalpy += computeAddedHeatDuct(i_ch, iz);
664 
665  // compute the sweep flow enthalpy change
666  auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
667  Real sweep_enthalpy = 0.0;
668 
669  if ((subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER) &&
670  (wire_diameter != 0.0) && (wire_lead_length != 0.0))
671  {
672  const Real & pitch = _subchannel_mesh.getPitch();
673  const Real & pin_diameter = _subchannel_mesh.getPinDiameter();
674  const Real & wire_lead_length = _tri_sch_mesh.getWireLeadLength();
675  const Real & wire_diameter = _tri_sch_mesh.getWireDiameter();
676  auto gap = _tri_sch_mesh.getDuctToPinGap();
677  auto w = pin_diameter + gap;
678  auto theta =
679  std::acos(wire_lead_length /
680  std::sqrt(std::pow(wire_lead_length, 2) +
681  std::pow(libMesh::pi * (pin_diameter + wire_diameter), 2)));
682  auto Sij = dz * gap;
683  auto Si = (*_S_flow_soln)(node_in);
684  // in/out channels for i_ch
685  auto sweep_in = _tri_sch_mesh.getSweepFlowChans(i_ch).first;
686  auto * node_sin = _subchannel_mesh.getChannelNode(sweep_in, iz - 1);
687 
688  // Calculation of flow regime
689  auto ReL = 320.0 * std::pow(10.0, pitch / pin_diameter - 1);
690  auto ReT = 10000.0 * std::pow(10.0, 0.7 * (pitch / pin_diameter - 1));
691  auto massflux = (*_mdot_soln)(node_in) / Si;
692  auto w_perim = (*_w_perim_soln)(node_in);
693  auto mu = (*_mu_soln)(node_in);
694  // hydraulic diameter
695  auto hD = 4.0 * Si / w_perim;
696  auto Re = massflux * hD / mu;
697  // Calculation of geometric parameters
698  auto Ar2 = libMesh::pi * (pin_diameter + wire_diameter) * wire_diameter / 4.0;
699  auto A2prime =
700  pitch * (w - pin_diameter / 2.0) - libMesh::pi * std::pow(pin_diameter, 2) / 8.0;
701  auto A2 = A2prime - libMesh::pi * std::pow(wire_diameter, 2) / 8.0 / std::cos(theta);
702  auto Cs = 0.0;
703  if (Re < ReL)
704  {
705  Cs = 0.033 * std::pow(wire_lead_length / pin_diameter, 0.3);
706  }
707  else if (Re > ReT)
708  {
709  Cs = 0.75 * std::pow(wire_lead_length / pin_diameter, 0.3);
710  }
711  else
712  {
713  auto psi = (std::log(Re) - std::log(ReL)) / (std::log(ReT) - std::log(ReL));
714  auto gamma = 2.0 / 3.0;
715  Cs = 0.75 * std::pow(wire_lead_length / pin_diameter, 0.3) +
716  (0.75 * std::pow(wire_lead_length / pin_diameter, 0.3) -
717  0.033 * std::pow(wire_lead_length / pin_diameter, 0.3)) *
718  std::pow(psi, gamma);
719  }
720  // Calculation of turbulent mixing parameter
721  auto beta = Cs * std::pow(Ar2 / A2, 0.5) * std::tan(theta);
722 
723  auto wsweep_in = gedge_ave * beta * Sij;
724  auto wsweep_out = gedge_ave * beta * Sij;
725  auto sweep_hin = (*_h_soln)(node_sin);
726  auto sweep_hout = (*_h_soln)(node_in);
727  sweep_enthalpy = (wsweep_in * sweep_hin - wsweep_out * sweep_hout);
728  }
729 
730  // Calculate sum of crossflow into channel i from channels j around i
731  unsigned int counter = 0;
732  for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
733  {
734  auto chans = _subchannel_mesh.getGapChannels(i_gap);
735  unsigned int ii_ch = chans.first;
736  // i is always the smallest and first index in the mapping
737  unsigned int jj_ch = chans.second;
738  auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
739  auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
740  // Define donor enthalpy
741  auto h_star = 0.0;
742  if (_Wij(i_gap, iz) > 0.0)
743  h_star = (*_h_soln)(node_in_i);
744  else if (_Wij(i_gap, iz) < 0.0)
745  h_star = (*_h_soln)(node_in_j);
746  // take care of the sign by applying the map, use donor cell
747  sumWijh += _subchannel_mesh.getCrossflowSign(i_ch, counter) * _Wij(i_gap, iz) * h_star;
748  sumWijPrimeDhij += _WijPrime(i_gap, iz) * (2 * (*_h_soln)(node_in) -
749  (*_h_soln)(node_in_j) - (*_h_soln)(node_in_i));
750  counter++;
751 
752  // compute the radial heat conduction through the gaps
753  auto subch_type_i = _subchannel_mesh.getSubchannelType(ii_ch);
754  auto subch_type_j = _subchannel_mesh.getSubchannelType(jj_ch);
755  Real dist_ij = pitch;
756 
757  if (subch_type_i == EChannelType::EDGE && subch_type_j == EChannelType::EDGE)
758  {
759  dist_ij = pitch;
760  }
761  else if ((subch_type_i == EChannelType::CORNER && subch_type_j == EChannelType::EDGE) ||
762  (subch_type_i == EChannelType::EDGE && subch_type_j == EChannelType::CORNER))
763  {
764  dist_ij = pitch;
765  }
766  else
767  {
768  dist_ij = pitch / std::sqrt(3);
769  }
770 
771  auto Sij = dz * _subchannel_mesh.getGapWidth(iz, i_gap);
772  auto thcon_i = _fp->k_from_p_T((*_P_soln)(node_in_i) + _P_out, (*_T_soln)(node_in_i));
773  auto thcon_j = _fp->k_from_p_T((*_P_soln)(node_in_j) + _P_out, (*_T_soln)(node_in_j));
774  auto shape_factor =
775  0.66 * (pitch / pin_diameter) *
776  std::pow((_subchannel_mesh.getGapWidth(iz, i_gap) / pin_diameter), -0.3);
777  if (ii_ch == i_ch)
778  {
779  e_cond += 0.5 * (thcon_i + thcon_j) * Sij * shape_factor *
780  ((*_T_soln)(node_in_j) - (*_T_soln)(node_in_i)) / dist_ij;
781  }
782  else
783  {
784  e_cond += -0.5 * (thcon_i + thcon_j) * Sij * shape_factor *
785  ((*_T_soln)(node_in_j) - (*_T_soln)(node_in_i)) / dist_ij;
786  }
787  }
788 
789  // compute the axial heat conduction between current and lower axial node
790  auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz);
791  auto * node_in_j = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
792  auto thcon_i = _fp->k_from_p_T((*_P_soln)(node_in_i) + _P_out, (*_T_soln)(node_in_i));
793  auto thcon_j = _fp->k_from_p_T((*_P_soln)(node_in_j) + _P_out, (*_T_soln)(node_in_j));
794  auto Si = (*_S_flow_soln)(node_in_i);
795  auto dist_ij = z_grid[iz] - z_grid[iz - 1];
796 
797  e_cond += 0.5 * (thcon_i + thcon_j) * Si * ((*_T_soln)(node_in_j) - (*_T_soln)(node_in_i)) /
798  dist_ij;
799 
800  unsigned int nz = _subchannel_mesh.getNumOfAxialCells();
801  // compute the axial heat conduction between current and upper axial node
802  if (iz < nz)
803  {
804  auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz);
805  auto * node_in_j = _subchannel_mesh.getChannelNode(i_ch, iz + 1);
806  auto thcon_i = _fp->k_from_p_T((*_P_soln)(node_in_i) + _P_out, (*_T_soln)(node_in_i));
807  auto thcon_j = _fp->k_from_p_T((*_P_soln)(node_in_j) + _P_out, (*_T_soln)(node_in_j));
808  auto Si = (*_S_flow_soln)(node_in_i);
809  auto dist_ij = z_grid[iz + 1] - z_grid[iz];
810  e_cond += 0.5 * (thcon_i + thcon_j) * Si *
811  ((*_T_soln)(node_in_j) - (*_T_soln)(node_in_i)) / dist_ij;
812  }
813 
814  // end of radial heat conduction calc.
815  h_out =
816  (mdot_in * h_in - sumWijh - sumWijPrimeDhij + added_enthalpy + e_cond + sweep_enthalpy +
817  _TR * _rho_soln->old(node_out) * _h_soln->old(node_out) * volume / _dt) /
818  (mdot_out + _TR * (*_rho_soln)(node_out)*volume / _dt);
819  if (h_out < 0)
820  {
821  mooseError(name(),
822  " : Calculation of negative Enthalpy h_out = : ",
823  h_out,
824  " Axial Level= : ",
825  iz);
826  }
827  _h_soln->set(node_out, h_out); // J/kg
828  }
829  }
830  }
831  else
832  {
833  LibmeshPetscCall(MatZeroEntries(_hc_time_derivative_mat));
834  LibmeshPetscCall(MatZeroEntries(_hc_advective_derivative_mat));
835  LibmeshPetscCall(MatZeroEntries(_hc_cross_derivative_mat));
836  LibmeshPetscCall(MatZeroEntries(_hc_axial_heat_conduction_mat));
837  LibmeshPetscCall(MatZeroEntries(_hc_radial_heat_conduction_mat));
838  LibmeshPetscCall(MatZeroEntries(_hc_sweep_enthalpy_mat));
839 
840  LibmeshPetscCall(VecZeroEntries(_hc_time_derivative_rhs));
841  LibmeshPetscCall(VecZeroEntries(_hc_advective_derivative_rhs));
842  LibmeshPetscCall(VecZeroEntries(_hc_cross_derivative_rhs));
843  LibmeshPetscCall(VecZeroEntries(_hc_added_heat_rhs));
844  LibmeshPetscCall(VecZeroEntries(_hc_axial_heat_conduction_rhs));
845  LibmeshPetscCall(VecZeroEntries(_hc_radial_heat_conduction_rhs));
846  LibmeshPetscCall(VecZeroEntries(_hc_sweep_enthalpy_rhs));
847 
848  LibmeshPetscCall(MatZeroEntries(_hc_sys_h_mat));
849  LibmeshPetscCall(VecZeroEntries(_hc_sys_h_rhs));
850 
851  for (unsigned int iz = first_node; iz < last_node + 1; iz++)
852  {
853  auto dz = _z_grid[iz] - _z_grid[iz - 1];
854  auto heated_length = _subchannel_mesh.getHeatedLength();
855  auto unheated_length_entry = _subchannel_mesh.getHeatedLengthEntry();
857  auto pin_diameter = _subchannel_mesh.getPinDiameter();
858  auto iz_ind = iz - first_node;
859 
860  for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
861  {
862  auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
863  auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
864  auto S_in = (*_S_flow_soln)(node_in);
865  auto S_out = (*_S_flow_soln)(node_out);
866  auto S_interp = computeInterpolatedValue(S_out, S_in, 0.5);
867  auto volume = dz * S_interp;
868 
869  PetscScalar Pe = 0.5;
870  if (_interpolation_scheme == 3)
871  {
872  // Compute the Peclet number
873  auto w_perim_in = (*_w_perim_soln)(node_in);
874  auto w_perim_out = (*_w_perim_soln)(node_out);
875  auto w_perim_interp = this->computeInterpolatedValue(w_perim_out, w_perim_in, 0.5);
876  auto K_in = _fp->k_from_p_T((*_P_soln)(node_in) + _P_out, (*_T_soln)(node_in));
877  auto K_out = _fp->k_from_p_T((*_P_soln)(node_out) + _P_out, (*_T_soln)(node_out));
878  auto K = this->computeInterpolatedValue(K_out, K_in, 0.5);
879  auto cp_in = _fp->cp_from_p_T((*_P_soln)(node_in) + _P_out, (*_T_soln)(node_in));
880  auto cp_out = _fp->cp_from_p_T((*_P_soln)(node_out) + _P_out, (*_T_soln)(node_out));
881  auto cp = this->computeInterpolatedValue(cp_out, cp_in, 0.5);
882  auto mdot_loc =
883  this->computeInterpolatedValue((*_mdot_soln)(node_out), (*_mdot_soln)(node_in), 0.5);
884  // hydraulic diameter in the i direction
885  auto Dh_i = 4.0 * S_interp / w_perim_interp;
886  Pe = mdot_loc * Dh_i * cp / (K * S_interp) * (mdot_loc / std::abs(mdot_loc));
887  }
889 
891  if (iz == first_node)
892  {
893  PetscScalar value_vec_tt =
894  -1.0 * _TR * alpha * (*_rho_soln)(node_in) * (*_h_soln)(node_in)*volume / _dt;
895  PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
896  LibmeshPetscCall(
897  VecSetValues(_hc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
898  }
899  else
900  {
901  PetscInt row_tt = i_ch + _n_channels * iz_ind;
902  PetscInt col_tt = i_ch + _n_channels * (iz_ind - 1);
903  PetscScalar value_tt = _TR * alpha * (*_rho_soln)(node_in)*volume / _dt;
904  LibmeshPetscCall(MatSetValues(
905  _hc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
906  }
907 
908  // Adding diagonal elements
909  PetscInt row_tt = i_ch + _n_channels * iz_ind;
910  PetscInt col_tt = i_ch + _n_channels * iz_ind;
911  PetscScalar value_tt = _TR * (1.0 - alpha) * (*_rho_soln)(node_out)*volume / _dt;
912  LibmeshPetscCall(MatSetValues(
913  _hc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
914 
915  // Adding RHS elements
916  PetscScalar rho_old_interp =
917  computeInterpolatedValue(_rho_soln->old(node_out), _rho_soln->old(node_in), Pe);
918  PetscScalar h_old_interp =
919  computeInterpolatedValue(_h_soln->old(node_out), _h_soln->old(node_in), Pe);
920  PetscScalar value_vec_tt = _TR * rho_old_interp * h_old_interp * volume / _dt;
921  PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
922  LibmeshPetscCall(
923  VecSetValues(_hc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
924 
926  if (iz == first_node)
927  {
928  PetscInt row_at = i_ch + _n_channels * iz_ind;
929  PetscScalar value_at = alpha * (*_mdot_soln)(node_in) * (*_h_soln)(node_in);
930  LibmeshPetscCall(
931  VecSetValues(_hc_advective_derivative_rhs, 1, &row_at, &value_at, ADD_VALUES));
932 
933  value_at = alpha * (*_mdot_soln)(node_out) - (1 - alpha) * (*_mdot_soln)(node_in);
934  PetscInt col_at = i_ch + _n_channels * iz_ind;
935  LibmeshPetscCall(MatSetValues(
936  _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, ADD_VALUES));
937 
938  value_at = (1 - alpha) * (*_mdot_soln)(node_out);
939  col_at = i_ch + _n_channels * (iz_ind + 1);
940  LibmeshPetscCall(MatSetValues(
941  _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, ADD_VALUES));
942  }
943  else if (iz == last_node)
944  {
945  PetscInt row_at = i_ch + _n_channels * iz_ind;
946  PetscScalar value_at = 1.0 * (*_mdot_soln)(node_out);
947  PetscInt col_at = i_ch + _n_channels * iz_ind;
948  LibmeshPetscCall(MatSetValues(
949  _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, ADD_VALUES));
950 
951  value_at = -1.0 * (*_mdot_soln)(node_in);
952  col_at = i_ch + _n_channels * (iz_ind - 1);
953  LibmeshPetscCall(MatSetValues(
954  _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, ADD_VALUES));
955  }
956  else
957  {
958  PetscInt row_at = i_ch + _n_channels * iz_ind;
959  PetscInt col_at;
960 
961  PetscScalar value_at = -alpha * (*_mdot_soln)(node_in);
962  col_at = i_ch + _n_channels * (iz_ind - 1);
963  LibmeshPetscCall(MatSetValues(
964  _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, ADD_VALUES));
965 
966  value_at = alpha * (*_mdot_soln)(node_out) - (1 - alpha) * (*_mdot_soln)(node_in);
967  col_at = i_ch + _n_channels * iz_ind;
968  LibmeshPetscCall(MatSetValues(
969  _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, ADD_VALUES));
970 
971  value_at = (1 - alpha) * (*_mdot_soln)(node_out);
972  col_at = i_ch + _n_channels * (iz_ind + 1);
973  LibmeshPetscCall(MatSetValues(
974  _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, ADD_VALUES));
975  }
976 
978  auto * node_center = _subchannel_mesh.getChannelNode(i_ch, iz);
979  auto K_center = _fp->k_from_p_T((*_P_soln)(node_center) + _P_out, (*_T_soln)(node_center));
980  auto cp_center =
981  _fp->cp_from_p_T((*_P_soln)(node_center) + _P_out, (*_T_soln)(node_center));
982  auto diff_center = K_center / (cp_center + 1e-15);
983 
984  if (iz == first_node)
985  {
986  auto * node_top = _subchannel_mesh.getChannelNode(i_ch, iz + 1);
987  auto * node_bottom = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
988  auto K_bottom =
989  _fp->k_from_p_T((*_P_soln)(node_bottom) + _P_out, (*_T_soln)(node_bottom));
990  auto K_top = _fp->k_from_p_T((*_P_soln)(node_top) + _P_out, (*_T_soln)(node_top));
991  auto cp_bottom =
992  _fp->cp_from_p_T((*_P_soln)(node_bottom) + _P_out, (*_T_soln)(node_bottom));
993  auto cp_top = _fp->cp_from_p_T((*_P_soln)(node_top) + _P_out, (*_T_soln)(node_top));
994  auto diff_bottom = K_bottom / (cp_bottom + 1e-15);
995  auto diff_top = K_top / (cp_top + 1e-15);
996 
997  auto dz_up = _z_grid[iz + 1] - _z_grid[iz];
998  auto dz_down = _z_grid[iz] - _z_grid[iz - 1];
999  auto S_up =
1000  computeInterpolatedValue((*_S_flow_soln)(node_top), (*_S_flow_soln)(node_center));
1001  auto S_down =
1002  computeInterpolatedValue((*_S_flow_soln)(node_center), (*_S_flow_soln)(node_bottom));
1003  auto diff_up = computeInterpolatedValue(diff_top, diff_center);
1004  auto diff_down = computeInterpolatedValue(diff_center, diff_bottom);
1005 
1006  // Diagonal value
1007  PetscInt row_at = i_ch + _n_channels * iz_ind;
1008  PetscInt col_at = i_ch + _n_channels * iz_ind;
1009  PetscScalar value_at = diff_up * S_up / dz_up + diff_down * S_down / dz_down;
1010  LibmeshPetscCall(MatSetValues(
1011  _hc_axial_heat_conduction_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
1012 
1013  // Bottom value
1014  value_at = 1.0 * diff_down * S_down / dz_down * (*_h_soln)(node_bottom);
1015  LibmeshPetscCall(
1016  VecSetValues(_hc_axial_heat_conduction_rhs, 1, &row_at, &value_at, ADD_VALUES));
1017 
1018  // Top value
1019  col_at = i_ch + _n_channels * (iz_ind + 1);
1020  value_at = -diff_up * S_up / dz_up;
1021  LibmeshPetscCall(MatSetValues(
1022  _hc_axial_heat_conduction_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
1023  }
1024  else if (iz == last_node)
1025  {
1026  auto * node_bottom = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1027  auto K_bottom =
1028  _fp->k_from_p_T((*_P_soln)(node_bottom) + _P_out, (*_T_soln)(node_bottom));
1029  auto cp_bottom =
1030  _fp->cp_from_p_T((*_P_soln)(node_bottom) + _P_out, (*_T_soln)(node_bottom));
1031  auto diff_bottom = K_bottom / (cp_bottom + 1e-15);
1032 
1033  auto dz_down = _z_grid[iz] - _z_grid[iz - 1];
1034  auto S_down = 0.5 * ((*_S_flow_soln)(node_center) + (*_S_flow_soln)(node_bottom));
1035  auto diff_down = 0.5 * (diff_center + diff_bottom);
1036 
1037  // Diagonal value
1038  PetscInt row_at = i_ch + _n_channels * iz_ind;
1039  PetscInt col_at = i_ch + _n_channels * iz_ind;
1040  PetscScalar value_at = diff_down * S_down / dz_down;
1041  LibmeshPetscCall(MatSetValues(
1042  _hc_axial_heat_conduction_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
1043 
1044  // Bottom value
1045  col_at = i_ch + _n_channels * (iz_ind - 1);
1046  value_at = -diff_down * S_down / dz_down;
1047  LibmeshPetscCall(MatSetValues(
1048  _hc_axial_heat_conduction_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
1049 
1050  // Outflow derivative
1052  // value_at = -1.0 * (*_mdot_soln)(node_center) * (*_h_soln)(node_center);
1053  // VecSetValues(_hc_axial_heat_conduction_rhs, 1, &row_at, &value_at, ADD_VALUES);
1054  }
1055  else
1056  {
1057  auto * node_top = _subchannel_mesh.getChannelNode(i_ch, iz + 1);
1058  auto * node_bottom = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1059  auto K_bottom =
1060  _fp->k_from_p_T((*_P_soln)(node_bottom) + _P_out, (*_T_soln)(node_bottom));
1061  auto K_top = _fp->k_from_p_T((*_P_soln)(node_top) + _P_out, (*_T_soln)(node_top));
1062  auto cp_bottom =
1063  _fp->cp_from_p_T((*_P_soln)(node_bottom) + _P_out, (*_T_soln)(node_bottom));
1064  auto cp_top = _fp->cp_from_p_T((*_P_soln)(node_top) + _P_out, (*_T_soln)(node_top));
1065  auto diff_bottom = K_bottom / (cp_bottom + 1e-15);
1066  auto diff_top = K_top / (cp_top + 1e-15);
1067 
1068  auto dz_up = _z_grid[iz + 1] - _z_grid[iz];
1069  auto dz_down = _z_grid[iz] - _z_grid[iz - 1];
1070  auto S_up =
1071  computeInterpolatedValue((*_S_flow_soln)(node_top), (*_S_flow_soln)(node_center));
1072  auto S_down =
1073  computeInterpolatedValue((*_S_flow_soln)(node_center), (*_S_flow_soln)(node_bottom));
1074  auto diff_up = computeInterpolatedValue(diff_top, diff_center);
1075  auto diff_down = computeInterpolatedValue(diff_center, diff_bottom);
1076 
1077  // Diagonal value
1078  PetscInt row_at = i_ch + _n_channels * iz_ind;
1079  PetscInt col_at = i_ch + _n_channels * iz_ind;
1080  PetscScalar value_at = diff_up * S_up / dz_up + diff_down * S_down / dz_down;
1081  LibmeshPetscCall(MatSetValues(
1082  _hc_axial_heat_conduction_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
1083 
1084  // Bottom value
1085  col_at = i_ch + _n_channels * (iz_ind - 1);
1086  value_at = -diff_down * S_down / dz_down;
1087  LibmeshPetscCall(MatSetValues(
1088  _hc_axial_heat_conduction_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
1089 
1090  // Top value
1091  col_at = i_ch + _n_channels * (iz_ind + 1);
1092  value_at = -diff_up * S_up / dz_up;
1093  LibmeshPetscCall(MatSetValues(
1094  _hc_axial_heat_conduction_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
1095  }
1096 
1098  unsigned int counter = 0;
1099  unsigned int cross_index = iz;
1100  // Real radial_heat_conduction(0.0);
1101  for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
1102  {
1103  auto chans = _subchannel_mesh.getGapChannels(i_gap);
1104  unsigned int ii_ch = chans.first;
1105  unsigned int jj_ch = chans.second;
1106  auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
1107  auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
1108  PetscScalar h_star;
1109  // figure out donor axial velocity
1110  if (_Wij(i_gap, cross_index) > 0.0)
1111  {
1112  if (iz == first_node)
1113  {
1114  h_star = (*_h_soln)(node_in_i);
1115  PetscScalar value_vec_ct = -1.0 * alpha *
1116  _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1117  _Wij(i_gap, cross_index) * h_star;
1118  PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
1119  LibmeshPetscCall(VecSetValues(
1120  _hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
1121  }
1122  else
1123  {
1124  PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1125  _Wij(i_gap, cross_index);
1126  PetscInt row_ct = i_ch + _n_channels * iz_ind;
1127  PetscInt col_ct = ii_ch + _n_channels * (iz_ind - 1);
1128  LibmeshPetscCall(MatSetValues(
1129  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1130  }
1131  PetscScalar value_ct = (1.0 - alpha) *
1132  _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1133  _Wij(i_gap, cross_index);
1134  PetscInt row_ct = i_ch + _n_channels * iz_ind;
1135  PetscInt col_ct = ii_ch + _n_channels * iz_ind;
1136  LibmeshPetscCall(MatSetValues(
1137  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1138  }
1139  else if (_Wij(i_gap, cross_index) < 0.0) // _Wij=0 operations not necessary
1140  {
1141  if (iz == first_node)
1142  {
1143  h_star = (*_h_soln)(node_in_j);
1144  PetscScalar value_vec_ct = -1.0 * alpha *
1145  _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1146  _Wij(i_gap, cross_index) * h_star;
1147  PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
1148  LibmeshPetscCall(VecSetValues(
1149  _hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
1150  }
1151  else
1152  {
1153  PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1154  _Wij(i_gap, cross_index);
1155  PetscInt row_ct = i_ch + _n_channels * iz_ind;
1156  PetscInt col_ct = jj_ch + _n_channels * (iz_ind - 1);
1157  LibmeshPetscCall(MatSetValues(
1158  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1159  }
1160  PetscScalar value_ct = (1.0 - alpha) *
1161  _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1162  _Wij(i_gap, cross_index);
1163  PetscInt row_ct = i_ch + _n_channels * iz_ind;
1164  PetscInt col_ct = jj_ch + _n_channels * iz_ind;
1165  LibmeshPetscCall(MatSetValues(
1166  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1167  }
1168 
1169  // Turbulent cross flows
1170  if (iz == first_node)
1171  {
1172  PetscScalar value_vec_ct =
1173  -2.0 * alpha * (*_h_soln)(node_in)*_WijPrime(i_gap, cross_index);
1174  value_vec_ct += alpha * (*_h_soln)(node_in_j)*_WijPrime(i_gap, cross_index);
1175  value_vec_ct += alpha * (*_h_soln)(node_in_i)*_WijPrime(i_gap, cross_index);
1176  PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
1177  LibmeshPetscCall(
1178  VecSetValues(_hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
1179  }
1180  else
1181  {
1182  PetscScalar value_center_ct = 2.0 * alpha * _WijPrime(i_gap, cross_index);
1183  PetscInt row_ct = i_ch + _n_channels * iz_ind;
1184  PetscInt col_ct = i_ch + _n_channels * (iz_ind - 1);
1185  LibmeshPetscCall(MatSetValues(
1186  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
1187 
1188  PetscScalar value_left_ct = -1.0 * alpha * _WijPrime(i_gap, cross_index);
1189  row_ct = i_ch + _n_channels * iz_ind;
1190  col_ct = jj_ch + _n_channels * (iz_ind - 1);
1191  LibmeshPetscCall(MatSetValues(
1192  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
1193 
1194  PetscScalar value_right_ct = -1.0 * alpha * _WijPrime(i_gap, cross_index);
1195  row_ct = i_ch + _n_channels * iz_ind;
1196  col_ct = ii_ch + _n_channels * (iz_ind - 1);
1197  LibmeshPetscCall(MatSetValues(
1198  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
1199  }
1200  PetscScalar value_center_ct = 2.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
1201  PetscInt row_ct = i_ch + _n_channels * iz_ind;
1202  PetscInt col_ct = i_ch + _n_channels * iz_ind;
1203  LibmeshPetscCall(MatSetValues(
1204  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
1205 
1206  PetscScalar value_left_ct = -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
1207  row_ct = i_ch + _n_channels * iz_ind;
1208  col_ct = jj_ch + _n_channels * iz_ind;
1209  LibmeshPetscCall(MatSetValues(
1210  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
1211 
1212  PetscScalar value_right_ct = -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
1213  row_ct = i_ch + _n_channels * iz_ind;
1214  col_ct = ii_ch + _n_channels * iz_ind;
1215  LibmeshPetscCall(MatSetValues(
1216  _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
1217 
1219  auto subch_type_i = _subchannel_mesh.getSubchannelType(ii_ch);
1220  auto subch_type_j = _subchannel_mesh.getSubchannelType(jj_ch);
1221  Real dist_ij = pitch;
1222 
1223  if (subch_type_i == EChannelType::EDGE && subch_type_j == EChannelType::EDGE)
1224  {
1225  dist_ij = pitch;
1226  }
1227  else if ((subch_type_i == EChannelType::CORNER && subch_type_j == EChannelType::EDGE) ||
1228  (subch_type_i == EChannelType::EDGE && subch_type_j == EChannelType::CORNER))
1229  {
1230  dist_ij = pitch;
1231  }
1232  else
1233  {
1234  dist_ij = pitch / std::sqrt(3);
1235  }
1236 
1237  auto Sij = dz * _subchannel_mesh.getGapWidth(iz, i_gap);
1238  auto K_i = _fp->k_from_p_T((*_P_soln)(node_in_i) + _P_out, (*_T_soln)(node_in_i));
1239  auto K_j = _fp->k_from_p_T((*_P_soln)(node_in_j) + _P_out, (*_T_soln)(node_in_j));
1240  auto cp_i = _fp->cp_from_p_T((*_P_soln)(node_in_i) + _P_out, (*_T_soln)(node_in_i));
1241  auto cp_j = _fp->cp_from_p_T((*_P_soln)(node_in_j) + _P_out, (*_T_soln)(node_in_j));
1242  auto A_i = K_i / cp_i;
1243  auto A_j = K_j / cp_j;
1244  auto harm_A = 2.0 * A_i * A_j / (A_i + A_j);
1245  auto shape_factor =
1246  0.66 * (pitch / pin_diameter) *
1247  std::pow((_subchannel_mesh.getGapWidth(iz, i_gap) / pin_diameter), -0.3);
1248  // auto base_value = 0.5 * (A_i + A_j) * Sij * shape_factor / dist_ij;
1249  auto base_value = harm_A * shape_factor * Sij / dist_ij;
1250  auto neg_base_value = -1.0 * base_value;
1251 
1252  row_ct = ii_ch + _n_channels * iz_ind;
1253  col_ct = ii_ch + _n_channels * iz_ind;
1254  LibmeshPetscCall(MatSetValues(
1255  _hc_radial_heat_conduction_mat, 1, &row_ct, 1, &col_ct, &base_value, ADD_VALUES));
1256 
1257  row_ct = jj_ch + _n_channels * iz_ind;
1258  col_ct = jj_ch + _n_channels * iz_ind;
1259  LibmeshPetscCall(MatSetValues(
1260  _hc_radial_heat_conduction_mat, 1, &row_ct, 1, &col_ct, &base_value, ADD_VALUES));
1261 
1262  row_ct = ii_ch + _n_channels * iz_ind;
1263  col_ct = jj_ch + _n_channels * iz_ind;
1264  LibmeshPetscCall(MatSetValues(
1265  _hc_radial_heat_conduction_mat, 1, &row_ct, 1, &col_ct, &neg_base_value, ADD_VALUES));
1266 
1267  row_ct = jj_ch + _n_channels * iz_ind;
1268  col_ct = ii_ch + _n_channels * iz_ind;
1269  LibmeshPetscCall(MatSetValues(
1270  _hc_radial_heat_conduction_mat, 1, &row_ct, 1, &col_ct, &neg_base_value, ADD_VALUES));
1271  counter++;
1272  }
1273 
1274  // compute the sweep flow enthalpy change
1275  Real gedge_ave = 0.0;
1276  Real mdot_sum = 0.0;
1277  Real si_sum = 0.0;
1278  for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1279  {
1280  auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
1281  if (subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER)
1282  {
1283  auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1284  auto Si = (*_S_flow_soln)(node_in);
1285  auto mdot_in = (*_mdot_soln)(node_in);
1286  mdot_sum = mdot_sum + mdot_in;
1287  si_sum = si_sum + Si;
1288  }
1289  }
1290  gedge_ave = mdot_sum / si_sum;
1291  auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
1292  PetscScalar sweep_enthalpy = 0.0;
1293  if ((subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER) &&
1294  (wire_diameter != 0.0) && (wire_lead_length != 0.0))
1295  {
1296  const Real & pitch = _subchannel_mesh.getPitch();
1297  const Real & pin_diameter = _subchannel_mesh.getPinDiameter();
1298  const Real & wire_lead_length = _tri_sch_mesh.getWireLeadLength();
1299  const Real & wire_diameter = _tri_sch_mesh.getWireDiameter();
1300  auto gap = _tri_sch_mesh.getDuctToPinGap();
1301  auto w = pin_diameter + gap;
1302  auto theta =
1303  std::acos(wire_lead_length /
1304  std::sqrt(std::pow(wire_lead_length, 2) +
1305  std::pow(libMesh::pi * (pin_diameter + wire_diameter), 2)));
1306  auto Sij = dz * gap;
1307  auto Si = (*_S_flow_soln)(node_in);
1308  // in/out channels for i_ch
1309  auto sweep_in = _tri_sch_mesh.getSweepFlowChans(i_ch).first;
1310  auto * node_sin = _subchannel_mesh.getChannelNode(sweep_in, iz - 1);
1311 
1312  // Calculation of flow regime
1313  auto ReL = 320.0 * std::pow(10.0, pitch / pin_diameter - 1);
1314  auto ReT = 10000.0 * std::pow(10.0, 0.7 * (pitch / pin_diameter - 1));
1315  auto massflux = (*_mdot_soln)(node_in) / Si;
1316  auto w_perim = (*_w_perim_soln)(node_in);
1317  auto mu = (*_mu_soln)(node_in);
1318  // hydraulic diameter
1319  auto hD = 4.0 * Si / w_perim;
1320  auto Re = massflux * hD / mu;
1321  // Calculation of geometric parameters
1322  auto Ar2 = libMesh::pi * (pin_diameter + wire_diameter) * wire_diameter / 4.0;
1323  auto A2prime =
1324  pitch * (w - pin_diameter / 2.0) - libMesh::pi * std::pow(pin_diameter, 2) / 8.0;
1325  auto A2 = A2prime - libMesh::pi * std::pow(wire_diameter, 2) / 8.0 / std::cos(theta);
1326  auto Cs = 0.0;
1327  if (Re < ReL)
1328  {
1329  Cs = 0.033 * std::pow(wire_lead_length / pin_diameter, 0.3);
1330  }
1331  else if (Re > ReT)
1332  {
1333  Cs = 0.75 * std::pow(wire_lead_length / pin_diameter, 0.3);
1334  }
1335  else
1336  {
1337  auto psi = (std::log(Re) - std::log(ReL)) / (std::log(ReT) - std::log(ReL));
1338  auto gamma = 2.0 / 3.0;
1339  Cs = 0.75 * std::pow(wire_lead_length / pin_diameter, 0.3) +
1340  (0.75 * std::pow(wire_lead_length / pin_diameter, 0.3) -
1341  0.033 * std::pow(wire_lead_length / pin_diameter, 0.3)) *
1342  std::pow(psi, gamma);
1343  }
1344  // Calculation of turbulent mixing parameter
1345  auto beta = Cs * std::pow(Ar2 / A2, 0.5) * std::tan(theta);
1346 
1347  auto wsweep_in = gedge_ave * beta * Sij;
1348  auto wsweep_out = gedge_ave * beta * Sij;
1349  auto sweep_hin = (*_h_soln)(node_sin);
1350  auto sweep_hout = (*_h_soln)(node_in);
1351  sweep_enthalpy = (wsweep_in * sweep_hin - wsweep_out * sweep_hout);
1352 
1353  if (iz == first_node)
1354  {
1355  PetscInt row_sh = i_ch + _n_channels * iz_ind;
1356  PetscScalar value_hs = -sweep_enthalpy;
1357  LibmeshPetscCall(
1358  VecSetValues(_hc_sweep_enthalpy_rhs, 1, &row_sh, &value_hs, ADD_VALUES));
1359  }
1360  else
1361  {
1362  PetscInt row_sh = i_ch + _n_channels * (iz_ind - 1);
1363  PetscInt col_sh = i_ch + _n_channels * (iz_ind - 1);
1364  LibmeshPetscCall(MatSetValues(
1365  _hc_sweep_enthalpy_mat, 1, &row_sh, 1, &col_sh, &wsweep_out, ADD_VALUES));
1366  PetscInt col_sh_l = sweep_in + _n_channels * (iz_ind - 1);
1367  PetscScalar neg_sweep_in = -1.0 * wsweep_in;
1368  LibmeshPetscCall(MatSetValues(
1369  _hc_sweep_enthalpy_mat, 1, &row_sh, 1, &col_sh_l, &(neg_sweep_in), ADD_VALUES));
1370  }
1371  }
1372 
1374  PetscScalar added_enthalpy;
1375  if (_z_grid[iz] > unheated_length_entry &&
1376  _z_grid[iz] <= unheated_length_entry + heated_length)
1377  added_enthalpy = computeAddedHeatPin(i_ch, iz);
1378  else
1379  added_enthalpy = 0.0;
1380  added_enthalpy += computeAddedHeatDuct(i_ch, iz);
1381  PetscInt row_vec_ht = i_ch + _n_channels * iz_ind;
1382  LibmeshPetscCall(
1383  VecSetValues(_hc_added_heat_rhs, 1, &row_vec_ht, &added_enthalpy, ADD_VALUES));
1384  }
1385  }
1387  LibmeshPetscCall(MatAssemblyBegin(_hc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1388  LibmeshPetscCall(MatAssemblyEnd(_hc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1389  LibmeshPetscCall(MatAssemblyBegin(_hc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1390  LibmeshPetscCall(MatAssemblyEnd(_hc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1391  LibmeshPetscCall(MatAssemblyBegin(_hc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
1392  LibmeshPetscCall(MatAssemblyEnd(_hc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
1393  LibmeshPetscCall(MatAssemblyBegin(_hc_axial_heat_conduction_mat, MAT_FINAL_ASSEMBLY));
1394  LibmeshPetscCall(MatAssemblyEnd(_hc_axial_heat_conduction_mat, MAT_FINAL_ASSEMBLY));
1395  LibmeshPetscCall(MatAssemblyBegin(_hc_radial_heat_conduction_mat, MAT_FINAL_ASSEMBLY));
1396  LibmeshPetscCall(MatAssemblyEnd(_hc_radial_heat_conduction_mat, MAT_FINAL_ASSEMBLY));
1397  LibmeshPetscCall(MatAssemblyBegin(_hc_sweep_enthalpy_mat, MAT_FINAL_ASSEMBLY));
1398  LibmeshPetscCall(MatAssemblyEnd(_hc_sweep_enthalpy_mat, MAT_FINAL_ASSEMBLY));
1399  LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1400  LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1402  LibmeshPetscCall(
1403  MatAXPY(_hc_sys_h_mat, 1.0, _hc_time_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1404  LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1405  LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1406  LibmeshPetscCall(
1407  MatAXPY(_hc_sys_h_mat, 1.0, _hc_advective_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1408  LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1409  LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1410  LibmeshPetscCall(
1411  MatAXPY(_hc_sys_h_mat, 1.0, _hc_cross_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1412  LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1413  LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1414  LibmeshPetscCall(
1415  MatAXPY(_hc_sys_h_mat, 1.0, _hc_axial_heat_conduction_mat, DIFFERENT_NONZERO_PATTERN));
1416  LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1417  LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1418  LibmeshPetscCall(
1419  MatAXPY(_hc_sys_h_mat, 1.0, _hc_radial_heat_conduction_mat, DIFFERENT_NONZERO_PATTERN));
1420  LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1421  LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1422  LibmeshPetscCall(
1423  MatAXPY(_hc_sys_h_mat, 1.0, _hc_sweep_enthalpy_mat, DIFFERENT_NONZERO_PATTERN));
1424  LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1425  LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1426  if (_verbose_subchannel)
1427  _console << "Block: " << iblock << " - Enthalpy conservation matrix assembled" << std::endl;
1428  // RHS
1429  LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_time_derivative_rhs));
1430  LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_advective_derivative_rhs));
1431  LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_cross_derivative_rhs));
1432  LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_added_heat_rhs));
1433  LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_axial_heat_conduction_rhs));
1434  LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_radial_heat_conduction_rhs));
1435  LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_sweep_enthalpy_rhs));
1436 
1438  {
1439  // Assembly the matrix system
1440  KSP ksploc;
1441  PC pc;
1442  Vec sol;
1443  LibmeshPetscCall(VecDuplicate(_hc_sys_h_rhs, &sol));
1444  LibmeshPetscCall(KSPCreate(PETSC_COMM_WORLD, &ksploc));
1445  LibmeshPetscCall(KSPSetOperators(ksploc, _hc_sys_h_mat, _hc_sys_h_mat));
1446  LibmeshPetscCall(KSPGetPC(ksploc, &pc));
1447  LibmeshPetscCall(PCSetType(pc, PCJACOBI));
1448  LibmeshPetscCall(KSPSetTolerances(ksploc, _rtol, _atol, _dtol, _maxit));
1449  LibmeshPetscCall(KSPSetOptionsPrefix(ksploc, "h_sys_"));
1450  LibmeshPetscCall(KSPSetFromOptions(ksploc));
1451  LibmeshPetscCall(KSPSolve(ksploc, _hc_sys_h_rhs, sol));
1452  // VecView(sol, PETSC_VIEWER_STDOUT_WORLD);
1453  PetscScalar * xx;
1454  LibmeshPetscCall(VecGetArray(sol, &xx));
1455  for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1456  {
1457  auto iz_ind = iz - first_node;
1458  for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1459  {
1460  auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1461  auto h_out = xx[iz_ind * _n_channels + i_ch];
1462  if (h_out < 0)
1463  {
1464  mooseError(name(),
1465  " : Calculation of negative Enthalpy h_out = : ",
1466  h_out,
1467  " Axial Level= : ",
1468  iz);
1469  }
1470  _h_soln->set(node_out, h_out);
1471  }
1472  }
1473  LibmeshPetscCall(KSPDestroy(&ksploc));
1474  LibmeshPetscCall(VecDestroy(&sol));
1475  }
1476  }
1477 }
const bool _pin_mesh_exist
Flag that informs if there is a pin mesh or not.
virtual Real computeAddedHeatPin(unsigned int i_ch, unsigned int iz) override
Computes added heat for channel i_ch and cell iz.
virtual const std::pair< unsigned int, unsigned int > & getGapPins(unsigned int i_gap) const =0
Return a pair of pin indices for a given gap index.
virtual const Real & getPinDiameter() const
Return Pin diameter.
std::unique_ptr< SolutionHandle > _T_soln
std::unique_ptr< SolutionHandle > _h_soln
const PetscReal & _dtol
The divergence tolerance for the ksp linear solver.
const bool _monolithic_thermal_bool
Thermal monolithic bool.
virtual const std::vector< unsigned int > & getPinChannels(unsigned int i_pin) const =0
Return a vector of channel indices for a given Pin index.
T & getMesh(MooseMesh &mesh)
function to cast mesh
Definition: SCM.h:35
Real _TR
Flag that activates or deactivates the transient parts of the equations we solve by multiplication...
virtual const Real & getDuctToPinGap() const
Return the the gap thickness between the duct and peripheral fuel pins.
const PostprocessorValue & _P_out
Outlet Pressure.
virtual Real computeFrictionFactor(FrictionStruct friction_args) override
Computes the axial friction factor for the sodium coolant and for each subchannel.
virtual const std::vector< unsigned int > & getChannelPins(unsigned int i_chan) const =0
Return a vector of pin indices for a given channel index.
virtual const std::vector< Real > & getZGrid() const
Get axial location of layers.
static const std::string K
Definition: NS.h:170
virtual const Real & getPitch() const
Return the pitch between 2 subchannels.
virtual EChannelType getSubchannelType(unsigned int index) const =0
Return the type of the subchannel for given subchannel index.
std::unique_ptr< SolutionHandle > _S_flow_soln
virtual void computeh(int iblock) override
Computes Enthalpy per channel for block iblock.
static InputParameters validParams()
PetscErrorCode createPetscVector(Vec &v, PetscInt n)
Petsc Functions.
int delta(unsigned int i, unsigned int j)
Delta function, which returns zero if $i j$ and unity if $i=j$.
PetscFunctionBegin
static InputParameters validParams()
virtual Node * getPinNode(unsigned int i_pin, unsigned iz) const =0
Get the pin mesh node for a given pin index and elevation index.
PetscScalar computeInterpolatedValue(PetscScalar topValue, PetscScalar botValue, PetscScalar Peclet=0.0)
const bool _segregated_bool
Segregated solve.
virtual const unsigned int & getNumOfRings() const
Return the number of rings.
virtual const std::string & name() const
virtual const std::vector< unsigned int > & getChannelGaps(unsigned int i_chan) const =0
Return a vector of gap indices for a given channel index.
void mooseWarning(Args &&... args) const
Real computeAddedHeatDuct(unsigned int i_ch, unsigned int iz)
Function that computes the heat flux added by the duct.
virtual Node * getChannelNode(unsigned int i_chan, unsigned iz) const =0
Get the subchannel mesh node for a given channel index and elevation index.
std::unique_ptr< SolutionHandle > _rho_soln
static const std::string cp
Definition: NS.h:121
Vec _hc_added_heat_rhs
Enthalpy conservation - source and sink.
std::vector< Real > _z_grid
axial location of nodes
Triangular subchannel solver.
virtual const Real & getHeatedLength() const
Return heated length.
static const std::string S
Definition: NS.h:163
Real f(Real x)
Test function for Brents method.
std::unique_ptr< SolutionHandle > _q_prime_soln
static const std::string mu
Definition: NS.h:123
static const std::string pitch
const PetscReal & _atol
The absolute convergence tolerance for the ksp linear solver.
PetscScalar computeInterpolationCoefficients(PetscScalar Peclet=0.0)
Functions that computes the interpolation scheme given the Peclet number.
std::shared_ptr< AuxiliarySystem > _aux
Mat _hc_cross_derivative_mat
Enthalpy conservation - cross flux derivative.
const PetscInt & _maxit
The maximum number of iterations to use for the ksp linear solver.
PetscErrorCode createPetscMatrix(Mat &M, PetscInt n, PetscInt m)
std::vector< std::vector< Real > > _gij_map
gap size
const MooseEnum _interpolation_scheme
The interpolation method used in constructing the systems.
std::unique_ptr< SolutionHandle > _Dpin_soln
virtual const std::vector< Real > & getReductionBlockage() const
Get area reduction of blocked subchannels.
Real volume(const MeshBase &mesh, unsigned int dim=libMesh::invalid_uint)
Mesh class for triangular, edge and corner subchannels for hexagonal lattice fuel assemblies...
Base class for the 1-phase steady-state/transient subchannel solver.
virtual const Real & getFlatToFlat() const
Return flat to flat [m].
std::unique_ptr< SolutionHandle > _mdot_soln
virtual const unsigned int & getNumOfAxialCells() const
Return the number of axial cells.
virtual Real getGapWidth(unsigned int axial_index, unsigned int gap_index) const =0
Return gap width for a given gap index.
virtual const std::pair< unsigned int, unsigned int > & getSweepFlowChans(unsigned int i_chan) const
static const std::string Z
Definition: NS.h:169
const PetscReal & _rtol
The relative convergence tolerance, (relative decrease) for the ksp linear solver.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual const std::vector< Real > & getZBlockage() const
Get axial location of blockage (in,out) [m].
libMesh::DenseMatrix< Real > & _Wij
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
static const std::string alpha
Definition: NS.h:134
virtual const Real & getCrossflowSign(unsigned int i_chan, unsigned int i_local) const =0
Return a sign for the crossflow given a subchannel index and local neighbor index.
virtual void initializeSolution() override
Function to initialize the solution & geometry fields.
const bool _verbose_subchannel
Boolean to printout information related to subchannel solve.
void mooseError(Args &&... args) const
virtual const Real & getWireLeadLength() const
Return the wire lead length.
void addClassDescription(const std::string &doc_string)
Mat _hc_advective_derivative_mat
Enthalpy conservation - advective (Eulerian) derivative;.
virtual const std::vector< unsigned int > & getIndexBlockage() const
Get index of blocked subchannels.
const bool _implicit_bool
Flag to define the usage of a implicit or explicit solution.
const ConsoleStream _console
registerMooseObject("SubChannelApp", TriSubChannel1PhaseProblem)
Mat _hc_time_derivative_mat
Enthalpy Enthalpy conservation - time derivative.
const bool _deformation
Flag that activates the effect of deformation (pin/duct) based on the auxvalues for displacement...
libMesh::DenseMatrix< Real > _WijPrime
PetscFunctionReturn(LIBMESH_PETSC_SUCCESS)
TriSubChannel1PhaseProblem(const InputParameters &params)
MooseUnits pow(const MooseUnits &, int)
std::unique_ptr< SolutionHandle > _P_soln
static const std::string k
Definition: NS.h:130
std::unique_ptr< SolutionHandle > _w_perim_soln
static const std::string cL
Definition: NS.h:111
virtual Real computeBeta(unsigned int i_gap, unsigned int iz) override
Computes turbulent mixing coefficient.
Definition: SCM.h:16
virtual const Real & getHeatedLengthEntry() const
Return unheated length at entry.
const SinglePhaseFluidProperties * _fp
Solutions handles and link to TH tables properties.
const Real pi
Mat _hc_sys_h_mat
System matrices.
std::unique_ptr< SolutionHandle > _mu_soln
virtual const std::pair< unsigned int, unsigned int > & getGapChannels(unsigned int i_gap) const =0
Return a pair of subchannel indices for a given gap index.
virtual const Real & getWireDiameter() const
Return wire diameter.