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