https://mooseframework.inl.gov
Loading...
Searching...
No Matches
QuadSubChannel1PhaseProblem.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 "SCM.h"
14
16
19{
22 "Solver class for subchannels in a square lattice assembly and bare fuel pins");
23 return params;
24}
25
30
31void
33{
35
37 if (_deformation)
38 {
39 _console << " =========== DEFORMATION RECALCULATION ACTIVATED ============== " << std::endl;
40 Real standard_area, additional_area, wetted_perimeter, displaced_area;
41 auto pitch = _subchannel_mesh.getPitch();
42 auto side_gap = _subchannel_mesh.getSideGap();
43 auto z_blockage = _subchannel_mesh.getZBlockage();
44 auto index_blockage = _subchannel_mesh.getIndexBlockage();
45 auto reduction_blockage = _subchannel_mesh.getReductionBlockage();
46 for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
47 {
48 for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
49 {
50 auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
51 auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
52 auto Z = _z_grid[iz];
53 Real rod_area = 0.0;
54 Real rod_perimeter = 0.0;
55 for (auto i_pin : _subchannel_mesh.getChannelPins(i_ch))
56 {
57 auto * pin_node = _subchannel_mesh.getPinNode(i_pin, iz);
58 rod_area += 0.25 * 0.25 * M_PI * (*_Dpin_soln)(pin_node) * (*_Dpin_soln)(pin_node);
59 rod_perimeter += 0.25 * M_PI * (*_Dpin_soln)(pin_node);
60 }
61
62 if (subch_type == EChannelType::CORNER)
63 {
64 standard_area = 0.25 * pitch * pitch;
65 displaced_area = (2 * side_gap + pitch) * (*_displacement_soln)(node) / sqrt(2) +
66 (*_displacement_soln)(node) * (*_displacement_soln)(node) / 2;
67 additional_area = pitch * side_gap + side_gap * side_gap;
68 wetted_perimeter =
69 rod_perimeter + pitch + 2 * side_gap + 2 * (*_displacement_soln)(node) / sqrt(2);
70 }
71 else if (subch_type == EChannelType::EDGE)
72 {
73 standard_area = 0.5 * pitch * pitch;
74 additional_area = pitch * side_gap;
75 displaced_area = pitch * (*_displacement_soln)(node);
76 wetted_perimeter = rod_perimeter + pitch;
77 }
78 else
79 {
80 standard_area = pitch * pitch;
81 displaced_area = 0.0;
82 additional_area = 0.0;
83 wetted_perimeter = rod_perimeter;
84 }
85
87 auto subchannel_area = displaced_area + standard_area + additional_area - rod_area;
88
90 auto overlapping_pin_area = 0.0;
91 auto overlapping_wetted_perimeter = 0.0;
92 for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
93 {
94 auto gap_pins = _subchannel_mesh.getGapPins(i_gap);
95 auto pin_1 = gap_pins.first;
96 auto pin_2 = gap_pins.second;
97 auto * pin_node_1 = _subchannel_mesh.getPinNode(pin_1, iz);
98 auto * pin_node_2 = _subchannel_mesh.getPinNode(pin_2, iz);
99 auto Diameter1 = (*_Dpin_soln)(pin_node_1);
100 auto Radius1 = Diameter1 / 2.0;
101 auto Diameter2 = (*_Dpin_soln)(pin_node_2);
102 auto Radius2 = Diameter2 / 2.0;
103 auto pitch = _subchannel_mesh.getPitch();
104
105 if (pitch < (Radius1 + Radius2)) // overlapping pins
106 {
107 mooseWarning(" The gap of index : '", i_gap, " at axial cell ", iz, " ' is blocked.");
108 auto cos1 =
109 (pitch * pitch + Radius1 * Radius1 - Radius2 * Radius2) / (2 * pitch * Radius1);
110 auto cos2 =
111 (pitch * pitch + Radius2 * Radius2 - Radius1 * Radius1) / (2 * pitch * Radius2);
112 auto angle1 = 2.0 * acos(cos1);
113 auto angle2 = 2.0 * acos(cos2);
114 // half of the intersecting arc-length
115 overlapping_wetted_perimeter += 0.5 * angle1 * Radius1 + 0.5 * angle2 * Radius2;
116 // Half of the overlapping area
117 overlapping_pin_area +=
118 0.5 * Radius1 * Radius1 * acos(cos1) + 0.5 * Radius2 * Radius2 * acos(cos2) -
119 0.25 * sqrt((-pitch + Radius1 + Radius2) * (pitch + Radius1 - Radius2) *
120 (pitch - Radius1 + Radius2) * (pitch + Radius1 + Radius2));
121 }
122 }
123 subchannel_area += overlapping_pin_area; // correct surface area
124 wetted_perimeter += -overlapping_wetted_perimeter; // correct wetted perimeter
125
127 auto index = 0;
128 for (const auto & i_blockage : index_blockage)
129 {
130 if (i_ch == i_blockage && (Z >= z_blockage.front() && Z <= z_blockage.back()))
131 {
132 subchannel_area *= reduction_blockage[index];
133 }
134 index++;
135 }
136
137 _S_flow_soln->set(node, subchannel_area);
138 _w_perim_soln->set(node, wetted_perimeter);
139 }
140 }
142 for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
143 {
144 for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
145 {
146 auto gap_pins = _subchannel_mesh.getGapPins(i_gap);
147 auto pin_1 = gap_pins.first;
148 auto pin_2 = gap_pins.second;
149 auto * pin_node_1 = _subchannel_mesh.getPinNode(pin_1, iz);
150 auto * pin_node_2 = _subchannel_mesh.getPinNode(pin_2, iz);
151 if (pin_1 == pin_2) // Corner or edge gap
152 {
153 auto displacement = 0.0;
154 auto counter = 0.0;
155 for (auto i_ch : _subchannel_mesh.getPinChannels(pin_1))
156 {
157 auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
158 auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
159 if (subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER)
160 {
161 displacement += (*_displacement_soln)(node);
162 counter += 1.0;
163 }
164 }
165 displacement = displacement / counter;
167 iz, i_gap, (pitch - (*_Dpin_soln)(pin_node_1)) / 2.0 + side_gap + displacement);
168 }
169 else // center gap
170 {
172 iz, i_gap, pitch - (*_Dpin_soln)(pin_node_1) / 2.0 - (*_Dpin_soln)(pin_node_2) / 2.0);
173 }
174 // if pins come in contact, the gap is zero
175 if (_subchannel_mesh.getGapWidth(iz, i_gap) <= 0.0)
176 _subchannel_mesh.setGapWidth(iz, i_gap, 0.0);
177 }
178 }
179 }
180
181 for (unsigned int iz = 1; iz < _n_cells + 1; iz++)
182 {
183 for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
184 {
185 auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
186 auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
187 _mdot_soln->set(node_out, (*_mdot_soln)(node_in));
188 }
189 }
190
191 // We must do a global assembly to make sure data is parallel consistent before we do things
192 // like compute L2 norms
193 _aux->solution().close();
194}
195
196Real
197QuadSubChannel1PhaseProblem::computeAddedHeatPin(unsigned int i_ch, unsigned int iz) const
198{
199 if (!_pin_mesh_exist)
200 return 0.0;
201
202 // Compute axial location of nodes.
203 auto z2 = _z_grid[iz];
204 auto z1 = _z_grid[iz - 1];
205 auto heated_length = _subchannel_mesh.getHeatedLength();
206 auto unheated_length_entry = _subchannel_mesh.getHeatedLengthEntry();
207 if (MooseUtils::absoluteFuzzyGreaterThan(z2, unheated_length_entry) &&
208 MooseUtils::absoluteFuzzyLessThan(z1, unheated_length_entry + heated_length))
209 {
210 // Compute the height of this element.
211 auto dz = z2 - z1;
212 auto heat_rate_in = 0.0;
213 auto heat_rate_out = 0.0;
214 for (auto i_pin : _subchannel_mesh.getChannelPins(i_ch))
215 {
216 auto * node_in = _subchannel_mesh.getPinNode(i_pin, iz - 1);
217 auto * node_out = _subchannel_mesh.getPinNode(i_pin, iz);
218 heat_rate_out += 0.25 * (*_q_prime_soln)(node_out);
219 heat_rate_in += 0.25 * (*_q_prime_soln)(node_in);
220 }
221 return (heat_rate_in + heat_rate_out) * dz / 2.0;
222 }
223 else
224 return 0.0;
225}
226
227Real
229{
230 auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
231 if (subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER)
232 {
233 auto width = _subchannel_mesh.getPitch();
234 if (subch_type == EChannelType::CORNER)
235 width += 2.0 * _subchannel_mesh.getSideGap();
236 return width;
237 }
238 else
239 mooseError("Channel is not a perimetric subchannel ");
240}
241
242void
244{
245 unsigned int last_node = (iblock + 1) * _block_size;
246 unsigned int first_node = iblock * _block_size + 1;
247 if (iblock == 0)
248 {
249 for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
250 {
251 auto * node = _subchannel_mesh.getChannelNode(i_ch, 0);
252 auto h_out = _fp->h_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
253 if (h_out < 0)
254 {
256 name(), " : Calculation of negative Enthalpy h_out = : ", h_out, " Axial Level= : ", 0);
257 }
258 _h_soln->set(node, h_out);
259 }
260 }
261
262 if (!_implicit_bool)
263 {
264 for (unsigned int iz = first_node; iz < last_node + 1; iz++)
265 {
266 auto dz = _z_grid[iz] - _z_grid[iz - 1];
267 for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
268 {
269 auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
270 auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
271 auto mdot_in = (*_mdot_soln)(node_in);
272 auto h_in = (*_h_soln)(node_in); // J/kg
273 auto volume = dz * (*_S_flow_soln)(node_in);
274 auto mdot_out = (*_mdot_soln)(node_out);
275 auto h_out = 0.0;
276 Real sumWijh = 0.0;
277 Real sumWijPrimeDhij = 0.0;
278 Real added_enthalpy = computeAddedHeatPin(i_ch, iz);
279 // Calculate sum of crossflow into channel i from channels j around i
280 unsigned int counter = 0;
281 for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
282 {
283 auto chans = _subchannel_mesh.getGapChannels(i_gap);
284 unsigned int ii_ch = chans.first;
285 // i is always the smallest and first index in the mapping
286 unsigned int jj_ch = chans.second;
287 auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
288 auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
289 // Define donor enthalpy
290 Real h_star = 0.0;
291 if (_Wij(i_gap, iz) > 0.0)
292 h_star = (*_h_soln)(node_in_i);
293 else if (_Wij(i_gap, iz) < 0.0)
294 h_star = (*_h_soln)(node_in_j);
295 // take care of the sign by applying the map, use donor cell
296 sumWijh += _subchannel_mesh.getCrossflowSign(i_ch, counter) * _Wij(i_gap, iz) * h_star;
297 sumWijPrimeDhij += _WijPrime(i_gap, iz) * (2 * (*_h_soln)(node_in) -
298 (*_h_soln)(node_in_j) - (*_h_soln)(node_in_i));
299 counter++;
300 }
301 h_out = (mdot_in * h_in - sumWijh - sumWijPrimeDhij + added_enthalpy +
302 _TR * _rho_soln->old(node_out) * _h_soln->old(node_out) * volume / _dt) /
303 (mdot_out + _TR * (*_rho_soln)(node_out)*volume / _dt);
304 if (h_out < 0)
305 {
307 " : Calculation of negative Enthalpy h_out = : ",
308 h_out,
309 " Axial Level= : ",
310 iz);
311 }
312 _h_soln->set(node_out, h_out); // J/kg
313 }
314 }
315 }
316 else
317 {
318 LibmeshPetscCall(MatZeroEntries(_hc_time_derivative_mat));
319 LibmeshPetscCall(MatZeroEntries(_hc_advective_derivative_mat));
320 LibmeshPetscCall(MatZeroEntries(_hc_cross_derivative_mat));
321 LibmeshPetscCall(VecZeroEntries(_hc_time_derivative_rhs));
322 LibmeshPetscCall(VecZeroEntries(_hc_advective_derivative_rhs));
323 LibmeshPetscCall(VecZeroEntries(_hc_cross_derivative_rhs));
324 LibmeshPetscCall(VecZeroEntries(_hc_added_heat_rhs));
325 LibmeshPetscCall(MatZeroEntries(_hc_sys_h_mat));
326 LibmeshPetscCall(VecZeroEntries(_hc_sys_h_rhs));
327 for (unsigned int iz = first_node; iz < last_node + 1; iz++)
328 {
329 auto dz = _z_grid[iz] - _z_grid[iz - 1];
330 auto iz_ind = iz - first_node;
331 for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
332 {
333 auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
334 auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
335 auto S_in = (*_S_flow_soln)(node_in);
336 auto S_out = (*_S_flow_soln)(node_out);
337 auto S_interp = computeInterpolatedValue(S_out, S_in, 0.5);
338 auto volume = dz * S_interp;
339
340 // interpolation weight coefficient
341 PetscScalar Pe = 0.5;
342 if (_interpolation_scheme == 3)
343 {
344 // Compute the cell Peclet number
345 auto w_perim_in = (*_w_perim_soln)(node_in);
346 auto w_perim_out = (*_w_perim_soln)(node_out);
347 auto w_perim_interp = this->computeInterpolatedValue(w_perim_out, w_perim_in, 0.5);
348 auto K_in = _fp->k_from_p_T((*_P_soln)(node_in) + _P_out, (*_T_soln)(node_in));
349 auto K_out = _fp->k_from_p_T((*_P_soln)(node_out) + _P_out, (*_T_soln)(node_out));
350 auto K = this->computeInterpolatedValue(K_out, K_in, 0.5);
351 auto cp_in = _fp->cp_from_p_T((*_P_soln)(node_in) + _P_out, (*_T_soln)(node_in));
352 auto cp_out = _fp->cp_from_p_T((*_P_soln)(node_out) + _P_out, (*_T_soln)(node_out));
353 auto cp = this->computeInterpolatedValue(cp_out, cp_in, 0.5);
354 auto mdot_loc =
355 this->computeInterpolatedValue((*_mdot_soln)(node_out), (*_mdot_soln)(node_in), 0.5);
356 // hydraulic diameter in the i direction
357 auto Dh_i = 4.0 * S_interp / w_perim_interp;
358 Pe = mdot_loc * Dh_i * cp / (K * S_interp) * (mdot_loc / std::abs(mdot_loc));
359 }
360 auto alpha = computeInterpolationCoefficients(Pe);
361
362 // Keep temporal storage independent of the spatial interpolation scheme. Applying the
363 // upwind coefficient to this term puts the storage on the upstream node and makes the
364 // small-timestep matrix singular in the upwind limit.
365 PetscInt row_tt = i_ch + _n_channels * iz_ind;
366 PetscInt col_tt = i_ch + _n_channels * iz_ind;
367 PetscScalar value_tt = _TR * (*_rho_soln)(node_out)*volume / _dt;
368 LibmeshPetscCall(MatSetValues(
369 _hc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
370
371 PetscScalar value_vec_tt =
372 _TR * _rho_soln->old(node_out) * _h_soln->old(node_out) * volume / _dt;
373 PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
374 LibmeshPetscCall(
375 VecSetValues(_hc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
376
378 if (iz == first_node)
379 {
380 PetscScalar value_vec_at = (*_mdot_soln)(node_in) * (*_h_soln)(node_in);
381 PetscInt row_vec_at = i_ch + _n_channels * iz_ind;
382 LibmeshPetscCall(VecSetValues(
383 _hc_advective_derivative_rhs, 1, &row_vec_at, &value_vec_at, ADD_VALUES));
384 }
385 else
386 {
387 PetscInt row_at = i_ch + _n_channels * iz_ind;
388 PetscInt col_at = i_ch + _n_channels * (iz_ind - 1);
389 PetscScalar value_at = -1.0 * (*_mdot_soln)(node_in);
390 LibmeshPetscCall(MatSetValues(
391 _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
392 }
393
394 // Adding diagonal elements
395 PetscInt row_at = i_ch + _n_channels * iz_ind;
396 PetscInt col_at = i_ch + _n_channels * iz_ind;
397 PetscScalar value_at = (*_mdot_soln)(node_out);
398 LibmeshPetscCall(MatSetValues(
399 _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
400
402 unsigned int counter = 0;
403 unsigned int cross_index = iz; // iz-1;
404 for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
405 {
406 auto chans = _subchannel_mesh.getGapChannels(i_gap);
407 unsigned int ii_ch = chans.first;
408 unsigned int jj_ch = chans.second;
409 auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
410 auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
411 PetscScalar h_star;
412 // figure out donor axial velocity
413 if (_Wij(i_gap, cross_index) > 0.0)
414 {
415 if (iz == first_node)
416 {
417 h_star = (*_h_soln)(node_in_i);
418 PetscScalar value_vec_ct = -1.0 * alpha *
419 _subchannel_mesh.getCrossflowSign(i_ch, counter) *
420 _Wij(i_gap, cross_index) * h_star;
421 PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
422 LibmeshPetscCall(VecSetValues(
423 _hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
424 }
425 else
426 {
427 PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
428 _Wij(i_gap, cross_index);
429 PetscInt row_ct = i_ch + _n_channels * iz_ind;
430 PetscInt col_ct = ii_ch + _n_channels * (iz_ind - 1);
431 LibmeshPetscCall(MatSetValues(
432 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
433 }
434 PetscScalar value_ct = (1.0 - alpha) *
435 _subchannel_mesh.getCrossflowSign(i_ch, counter) *
436 _Wij(i_gap, cross_index);
437 PetscInt row_ct = i_ch + _n_channels * iz_ind;
438 PetscInt col_ct = ii_ch + _n_channels * iz_ind;
439 LibmeshPetscCall(MatSetValues(
440 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
441 }
442 else if (_Wij(i_gap, cross_index) < 0.0) // _Wij=0 operations not necessary
443 {
444 if (iz == first_node)
445 {
446 h_star = (*_h_soln)(node_in_j);
447 PetscScalar value_vec_ct = -1.0 * alpha *
448 _subchannel_mesh.getCrossflowSign(i_ch, counter) *
449 _Wij(i_gap, cross_index) * h_star;
450 PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
451 LibmeshPetscCall(VecSetValues(
452 _hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
453 }
454 else
455 {
456 PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
457 _Wij(i_gap, cross_index);
458 PetscInt row_ct = i_ch + _n_channels * iz_ind;
459 PetscInt col_ct = jj_ch + _n_channels * (iz_ind - 1);
460 LibmeshPetscCall(MatSetValues(
461 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
462 }
463 PetscScalar value_ct = (1.0 - alpha) *
464 _subchannel_mesh.getCrossflowSign(i_ch, counter) *
465 _Wij(i_gap, cross_index);
466 PetscInt row_ct = i_ch + _n_channels * iz_ind;
467 PetscInt col_ct = jj_ch + _n_channels * iz_ind;
468 LibmeshPetscCall(MatSetValues(
469 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
470 }
471
472 // Turbulent cross flows
473 if (iz == first_node)
474 {
475 PetscScalar value_vec_ct =
476 -2.0 * alpha * (*_h_soln)(node_in)*_WijPrime(i_gap, cross_index);
477 value_vec_ct += alpha * (*_h_soln)(node_in_j)*_WijPrime(i_gap, cross_index);
478 value_vec_ct += alpha * (*_h_soln)(node_in_i)*_WijPrime(i_gap, cross_index);
479 PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
480 LibmeshPetscCall(
481 VecSetValues(_hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
482 }
483 else
484 {
485 PetscScalar value_center_ct = 2.0 * alpha * _WijPrime(i_gap, cross_index);
486 PetscInt row_ct = i_ch + _n_channels * iz_ind;
487 PetscInt col_ct = i_ch + _n_channels * (iz_ind - 1);
488 LibmeshPetscCall(MatSetValues(
489 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
490
491 PetscScalar value_left_ct = -1.0 * alpha * _WijPrime(i_gap, cross_index);
492 row_ct = i_ch + _n_channels * iz_ind;
493 col_ct = jj_ch + _n_channels * (iz_ind - 1);
494 LibmeshPetscCall(MatSetValues(
495 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
496
497 PetscScalar value_right_ct = -1.0 * alpha * _WijPrime(i_gap, cross_index);
498 row_ct = i_ch + _n_channels * iz_ind;
499 col_ct = ii_ch + _n_channels * (iz_ind - 1);
500 LibmeshPetscCall(MatSetValues(
501 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
502 }
503 PetscScalar value_center_ct = 2.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
504 PetscInt row_ct = i_ch + _n_channels * iz_ind;
505 PetscInt col_ct = i_ch + _n_channels * iz_ind;
506 LibmeshPetscCall(MatSetValues(
507 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
508
509 PetscScalar value_left_ct = -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
510 row_ct = i_ch + _n_channels * iz_ind;
511 col_ct = jj_ch + _n_channels * iz_ind;
512 LibmeshPetscCall(MatSetValues(
513 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
514
515 PetscScalar value_right_ct = -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
516 row_ct = i_ch + _n_channels * iz_ind;
517 col_ct = ii_ch + _n_channels * iz_ind;
518 LibmeshPetscCall(MatSetValues(
519 _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
520 counter++;
521 }
522
524 PetscScalar added_enthalpy = computeAddedHeatPin(i_ch, iz);
525 PetscInt row_vec_ht = i_ch + _n_channels * iz_ind;
526 LibmeshPetscCall(
527 VecSetValues(_hc_added_heat_rhs, 1, &row_vec_ht, &added_enthalpy, ADD_VALUES));
528 }
529 }
531 LibmeshPetscCall(MatAssemblyBegin(_hc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
532 LibmeshPetscCall(MatAssemblyEnd(_hc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
533 LibmeshPetscCall(MatAssemblyBegin(_hc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
534 LibmeshPetscCall(MatAssemblyEnd(_hc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
535 LibmeshPetscCall(MatAssemblyBegin(_hc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
536 LibmeshPetscCall(MatAssemblyEnd(_hc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
537 LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
538 LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
539 // Matrix
540#if !PETSC_VERSION_LESS_THAN(3, 15, 0)
541 LibmeshPetscCall(MatAXPY(_hc_sys_h_mat, 1.0, _hc_time_derivative_mat, UNKNOWN_NONZERO_PATTERN));
542 LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
543 LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
544 LibmeshPetscCall(
545 MatAXPY(_hc_sys_h_mat, 1.0, _hc_advective_derivative_mat, UNKNOWN_NONZERO_PATTERN));
546 LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
547 LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
548 LibmeshPetscCall(
549 MatAXPY(_hc_sys_h_mat, 1.0, _hc_cross_derivative_mat, UNKNOWN_NONZERO_PATTERN));
550#else
551 LibmeshPetscCall(
552 MatAXPY(_hc_sys_h_mat, 1.0, _hc_time_derivative_mat, DIFFERENT_NONZERO_PATTERN));
553 LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
554 LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
555 LibmeshPetscCall(
556 MatAXPY(_hc_sys_h_mat, 1.0, _hc_advective_derivative_mat, DIFFERENT_NONZERO_PATTERN));
557 LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
558 LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
559 LibmeshPetscCall(
560 MatAXPY(_hc_sys_h_mat, 1.0, _hc_cross_derivative_mat, DIFFERENT_NONZERO_PATTERN));
561#endif
562 LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
563 LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
565 _console << "Block: " << iblock << " - Enthalpy conservation matrix assembled" << std::endl;
566 // RHS
567 LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_time_derivative_rhs));
568 LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_advective_derivative_rhs));
569 LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_cross_derivative_rhs));
570 LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_added_heat_rhs));
571
572 // Use system to solve for and populate enthalpy
573 LibmeshPetscCall(this->solveAndPopulateEnthalpy(
574 _hc_sys_h_mat, _hc_sys_h_rhs, first_node, last_node, "h_sys_"));
575 }
576}
registerMooseObject("SubChannelApp", QuadSubChannel1PhaseProblem)
const ConsoleStream _console
std::shared_ptr< AuxiliarySystem > _aux
void addClassDescription(const std::string &doc_string)
const std::string & name() const
void mooseError(Args &&... args) const
void mooseWarning(Args &&... args) const
Quadrilateral subchannel solver.
virtual void computeh(int iblock) override
Computes Enthalpy per channel for block iblock.
virtual Real getSubChannelPeripheralDuctWidth(unsigned int i_ch) const override
Function that computes the width of the duct cell that the peripheral subchannel i_ch sees.
QuadSubChannel1PhaseProblem(const InputParameters &params)
virtual void initializeSolution() override
Function to initialize the solution & geometry fields.
virtual Real computeAddedHeatPin(unsigned int i_ch, unsigned int iz) const override
Pure virtual: daughters provide different implementations.
Creates the mesh of subchannels in a quadrilateral lattice.
void setGapWidth(unsigned int axial_index, unsigned int gap_index, Real gap_width)
Set the gap width for a given axial cell and gap index.
Real getGapWidth(unsigned int axial_index, unsigned int gap_index) const override
Return gap width for a given gap index.
const std::vector< unsigned int > & getChannelGaps(unsigned int i_chan) const override
Return a vector of gap indices for a given channel index.
const Real & getCrossflowSign(unsigned int i_chan, unsigned int i_local) const override
Return a sign for the crossflow given a subchannel index and local neighbor index.
const std::pair< unsigned int, unsigned int > & getGapPins(unsigned int i_gap) const override
Return a pair of pin indices for a given gap index.
const Real & getPitch() const override
Return the undeformed pitch between 2 subchannels.
const std::vector< unsigned int > & getPinChannels(unsigned int i_pin) const override
Return a vector of channel indices for a given Pin index.
EChannelType getSubchannelType(unsigned int index) const override
Return the type of the subchannel for given subchannel index.
Node * getPinNode(unsigned int i_pin, unsigned int iz) const override
Get the pin mesh node for a given pin index and elevation index.
const Real & getSideGap() const
Returns the side gap, not to be confused with the gap between pins, this refers to the gap next to th...
const std::vector< unsigned int > & getChannelPins(unsigned int i_chan) const override
Return a vector of pin indices for a given channel index.
Node * getChannelNode(unsigned int i_chan, unsigned int iz) const override
Get the subchannel mesh node for a given channel index and elevation index.
const std::pair< unsigned int, unsigned int > & getGapChannels(unsigned int i_gap) const override
Return a pair of subchannel indices for a given gap index.
Base class for the 1-phase steady-state/transient subchannel solver.
PetscScalar computeInterpolatedValue(PetscScalar topValue, PetscScalar botValue, PetscScalar Peclet=0.0)
std::unique_ptr< SolutionHandle > _w_perim_soln
std::unique_ptr< SolutionHandle > _displacement_soln
Mat _hc_time_derivative_mat
Enthalpy Enthalpy conservation - time derivative.
static InputParameters validParams()
PetscErrorCode solveAndPopulateEnthalpy(Mat A, Vec rhs, unsigned int first_node, unsigned int last_node, const char *ksp_prefix)
Solve a linear system (A * x = rhs) with a simple PCJACOBI KSP and populate the enthalpy solution int...
const SinglePhaseFluidProperties * _fp
Non-owning pointer to fluid properties user object.
libMesh::DenseMatrix< Real > _WijPrime
const MooseEnum _interpolation_scheme
The interpolation method used in constructing the systems.
std::unique_ptr< SolutionHandle > _rho_soln
std::unique_ptr< SolutionHandle > _S_flow_soln
const PostprocessorValue & _P_out
Outlet pressure postprocessor value.
std::unique_ptr< SolutionHandle > _Dpin_soln
std::unique_ptr< SolutionHandle > _P_soln
void detectDeformation()
Detects whether pin diameter or duct displacement fields require geometry recalculation.
libMesh::DenseMatrix< Real > & _Wij
bool _deformation
Flag that activates the effect of deformation (pin/duct) based on the auxvalues for displacement,...
std::unique_ptr< SolutionHandle > _h_soln
std::unique_ptr< SolutionHandle > _T_soln
Real _TR
Flag that activates or deactivates the transient parts of the equations we solve by multiplication.
PetscScalar computeInterpolationCoefficients(PetscScalar Peclet=0.0)
Functions that computes the interpolation scheme given the Peclet number.
Vec _hc_added_heat_rhs
Enthalpy conservation - source and sink.
Mat _hc_advective_derivative_mat
Enthalpy conservation - advective (Eulerian) derivative;.
const bool _pin_mesh_exist
Flag that informs if there is a pin mesh or not.
const bool _implicit_bool
Flag to define the usage of a implicit or explicit solution.
std::vector< Real > _z_grid
axial location of nodes
std::unique_ptr< SolutionHandle > _mdot_soln
Solutions handles and link to TH tables properties.
const bool _verbose_subchannel
Boolean to printout information related to subchannel solve.
Mat _hc_cross_derivative_mat
Enthalpy conservation - cross flux derivative.
virtual const Real & getHeatedLength() const
Return heated length.
virtual const std::vector< Real > & getZBlockage() const
Get axial location of blockage (in,out) [m].
virtual const Real & getHeatedLengthEntry() const
Return unheated length at entry.
virtual const std::vector< Real > & getReductionBlockage() const
Get area reduction of blocked subchannels.
virtual const std::vector< unsigned int > & getIndexBlockage() const
Get index of blocked subchannels.
Definition SCM.h:17