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 "SubChannel1PhaseProblem.h"
11 : #include "SystemBase.h"
12 : #include "libmesh/petsc_vector.h"
13 : #include "libmesh/dense_matrix.h"
14 : #include "libmesh/dense_vector.h"
15 : #include <iostream>
16 : #include <cmath>
17 : #include "AuxiliarySystem.h"
18 : #include "SCM.h"
19 : #include "SinglePhaseFluidProperties.h"
20 : #include "SCMFrictionClosureBase.h"
21 : #include "SCMHTCClosureBase.h"
22 : #include "SCMMixingClosureBase.h"
23 : #include "TransientBase.h"
24 : #include "ImplicitEuler.h"
25 :
26 : struct Ctx
27 : {
28 : int iblock;
29 : SubChannel1PhaseProblem * schp;
30 : };
31 :
32 : PetscErrorCode
33 63857 : formFunction(SNES, Vec x, Vec f, void * ctx)
34 : {
35 : const PetscScalar * xx;
36 : PetscScalar * ff;
37 : PetscInt size;
38 :
39 : PetscFunctionBegin;
40 : Ctx * cc = static_cast<Ctx *>(ctx);
41 63857 : LibmeshPetscCallQ(VecGetSize(x, &size));
42 :
43 63857 : libMesh::DenseVector<Real> solution_seed(size, 0.0);
44 63857 : LibmeshPetscCallQ(VecGetArrayRead(x, &xx));
45 36952097 : for (PetscInt i = 0; i < size; i++)
46 36888240 : solution_seed(i) = xx[i];
47 :
48 63857 : LibmeshPetscCallQ(VecRestoreArrayRead(x, &xx));
49 :
50 : libMesh::DenseVector<Real> Wij_residual_vector =
51 63857 : cc->schp->residualFunction(cc->iblock, solution_seed);
52 :
53 63857 : LibmeshPetscCallQ(VecGetArray(f, &ff));
54 36952097 : for (int i = 0; i < size; i++)
55 36888240 : ff[i] = Wij_residual_vector(i);
56 :
57 63857 : LibmeshPetscCallQ(VecRestoreArray(f, &ff));
58 63857 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
59 : }
60 :
61 : InputParameters
62 556 : SubChannel1PhaseProblem::validParams()
63 : {
64 : // Enumerations
65 1112 : MooseEnum schemes("upwind downwind central_difference exponential", "central_difference");
66 1112 : MooseEnum gravity_direction("counter_flow co_flow none", "counter_flow");
67 :
68 : // Inputs
69 556 : InputParameters params = ExternalProblem::validParams();
70 556 : params += PostprocessorInterface::validParams();
71 556 : params.addClassDescription("Base class of the subchannel solvers");
72 1112 : params.addRequiredParam<unsigned int>("n_blocks", "The number of blocks in the axial direction");
73 1112 : params.addParam<Real>("P_tol", 1e-6, "Pressure tolerance");
74 1112 : params.addParam<Real>("T_tol", 1e-6, "Temperature tolerance");
75 1112 : params.addParam<int>("T_maxit", 100, "Maximum number of iterations for inner temperature loop");
76 1112 : params.addParam<PetscReal>("rtol", 1e-6, "Relative tolerance for ksp solver");
77 1112 : params.addParam<PetscReal>("atol", 1e-6, "Absolute tolerance for ksp solver");
78 1112 : params.addParam<PetscReal>("dtol", 1e5, "Divergence tolerance or ksp solver");
79 1112 : params.addParam<PetscInt>("maxit", 1e4, "Maximum number of iterations for ksp solver");
80 1112 : params.addParam<MooseEnum>(
81 : "interpolation_scheme",
82 : schemes,
83 : "Interpolation scheme used for the method. Default is central_difference");
84 1112 : params.addParam<MooseEnum>(
85 : "gravity", gravity_direction, "Direction of gravity. Default is counter_flow");
86 1112 : params.addParam<bool>(
87 1112 : "implicit", false, "Boolean to define the use of explicit or implicit solution.");
88 1112 : params.addParam<bool>("staggered_pressure",
89 1112 : false,
90 : "Boolean to define the use of staggered or collocated pressure.");
91 1112 : params.addParam<bool>(
92 1112 : "segregated", true, "Boolean to define whether to use a segregated solution.");
93 1112 : params.addParam<bool>(
94 1112 : "verbose_subchannel", false, "Boolean to print out information related to subchannel solve.");
95 1112 : params.addRequiredParam<bool>("compute_density", "Flag that enables the calculation of density");
96 1112 : params.addRequiredParam<bool>("compute_viscosity",
97 : "Flag that enables the calculation of viscosity");
98 1112 : params.addRequiredParam<bool>(
99 : "compute_power",
100 : "Flag that informs whether we solve the Enthalpy/Temperature equations or not");
101 1112 : params.addRequiredParam<PostprocessorName>(
102 : "P_out",
103 : "The postprocessor (or scalar) that provides the absolute outlet pressure [Pa]. The solved "
104 : "pressure variable P is relative to this value.");
105 1112 : params.addRequiredParam<UserObjectName>("fp", "Fluid properties user object name");
106 1112 : params.addRequiredParam<UserObjectName>("friction_closure",
107 : "Closure computing the friction factor");
108 1112 : params.addRequiredParam<UserObjectName>(
109 : "mixing_closure",
110 : "Closure computing the turbulent mixing, wire-induced "
111 : "mixing and sweep flow mixing parameter where applicable");
112 1112 : params.addParam<UserObjectName>(
113 : "pin_HTC_closure", "Closure computing HTC on fuel pin (required if pin mesh exists).");
114 1112 : params.addParam<UserObjectName>("duct_HTC_closure",
115 : "Closure computing HTC on duct (required if duct mesh exists).");
116 1112 : params.addParam<bool>(
117 1112 : "full_output", false, "Flag that enables the output of the maximum number of variables.");
118 1112 : params.addDeprecatedParam<Real>("beta",
119 : "Thermal diffusion coefficient used in turbulent crossflow.",
120 : "Use closure system instead.");
121 1668 : params.addDeprecatedParam<bool>(
122 : "constant_beta",
123 1112 : true,
124 : "Boolean to define the use of a constant beta or beta correlation (Kim and Chung, 2001)",
125 : "Use closure system instead.");
126 :
127 1112 : params.addParamNamesToGroup("P_tol T_tol T_maxit rtol atol dtol maxit",
128 : "Solver tolerances and iterations");
129 1112 : params.addParamNamesToGroup("implicit segregated staggered_pressure interpolation_scheme",
130 : "Solution method");
131 1112 : params.addParamNamesToGroup("fp friction_closure mixing_closure pin_HTC_closure duct_HTC_closure",
132 : "Closures");
133 1112 : params.addParamNamesToGroup("compute_density compute_viscosity compute_power gravity",
134 : "Physics models");
135 1112 : params.addParamNamesToGroup("verbose_subchannel full_output", "Output");
136 :
137 556 : return params;
138 556 : }
139 :
140 278 : SubChannel1PhaseProblem::SubChannel1PhaseProblem(const InputParameters & params)
141 : : ExternalProblem(params),
142 : PostprocessorInterface(this),
143 : _friction_args(/*i_ch=*/0, /*Re=*/1.0, /*S=*/0.0, /*w_perim=*/0.0),
144 : _nusselt_args(
145 : /*Re=*/1.0, /*Pr=*/1.0, std::numeric_limits<unsigned int>::max(), /*iz=*/0, /*i_ch=*/0),
146 278 : _P_out(getPostprocessorValue("P_out")),
147 278 : _fp(nullptr),
148 278 : _subchannel_mesh(SCM::getMesh<SubChannelMesh>(_mesh)),
149 556 : _n_blocks(getParam<unsigned int>("n_blocks")),
150 556 : _Wij(declareRestartableData<libMesh::DenseMatrix<Real>>("Wij")),
151 278 : _g_grav(9.81),
152 278 : _kij(_subchannel_mesh.getKij()),
153 278 : _one(1.0),
154 556 : _compute_density(getParam<bool>("compute_density")),
155 556 : _compute_viscosity(getParam<bool>("compute_viscosity")),
156 556 : _compute_power(getParam<bool>("compute_power")),
157 278 : _pin_mesh_exist(_subchannel_mesh.pinMeshExist()),
158 278 : _duct_mesh_exist(_subchannel_mesh.ductMeshExist()),
159 278 : _P_tol(getParam<Real>("P_tol")),
160 556 : _T_tol(getParam<Real>("T_tol")),
161 556 : _T_maxit(getParam<int>("T_maxit")),
162 556 : _rtol(getParam<PetscReal>("rtol")),
163 556 : _atol(getParam<PetscReal>("atol")),
164 556 : _dtol(getParam<PetscReal>("dtol")),
165 556 : _maxit(getParam<PetscInt>("maxit")),
166 556 : _interpolation_scheme(getParam<MooseEnum>("interpolation_scheme")),
167 556 : _gravity_direction(getParam<MooseEnum>("gravity")),
168 278 : _dir_grav(computeGravityDir(_gravity_direction)),
169 556 : _implicit_bool(getParam<bool>("implicit")),
170 556 : _staggered_pressure_bool(getParam<bool>("staggered_pressure")),
171 556 : _segregated_bool(getParam<bool>("segregated")),
172 556 : _verbose_subchannel(getParam<bool>("verbose_subchannel")),
173 : _Tpin_soln(nullptr),
174 : _duct_heat_flux_soln(nullptr),
175 : _Tduct_soln(nullptr),
176 1390 : _HTC_soln(nullptr)
177 : {
178 556 : if (params.isParamSetByUser("beta") || params.isParamSetByUser("constant_beta"))
179 0 : paramError("beta",
180 : "You are using a deprecated parameter. Please use the mixing_closure system.");
181 1085 : if (_pin_mesh_exist && !isParamValid("pin_HTC_closure"))
182 0 : paramError("pin_HTC_closure", "required when a pin mesh exists.");
183 479 : if (_duct_mesh_exist && !isParamValid("duct_HTC_closure"))
184 0 : paramError("duct_HTC_closure", "required when a duct mesh exists.");
185 : // NOTE: The four quantities below are 0 for processor_id != 0
186 278 : _n_cells = _subchannel_mesh.getNumOfAxialCells();
187 278 : _n_gaps = _subchannel_mesh.getNumOfGapsPerLayer();
188 278 : _n_pins = _subchannel_mesh.getNumOfPins();
189 278 : _n_channels = _subchannel_mesh.getNumOfChannels();
190 : // NOTE: The four quantities above are 0 for processor_id != 0
191 278 : _z_grid = _subchannel_mesh.getZGrid();
192 278 : _block_size = _n_cells / _n_blocks;
193 : // Pressure drop (lives on subchannel nodes)
194 278 : _DP.resize(_n_channels, _n_cells + 1);
195 : _DP.zero();
196 : // Turbulent crossflow (stuff that live on the gaps)
197 278 : if (!_app.isRestarting() && !_app.isRecovering())
198 : {
199 267 : _Wij.resize(_n_gaps, _n_cells + 1);
200 267 : _Wij.zero();
201 : }
202 278 : _Wij_old.resize(_n_gaps, _n_cells + 1);
203 : _Wij_old.zero();
204 278 : _WijPrime.resize(_n_gaps, _n_cells + 1);
205 : _WijPrime.zero();
206 278 : _Wij_residual_matrix.resize(_n_gaps, _block_size);
207 : _Wij_residual_matrix.zero();
208 278 : _converged = true;
209 :
210 : // Mass conservation components
211 278 : LibmeshPetscCall(
212 : createPetscMatrix(_mc_sumWij_mat, _block_size * _n_channels, _block_size * _n_gaps));
213 278 : LibmeshPetscCall(createPetscVector(_Wij_vec, _block_size * _n_gaps));
214 278 : LibmeshPetscCall(createPetscVector(_prod, _block_size * _n_channels));
215 278 : LibmeshPetscCall(createPetscVector(_prodp, _block_size * _n_channels));
216 278 : LibmeshPetscCall(createPetscMatrix(
217 : _mc_axial_convection_mat, _block_size * _n_channels, _block_size * _n_channels));
218 278 : LibmeshPetscCall(createPetscVector(_mc_axial_convection_rhs, _block_size * _n_channels));
219 :
220 : // Axial momentum conservation components
221 278 : LibmeshPetscCall(createPetscMatrix(
222 : _amc_turbulent_cross_flows_mat, _block_size * _n_gaps, _block_size * _n_channels));
223 278 : LibmeshPetscCall(createPetscVector(_amc_turbulent_cross_flows_rhs, _block_size * _n_gaps));
224 278 : LibmeshPetscCall(createPetscMatrix(
225 : _amc_time_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
226 278 : LibmeshPetscCall(createPetscVector(_amc_time_derivative_rhs, _block_size * _n_channels));
227 278 : LibmeshPetscCall(createPetscMatrix(
228 : _amc_advective_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
229 278 : LibmeshPetscCall(createPetscVector(_amc_advective_derivative_rhs, _block_size * _n_channels));
230 278 : LibmeshPetscCall(createPetscMatrix(
231 : _amc_cross_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
232 278 : LibmeshPetscCall(createPetscVector(_amc_cross_derivative_rhs, _block_size * _n_channels));
233 278 : LibmeshPetscCall(createPetscMatrix(
234 : _amc_friction_force_mat, _block_size * _n_channels, _block_size * _n_channels));
235 278 : LibmeshPetscCall(createPetscVector(_amc_friction_force_rhs, _block_size * _n_channels));
236 278 : LibmeshPetscCall(createPetscVector(_amc_gravity_rhs, _block_size * _n_channels));
237 278 : LibmeshPetscCall(createPetscMatrix(
238 : _amc_pressure_force_mat, _block_size * _n_channels, _block_size * _n_channels));
239 278 : LibmeshPetscCall(createPetscVector(_amc_pressure_force_rhs, _block_size * _n_channels));
240 278 : LibmeshPetscCall(
241 : createPetscMatrix(_amc_sys_mdot_mat, _block_size * _n_channels, _block_size * _n_channels));
242 278 : LibmeshPetscCall(createPetscVector(_amc_sys_mdot_rhs, _block_size * _n_channels));
243 :
244 : // Lateral momentum conservation components
245 278 : LibmeshPetscCall(
246 : createPetscMatrix(_cmc_time_derivative_mat, _block_size * _n_gaps, _block_size * _n_gaps));
247 278 : LibmeshPetscCall(createPetscVector(_cmc_time_derivative_rhs, _block_size * _n_gaps));
248 278 : LibmeshPetscCall(createPetscMatrix(
249 : _cmc_advective_derivative_mat, _block_size * _n_gaps, _block_size * _n_gaps));
250 278 : LibmeshPetscCall(createPetscVector(_cmc_advective_derivative_rhs, _block_size * _n_gaps));
251 278 : LibmeshPetscCall(
252 : createPetscMatrix(_cmc_friction_force_mat, _block_size * _n_gaps, _block_size * _n_gaps));
253 278 : LibmeshPetscCall(createPetscVector(_cmc_friction_force_rhs, _block_size * _n_gaps));
254 278 : LibmeshPetscCall(
255 : createPetscMatrix(_cmc_pressure_force_mat, _block_size * _n_gaps, _block_size * _n_channels));
256 278 : LibmeshPetscCall(createPetscVector(_cmc_pressure_force_rhs, _block_size * _n_gaps));
257 278 : LibmeshPetscCall(
258 : createPetscMatrix(_cmc_sys_Wij_mat, _block_size * _n_gaps, _block_size * _n_gaps));
259 278 : LibmeshPetscCall(createPetscVector(_cmc_sys_Wij_rhs, _block_size * _n_gaps));
260 :
261 : // Energy conservation components
262 278 : LibmeshPetscCall(createPetscMatrix(
263 : _hc_time_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
264 278 : LibmeshPetscCall(createPetscVector(_hc_time_derivative_rhs, _block_size * _n_channels));
265 278 : LibmeshPetscCall(createPetscMatrix(
266 : _hc_advective_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
267 278 : LibmeshPetscCall(createPetscVector(_hc_advective_derivative_rhs, _block_size * _n_channels));
268 278 : LibmeshPetscCall(createPetscMatrix(
269 : _hc_cross_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
270 278 : LibmeshPetscCall(createPetscVector(_hc_cross_derivative_rhs, _block_size * _n_channels));
271 278 : LibmeshPetscCall(createPetscVector(_hc_added_heat_rhs, _block_size * _n_channels));
272 278 : LibmeshPetscCall(
273 : createPetscMatrix(_hc_sys_h_mat, _block_size * _n_channels, _block_size * _n_channels));
274 278 : LibmeshPetscCall(createPetscVector(_hc_sys_h_rhs, _block_size * _n_channels));
275 :
276 278 : if ((_n_blocks == _n_cells) && _implicit_bool)
277 : {
278 0 : mooseError(name(),
279 : ": When implicit number of blocks can't be equal to number of cells. This will "
280 : "cause problems with the subchannel interpolation scheme.");
281 : }
282 278 : }
283 :
284 : void
285 265 : SubChannel1PhaseProblem::initialSetup()
286 : {
287 265 : ExternalProblem::initialSetup();
288 :
289 530 : _fp = &getUserObject<SinglePhaseFluidProperties>(getParam<UserObjectName>("fp"));
290 265 : _friction_closure =
291 530 : &getUserObject<SCMFrictionClosureBase>(getParam<UserObjectName>("friction_closure"));
292 265 : _mixing_closure =
293 530 : &getUserObject<SCMMixingClosureBase>(getParam<UserObjectName>("mixing_closure"));
294 :
295 : /// Set value for turbulent momentum modeling parameter CT
296 265 : _CT = _mixing_closure->getCT();
297 :
298 : // Create variables for output and storage
299 530 : _mdot_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::MASS_FLOW_RATE));
300 530 : _SumWij_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::SUM_CROSSFLOW));
301 530 : _P_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::PRESSURE));
302 530 : if (getParam<bool>("full_output"))
303 : {
304 508 : _DP_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::PRESSURE_DROP));
305 508 : _ff_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::FRICTION_FACTOR));
306 : }
307 530 : _h_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::ENTHALPY));
308 530 : _T_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::TEMPERATURE));
309 265 : if (_pin_mesh_exist)
310 : {
311 512 : _Tpin_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::PIN_TEMPERATURE));
312 512 : _Dpin_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::PIN_DIAMETER));
313 : _HTC_soln =
314 512 : std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::HEAT_TRANSFER_COEFFICIENT));
315 256 : _pin_HTC_closure =
316 768 : &getUserObject<SCMHTCClosureBase>(getParam<UserObjectName>("pin_HTC_closure"));
317 : }
318 530 : _rho_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::DENSITY));
319 530 : _mu_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::VISCOSITY));
320 530 : _S_flow_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::SURFACE_AREA));
321 530 : _w_perim_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::WETTED_PERIMETER));
322 530 : _q_prime_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::LINEAR_HEAT_RATE));
323 : _displacement_soln =
324 530 : std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::DISPLACEMENT));
325 265 : if (_duct_mesh_exist)
326 : {
327 : _duct_heat_flux_soln =
328 134 : std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::DUCT_HEAT_FLUX));
329 134 : _Tduct_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::DUCT_TEMPERATURE));
330 67 : _duct_HTC_closure =
331 201 : &getUserObject<SCMHTCClosureBase>(getParam<UserObjectName>("duct_HTC_closure"));
332 : }
333 265 : }
334 :
335 : void
336 333 : SubChannel1PhaseProblem::detectDeformation()
337 : {
338 : const Real tol = libMesh::TOLERANCE;
339 333 : const auto pin_diameter = _subchannel_mesh.getPinDiameter();
340 :
341 333 : if (_pin_mesh_exist)
342 : {
343 7190 : for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
344 153909 : for (unsigned int i_pin = 0; i_pin < _n_pins; i_pin++)
345 : {
346 147043 : auto * node = _subchannel_mesh.getPinNode(i_pin, iz);
347 147043 : const Real Dpin = (*_Dpin_soln)(node);
348 147043 : if (std::abs(Dpin) <= tol)
349 0 : mooseError("Dpin is zero at node ",
350 0 : node->id(),
351 : ". You must initialize Dpin to a non-zero value.");
352 147043 : if (std::abs(Dpin - pin_diameter) > tol)
353 690 : _deformation = true;
354 : }
355 : }
356 :
357 8048 : for (unsigned int iz = 0; iz < _n_cells + 1 && !_deformation; iz++)
358 302718 : for (unsigned int i_ch = 0; i_ch < _n_channels && !_deformation; i_ch++)
359 : {
360 295003 : auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
361 295003 : auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
362 :
363 414801 : if ((subch_type == EChannelType::CORNER || subch_type == EChannelType::EDGE) &&
364 119798 : std::abs((*_displacement_soln)(node)) > tol)
365 0 : _deformation = true;
366 : }
367 333 : }
368 :
369 825 : SubChannel1PhaseProblem::~SubChannel1PhaseProblem()
370 : {
371 275 : PetscErrorCode ierr = cleanUp();
372 275 : if (ierr)
373 0 : mooseError(name(), ": Error in memory cleanup");
374 275 : }
375 :
376 : PetscErrorCode
377 275 : SubChannel1PhaseProblem::cleanUp()
378 : {
379 : PetscFunctionBegin;
380 : // We need to clean up the petsc matrices/vectors
381 : // Mass conservation components
382 275 : LibmeshPetscCall(MatDestroy(&_mc_sumWij_mat));
383 275 : LibmeshPetscCall(VecDestroy(&_Wij_vec));
384 275 : LibmeshPetscCall(VecDestroy(&_prod));
385 275 : LibmeshPetscCall(VecDestroy(&_prodp));
386 275 : LibmeshPetscCall(MatDestroy(&_mc_axial_convection_mat));
387 275 : LibmeshPetscCall(VecDestroy(&_mc_axial_convection_rhs));
388 :
389 : // Axial momentum conservation components
390 275 : LibmeshPetscCall(MatDestroy(&_amc_turbulent_cross_flows_mat));
391 275 : LibmeshPetscCall(VecDestroy(&_amc_turbulent_cross_flows_rhs));
392 275 : LibmeshPetscCall(MatDestroy(&_amc_time_derivative_mat));
393 275 : LibmeshPetscCall(VecDestroy(&_amc_time_derivative_rhs));
394 275 : LibmeshPetscCall(MatDestroy(&_amc_advective_derivative_mat));
395 275 : LibmeshPetscCall(VecDestroy(&_amc_advective_derivative_rhs));
396 275 : LibmeshPetscCall(MatDestroy(&_amc_cross_derivative_mat));
397 275 : LibmeshPetscCall(VecDestroy(&_amc_cross_derivative_rhs));
398 275 : LibmeshPetscCall(MatDestroy(&_amc_friction_force_mat));
399 275 : LibmeshPetscCall(VecDestroy(&_amc_friction_force_rhs));
400 275 : LibmeshPetscCall(VecDestroy(&_amc_gravity_rhs));
401 275 : LibmeshPetscCall(MatDestroy(&_amc_pressure_force_mat));
402 275 : LibmeshPetscCall(VecDestroy(&_amc_pressure_force_rhs));
403 275 : LibmeshPetscCall(MatDestroy(&_amc_sys_mdot_mat));
404 275 : LibmeshPetscCall(VecDestroy(&_amc_sys_mdot_rhs));
405 :
406 : // Lateral momentum conservation components
407 275 : LibmeshPetscCall(MatDestroy(&_cmc_time_derivative_mat));
408 275 : LibmeshPetscCall(VecDestroy(&_cmc_time_derivative_rhs));
409 275 : LibmeshPetscCall(MatDestroy(&_cmc_advective_derivative_mat));
410 275 : LibmeshPetscCall(VecDestroy(&_cmc_advective_derivative_rhs));
411 275 : LibmeshPetscCall(MatDestroy(&_cmc_friction_force_mat));
412 275 : LibmeshPetscCall(VecDestroy(&_cmc_friction_force_rhs));
413 275 : LibmeshPetscCall(MatDestroy(&_cmc_pressure_force_mat));
414 275 : LibmeshPetscCall(VecDestroy(&_cmc_pressure_force_rhs));
415 275 : LibmeshPetscCall(MatDestroy(&_cmc_sys_Wij_mat));
416 275 : LibmeshPetscCall(VecDestroy(&_cmc_sys_Wij_rhs));
417 :
418 : // Energy conservation components
419 275 : LibmeshPetscCall(MatDestroy(&_hc_time_derivative_mat));
420 275 : LibmeshPetscCall(VecDestroy(&_hc_time_derivative_rhs));
421 275 : LibmeshPetscCall(MatDestroy(&_hc_advective_derivative_mat));
422 275 : LibmeshPetscCall(VecDestroy(&_hc_advective_derivative_rhs));
423 275 : LibmeshPetscCall(MatDestroy(&_hc_cross_derivative_mat));
424 275 : LibmeshPetscCall(VecDestroy(&_hc_cross_derivative_rhs));
425 275 : LibmeshPetscCall(VecDestroy(&_hc_added_heat_rhs));
426 275 : LibmeshPetscCall(MatDestroy(&_hc_sys_h_mat));
427 275 : LibmeshPetscCall(VecDestroy(&_hc_sys_h_rhs));
428 :
429 275 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
430 : }
431 :
432 : bool
433 330 : SubChannel1PhaseProblem::solverSystemConverged(const unsigned int)
434 : {
435 330 : return _converged;
436 : }
437 :
438 : PetscScalar
439 569663052 : SubChannel1PhaseProblem::computeInterpolationCoefficients(PetscScalar Peclet)
440 : {
441 569663052 : switch (_interpolation_scheme)
442 : {
443 : case 0: // upwind interpolation
444 : return 1.0;
445 0 : case 1: // downwind interpolation
446 0 : return 0.0;
447 469949172 : case 2: // central_difference interpolation
448 469949172 : return 0.5;
449 33092280 : case 3: // exponential interpolation (Peclet limited)
450 33092280 : return ((Peclet - 1.0) * std::exp(Peclet) + 1) / (Peclet * (std::exp(Peclet) - 1.) + 1e-10);
451 0 : default:
452 0 : mooseError(name(),
453 : ": Interpolation scheme should be a string: upwind, downwind, central_difference, "
454 : "exponential");
455 : }
456 : }
457 :
458 : PetscScalar
459 484599102 : SubChannel1PhaseProblem::computeInterpolatedValue(PetscScalar topValue,
460 : PetscScalar botValue,
461 : PetscScalar Peclet)
462 : {
463 484599102 : PetscScalar alpha = computeInterpolationCoefficients(Peclet);
464 484599102 : return alpha * botValue + (1.0 - alpha) * topValue;
465 : }
466 :
467 : void
468 312 : SubChannel1PhaseProblem::computeWijFromSolve(int iblock)
469 : {
470 312 : const unsigned int last_node = (iblock + 1) * _block_size;
471 312 : const unsigned int first_node = iblock * _block_size + 1;
472 : // Initial guess, port crossflow of block (iblock) into a vector that will act as my initial guess
473 312 : libMesh::DenseVector<Real> solution_seed(_n_gaps * _block_size, 0.0);
474 10552 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
475 : {
476 229660 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
477 : {
478 219420 : int i = _n_gaps * (iz - first_node) + i_gap; // column wise transfer
479 219420 : solution_seed(i) = _Wij(i_gap, iz);
480 : }
481 : }
482 :
483 : // Solving the combined lateral momentum equation for Wij using a PETSc solver and update vector
484 : // root
485 312 : libMesh::DenseVector<Real> root(_n_gaps * _block_size, 0.0);
486 312 : LibmeshPetscCall(petscSnesSolver(iblock, solution_seed, root));
487 :
488 : // Assign the solution to the cross-flow matrix
489 : int i = 0;
490 10552 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
491 : {
492 229660 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
493 : {
494 219420 : _Wij(i_gap, iz) = root(i);
495 219420 : i++;
496 : }
497 : }
498 312 : }
499 :
500 : void
501 65569 : SubChannel1PhaseProblem::computeSumWij(int iblock)
502 : {
503 65569 : const unsigned int last_node = (iblock + 1) * _block_size;
504 65569 : const unsigned int first_node = iblock * _block_size + 1;
505 : // Add to solution vector if explicit
506 65569 : if (!_implicit_bool)
507 : {
508 322625 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
509 : {
510 7086110 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
511 : {
512 6781680 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
513 : Real sumWij = 0.0;
514 : // Calculate sum of crossflow into channel i from channels j around i
515 : unsigned int counter = 0;
516 28205760 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
517 : {
518 21424080 : sumWij += _subchannel_mesh.getCrossflowSign(i_ch, counter) * _Wij(i_gap, iz);
519 21424080 : counter++;
520 : }
521 : // The net crossflow coming out of cell i [kg/sec]
522 6781680 : _SumWij_soln->set(node_out, sumWij);
523 : }
524 : }
525 : }
526 : // Add to matrix if implicit
527 : else
528 : {
529 535924 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
530 : {
531 488550 : unsigned int iz_ind = iz - first_node;
532 19505400 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
533 : {
534 : // Calculate sum of crossflow into channel i from channels j around i
535 : unsigned int counter = 0;
536 80680290 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
537 : {
538 61663440 : PetscInt row = i_ch + _n_channels * iz_ind;
539 61663440 : PetscInt col = i_gap + _n_gaps * iz_ind;
540 61663440 : PetscScalar value = _subchannel_mesh.getCrossflowSign(i_ch, counter);
541 61663440 : LibmeshPetscCall(MatSetValues(_mc_sumWij_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
542 61663440 : counter++;
543 : }
544 : }
545 : }
546 47374 : LibmeshPetscCall(MatAssemblyBegin(_mc_sumWij_mat, MAT_FINAL_ASSEMBLY));
547 47374 : LibmeshPetscCall(MatAssemblyEnd(_mc_sumWij_mat, MAT_FINAL_ASSEMBLY));
548 47374 : if (_segregated_bool)
549 : {
550 : Vec loc_prod;
551 : Vec loc_Wij;
552 45662 : LibmeshPetscCall(VecDuplicate(_amc_sys_mdot_rhs, &loc_prod));
553 45662 : LibmeshPetscCall(VecDuplicate(_Wij_vec, &loc_Wij));
554 45662 : LibmeshPetscCall(populateVectorFromDense<libMesh::DenseMatrix<Real>>(
555 : loc_Wij, _Wij, first_node, last_node, _n_gaps));
556 45662 : LibmeshPetscCall(MatMult(_mc_sumWij_mat, loc_Wij, loc_prod));
557 45662 : LibmeshPetscCall(populateSolutionChan<SolutionHandle>(
558 : loc_prod, *_SumWij_soln, first_node, last_node, _n_channels));
559 45662 : LibmeshPetscCall(VecDestroy(&loc_prod));
560 45662 : LibmeshPetscCall(VecDestroy(&loc_Wij));
561 : }
562 : }
563 65569 : }
564 :
565 : void
566 65569 : SubChannel1PhaseProblem::computeMdot(int iblock)
567 : {
568 65569 : const unsigned int last_node = (iblock + 1) * _block_size;
569 65569 : const unsigned int first_node = iblock * _block_size + 1;
570 65569 : if (!_implicit_bool)
571 : {
572 322625 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
573 : {
574 304430 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
575 7086110 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
576 : {
577 6781680 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
578 6781680 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
579 6781680 : auto volume = dz * (*_S_flow_soln)(node_in);
580 6781680 : auto time_term = _TR * ((*_rho_soln)(node_out)-_rho_soln->old(node_out)) * volume / _dt;
581 : // Wij positive out of i into j;
582 6781680 : auto mdot_out = (*_mdot_soln)(node_in) - (*_SumWij_soln)(node_out)-time_term;
583 6781680 : if (mdot_out < 0)
584 : {
585 0 : _console << "Wij = : " << _Wij << "\n";
586 0 : mooseError(name(),
587 : " : Calculation of negative mass flow mdot_out = : ",
588 : mdot_out,
589 : " Axial Level= : ",
590 : iz,
591 : " - Implicit solves are required for recirculating flow.");
592 : }
593 6781680 : _mdot_soln->set(node_out, mdot_out); // kg/sec
594 : }
595 : }
596 : }
597 : else
598 : {
599 535924 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
600 : {
601 488550 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
602 488550 : auto iz_ind = iz - first_node;
603 19505400 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
604 : {
605 19016850 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
606 19016850 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
607 19016850 : auto volume = dz * (*_S_flow_soln)(node_in);
608 :
609 : // Adding time derivative to the RHS
610 19016850 : auto time_term = _TR * ((*_rho_soln)(node_out)-_rho_soln->old(node_out)) * volume / _dt;
611 19016850 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
612 19016850 : PetscScalar value_vec = -1.0 * time_term;
613 19016850 : LibmeshPetscCall(
614 : VecSetValues(_mc_axial_convection_rhs, 1, &row_vec, &value_vec, INSERT_VALUES));
615 :
616 : // Imposing bottom boundary condition or adding of diagonal elements
617 19016850 : if (iz == first_node)
618 : {
619 1785870 : PetscScalar value_vec = (*_mdot_soln)(node_in);
620 1785870 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
621 1785870 : LibmeshPetscCall(
622 : VecSetValues(_mc_axial_convection_rhs, 1, &row_vec, &value_vec, ADD_VALUES));
623 : }
624 : else
625 : {
626 17230980 : PetscInt row = i_ch + _n_channels * iz_ind;
627 17230980 : PetscInt col = i_ch + _n_channels * (iz_ind - 1);
628 17230980 : PetscScalar value = -1.0;
629 17230980 : LibmeshPetscCall(
630 : MatSetValues(_mc_axial_convection_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
631 : }
632 :
633 : // Adding diagonal elements
634 19016850 : PetscInt row = i_ch + _n_channels * iz_ind;
635 19016850 : PetscInt col = i_ch + _n_channels * iz_ind;
636 19016850 : PetscScalar value = 1.0;
637 19016850 : LibmeshPetscCall(
638 : MatSetValues(_mc_axial_convection_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
639 :
640 : // Adding cross flows RHS
641 19016850 : if (_segregated_bool)
642 : {
643 15827820 : PetscScalar value_vec_2 = -1.0 * (*_SumWij_soln)(node_out);
644 15827820 : PetscInt row_vec_2 = i_ch + _n_channels * iz_ind;
645 15827820 : LibmeshPetscCall(
646 : VecSetValues(_mc_axial_convection_rhs, 1, &row_vec_2, &value_vec_2, ADD_VALUES));
647 : }
648 : }
649 : }
650 47374 : LibmeshPetscCall(MatAssemblyBegin(_mc_axial_convection_mat, MAT_FINAL_ASSEMBLY));
651 47374 : LibmeshPetscCall(MatAssemblyEnd(_mc_axial_convection_mat, MAT_FINAL_ASSEMBLY));
652 :
653 47374 : if (_segregated_bool)
654 : {
655 : KSP ksploc;
656 : PC pc;
657 : Vec sol;
658 45662 : LibmeshPetscCall(VecDuplicate(_mc_axial_convection_rhs, &sol));
659 45662 : LibmeshPetscCall(KSPCreate(PETSC_COMM_SELF, &ksploc));
660 45662 : LibmeshPetscCall(KSPSetOperators(ksploc, _mc_axial_convection_mat, _mc_axial_convection_mat));
661 45662 : LibmeshPetscCall(KSPGetPC(ksploc, &pc));
662 45662 : LibmeshPetscCall(PCSetType(pc, PCJACOBI));
663 45662 : LibmeshPetscCall(KSPSetTolerances(ksploc, _rtol, _atol, _dtol, _maxit));
664 45662 : LibmeshPetscCall(KSPSetFromOptions(ksploc));
665 45662 : LibmeshPetscCall(KSPSolve(ksploc, _mc_axial_convection_rhs, sol));
666 45662 : LibmeshPetscCall(populateSolutionChan<SolutionHandle>(
667 : sol, *_mdot_soln, first_node, last_node, _n_channels));
668 45662 : LibmeshPetscCall(VecZeroEntries(_mc_axial_convection_rhs));
669 45662 : LibmeshPetscCall(KSPDestroy(&ksploc));
670 45662 : LibmeshPetscCall(VecDestroy(&sol));
671 : }
672 : }
673 65569 : }
674 :
675 : void
676 65569 : SubChannel1PhaseProblem::computeDP(int iblock)
677 : {
678 65569 : const unsigned int last_node = (iblock + 1) * _block_size;
679 65569 : const unsigned int first_node = iblock * _block_size + 1;
680 65569 : if (!_implicit_bool)
681 : {
682 322625 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
683 : {
684 304430 : auto k_grid = _subchannel_mesh.getKGrid();
685 304430 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
686 7086110 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
687 : {
688 6781680 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
689 6781680 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
690 6781680 : auto rho_in = (*_rho_soln)(node_in);
691 6781680 : auto rho_out = (*_rho_soln)(node_out);
692 6781680 : auto mu_in = (*_mu_soln)(node_in);
693 6781680 : auto S = (*_S_flow_soln)(node_in);
694 6781680 : auto w_perim = (*_w_perim_soln)(node_in);
695 : // hydraulic diameter in the i direction
696 6781680 : auto Dh_i = 4.0 * S / w_perim;
697 6781680 : auto time_term = _TR * ((*_mdot_soln)(node_out)-_mdot_soln->old(node_out)) * dz / _dt -
698 6781680 : dz * 2.0 * (*_mdot_soln)(node_out) * (rho_out - _rho_soln->old(node_out)) /
699 6781680 : rho_in / _dt;
700 : auto mass_term1 =
701 6781680 : Utility::pow<2>((*_mdot_soln)(node_out)) * (1.0 / S / rho_out - 1.0 / S / rho_in);
702 6781680 : auto mass_term2 = -2.0 * (*_mdot_soln)(node_out) * (*_SumWij_soln)(node_out) / S / rho_in;
703 : auto crossflow_term = 0.0;
704 : auto turbulent_term = 0.0;
705 : unsigned int counter = 0;
706 28205760 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
707 : {
708 21424080 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
709 : unsigned int ii_ch = chans.first;
710 : unsigned int jj_ch = chans.second;
711 21424080 : auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
712 21424080 : auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
713 21424080 : auto * node_out_i = _subchannel_mesh.getChannelNode(ii_ch, iz);
714 21424080 : auto * node_out_j = _subchannel_mesh.getChannelNode(jj_ch, iz);
715 21424080 : auto rho_i = (*_rho_soln)(node_in_i);
716 21424080 : auto rho_j = (*_rho_soln)(node_in_j);
717 21424080 : auto Si = (*_S_flow_soln)(node_in_i);
718 21424080 : auto Sj = (*_S_flow_soln)(node_in_j);
719 : Real u_star = 0.0;
720 : // figure out donor axial velocity
721 21424080 : if (_Wij(i_gap, iz) > 0.0)
722 10264606 : u_star = (*_mdot_soln)(node_out_i) / Si / rho_i;
723 : else
724 11159474 : u_star = (*_mdot_soln)(node_out_j) / Sj / rho_j;
725 :
726 21424080 : crossflow_term +=
727 21424080 : _subchannel_mesh.getCrossflowSign(i_ch, counter) * _Wij(i_gap, iz) * u_star;
728 :
729 21424080 : turbulent_term += _WijPrime(i_gap, iz) * (2 * (*_mdot_soln)(node_out) / rho_in / S -
730 21424080 : (*_mdot_soln)(node_out_j) / Sj / rho_j -
731 21424080 : (*_mdot_soln)(node_out_i) / Si / rho_i);
732 21424080 : counter++;
733 : }
734 6781680 : turbulent_term *= _CT;
735 6781680 : auto Re = (((*_mdot_soln)(node_in) / S) * Dh_i / mu_in);
736 6781680 : _friction_args = FrictionStruct(i_ch, Re, S, w_perim);
737 6781680 : Real ff = _friction_closure->computeFrictionFactor(_friction_args);
738 6781680 : if (_ff_soln)
739 6781680 : _ff_soln->set(node_out, ff);
740 : /// Upwind local form loss
741 : auto ki = 0.0;
742 6781680 : if ((*_mdot_soln)(node_out) >= 0)
743 6781680 : ki = k_grid[i_ch][iz - 1];
744 : else
745 0 : ki = k_grid[i_ch][iz];
746 6781680 : auto friction_term = (ff * dz / Dh_i + ki) * 0.5 *
747 6781680 : (*_mdot_soln)(node_out)*std::abs((*_mdot_soln)(node_out)) /
748 6781680 : (S * (*_rho_soln)(node_out));
749 6781680 : auto gravity_term = _dir_grav * _g_grav * (*_rho_soln)(node_out)*dz * S;
750 6781680 : auto DP = (1 / S) * (time_term + mass_term1 + mass_term2 + crossflow_term + turbulent_term +
751 6781680 : friction_term + gravity_term); // Pa
752 6781680 : _DP(i_ch, iz) = DP;
753 6781680 : if (_DP_soln)
754 6781680 : _DP_soln->set(node_out, DP);
755 : }
756 304430 : }
757 : }
758 : else
759 : {
760 47374 : LibmeshPetscCall(MatZeroEntries(_amc_time_derivative_mat));
761 47374 : LibmeshPetscCall(MatZeroEntries(_amc_advective_derivative_mat));
762 47374 : LibmeshPetscCall(MatZeroEntries(_amc_cross_derivative_mat));
763 47374 : LibmeshPetscCall(MatZeroEntries(_amc_friction_force_mat));
764 47374 : LibmeshPetscCall(VecZeroEntries(_amc_time_derivative_rhs));
765 47374 : LibmeshPetscCall(VecZeroEntries(_amc_advective_derivative_rhs));
766 47374 : LibmeshPetscCall(VecZeroEntries(_amc_cross_derivative_rhs));
767 47374 : LibmeshPetscCall(VecZeroEntries(_amc_friction_force_rhs));
768 47374 : LibmeshPetscCall(VecZeroEntries(_amc_gravity_rhs));
769 47374 : LibmeshPetscCall(MatZeroEntries(_amc_sys_mdot_mat));
770 47374 : LibmeshPetscCall(VecZeroEntries(_amc_sys_mdot_rhs));
771 535924 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
772 : {
773 488550 : auto k_grid = _subchannel_mesh.getKGrid();
774 488550 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
775 488550 : auto iz_ind = iz - first_node;
776 19505400 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
777 : {
778 : // inlet and outlet nodes
779 19016850 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
780 19016850 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
781 :
782 : // interpolation weight coefficient
783 : PetscScalar Pe = 0.5;
784 19016850 : if (_interpolation_scheme == 3)
785 : {
786 : // Compute the Peclet number
787 808860 : auto S_in = (*_S_flow_soln)(node_in);
788 808860 : auto S_out = (*_S_flow_soln)(node_out);
789 808860 : auto S_interp = computeInterpolatedValue(S_out, S_in, 0.5);
790 808860 : auto w_perim_in = (*_w_perim_soln)(node_in);
791 808860 : auto w_perim_out = (*_w_perim_soln)(node_out);
792 808860 : auto w_perim_interp = this->computeInterpolatedValue(w_perim_out, w_perim_in, 0.5);
793 : auto mdot_loc =
794 808860 : this->computeInterpolatedValue((*_mdot_soln)(node_out), (*_mdot_soln)(node_in), 0.5);
795 808860 : auto mu_in = (*_mu_soln)(node_in);
796 808860 : auto mu_out = (*_mu_soln)(node_out);
797 808860 : auto mu_interp = this->computeInterpolatedValue(mu_out, mu_in, 0.5);
798 808860 : auto Dh_i = 4.0 * S_interp / w_perim_interp;
799 : // Compute friction factor
800 808860 : auto Re = ((mdot_loc / S_interp) * Dh_i / mu_interp);
801 808860 : _friction_args = FrictionStruct(i_ch, Re, S_interp, w_perim_interp);
802 808860 : Real ff = _friction_closure->computeFrictionFactor(_friction_args);
803 808860 : if (_ff_soln)
804 808860 : _ff_soln->set(node_out, ff);
805 : /// Upwind local form loss
806 : auto ki = 0.0;
807 808860 : if ((*_mdot_soln)(node_out) >= 0)
808 808860 : ki = k_grid[i_ch][iz - 1];
809 : else
810 0 : ki = k_grid[i_ch][iz];
811 808860 : Pe = 1.0 / ((ff * dz / Dh_i + ki) * 0.5) * mdot_loc / std::abs(mdot_loc);
812 : }
813 19016850 : auto alpha = computeInterpolationCoefficients(Pe);
814 :
815 : // inlet, outlet, and interpolated density
816 19016850 : auto rho_in = (*_rho_soln)(node_in);
817 19016850 : auto rho_out = (*_rho_soln)(node_out);
818 19016850 : auto rho_interp = computeInterpolatedValue(rho_out, rho_in, Pe);
819 :
820 : // inlet, outlet, and interpolated viscosity
821 19016850 : auto mu_in = (*_mu_soln)(node_in);
822 19016850 : auto mu_out = (*_mu_soln)(node_out);
823 19016850 : auto mu_interp = computeInterpolatedValue(mu_out, mu_in, Pe);
824 :
825 : // inlet, outlet, and interpolated axial surface area
826 19016850 : auto S_in = (*_S_flow_soln)(node_in);
827 19016850 : auto S_out = (*_S_flow_soln)(node_out);
828 19016850 : auto S_interp = computeInterpolatedValue(S_out, S_in, Pe);
829 :
830 : // inlet, outlet, and interpolated wetted perimeter
831 19016850 : auto w_perim_in = (*_w_perim_soln)(node_in);
832 19016850 : auto w_perim_out = (*_w_perim_soln)(node_out);
833 19016850 : auto w_perim_interp = computeInterpolatedValue(w_perim_out, w_perim_in, Pe);
834 :
835 : // hydraulic diameter in the i direction
836 19016850 : auto Dh_i = 4.0 * S_interp / w_perim_interp;
837 :
838 : /// Time derivative term
839 19016850 : if (iz == first_node)
840 : {
841 1785870 : PetscScalar value_vec_tt = -1.0 * _TR * alpha * (*_mdot_soln)(node_in)*dz / _dt;
842 1785870 : PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
843 1785870 : LibmeshPetscCall(
844 : VecSetValues(_amc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
845 : }
846 : else
847 : {
848 17230980 : PetscInt row_tt = i_ch + _n_channels * iz_ind;
849 17230980 : PetscInt col_tt = i_ch + _n_channels * (iz_ind - 1);
850 17230980 : PetscScalar value_tt = _TR * alpha * dz / _dt;
851 17230980 : LibmeshPetscCall(MatSetValues(
852 : _amc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
853 : }
854 :
855 : // Adding diagonal elements
856 19016850 : PetscInt row_tt = i_ch + _n_channels * iz_ind;
857 19016850 : PetscInt col_tt = i_ch + _n_channels * iz_ind;
858 19016850 : PetscScalar value_tt = _TR * (1.0 - alpha) * dz / _dt;
859 19016850 : LibmeshPetscCall(MatSetValues(
860 : _amc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
861 :
862 : // Adding RHS elements
863 : PetscScalar mdot_old_interp =
864 19016850 : computeInterpolatedValue(_mdot_soln->old(node_out), _mdot_soln->old(node_in), Pe);
865 19016850 : PetscScalar value_vec_tt = _TR * mdot_old_interp * dz / _dt;
866 19016850 : PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
867 19016850 : LibmeshPetscCall(
868 : VecSetValues(_amc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
869 :
870 : /// Advective derivative term
871 19016850 : if (iz == first_node)
872 : {
873 1785870 : PetscScalar value_vec_at = Utility::pow<2>((*_mdot_soln)(node_in)) / (S_in * rho_in);
874 1785870 : PetscInt row_vec_at = i_ch + _n_channels * iz_ind;
875 1785870 : LibmeshPetscCall(VecSetValues(
876 : _amc_advective_derivative_rhs, 1, &row_vec_at, &value_vec_at, ADD_VALUES));
877 : }
878 : else
879 : {
880 17230980 : PetscInt row_at = i_ch + _n_channels * iz_ind;
881 17230980 : PetscInt col_at = i_ch + _n_channels * (iz_ind - 1);
882 17230980 : PetscScalar value_at = -1.0 * std::abs((*_mdot_soln)(node_in)) / (S_in * rho_in);
883 17230980 : LibmeshPetscCall(MatSetValues(
884 : _amc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
885 : }
886 :
887 : // Adding diagonal elements
888 19016850 : PetscInt row_at = i_ch + _n_channels * iz_ind;
889 19016850 : PetscInt col_at = i_ch + _n_channels * iz_ind;
890 19016850 : PetscScalar value_at = std::abs((*_mdot_soln)(node_out)) / (S_out * rho_out);
891 19016850 : LibmeshPetscCall(MatSetValues(
892 : _amc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
893 :
894 : /// Cross derivative term
895 : unsigned int counter = 0;
896 : unsigned int cross_index = iz; // iz-1;
897 80680290 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
898 : {
899 61663440 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
900 : unsigned int ii_ch = chans.first;
901 : unsigned int jj_ch = chans.second;
902 61663440 : auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
903 61663440 : auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
904 61663440 : auto * node_out_i = _subchannel_mesh.getChannelNode(ii_ch, iz);
905 61663440 : auto * node_out_j = _subchannel_mesh.getChannelNode(jj_ch, iz);
906 : auto rho_i =
907 61663440 : computeInterpolatedValue((*_rho_soln)(node_out_i), (*_rho_soln)(node_in_i), Pe);
908 : auto rho_j =
909 61663440 : computeInterpolatedValue((*_rho_soln)(node_out_j), (*_rho_soln)(node_in_j), Pe);
910 : auto S_i =
911 61663440 : computeInterpolatedValue((*_S_flow_soln)(node_out_i), (*_S_flow_soln)(node_in_i), Pe);
912 : auto S_j =
913 61663440 : computeInterpolatedValue((*_S_flow_soln)(node_out_j), (*_S_flow_soln)(node_in_j), Pe);
914 : auto u_star = 0.0;
915 : // figure out donor axial velocity
916 61663440 : if (_Wij(i_gap, cross_index) > 0.0)
917 : {
918 32835580 : if (iz == first_node)
919 : {
920 3115762 : u_star = (*_mdot_soln)(node_in_i) / S_i / rho_i;
921 6231524 : PetscScalar value_vec_ct = -1.0 * alpha *
922 3115762 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
923 3115762 : _Wij(i_gap, cross_index) * u_star;
924 3115762 : PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
925 3115762 : LibmeshPetscCall(VecSetValues(
926 : _amc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
927 : }
928 : else
929 : {
930 29719818 : PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
931 29719818 : _Wij(i_gap, cross_index) / S_i / rho_i;
932 29719818 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
933 29719818 : PetscInt col_ct = ii_ch + _n_channels * (iz_ind - 1);
934 29719818 : LibmeshPetscCall(MatSetValues(
935 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
936 : }
937 32835580 : PetscScalar value_ct = (1.0 - alpha) *
938 32835580 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
939 32835580 : _Wij(i_gap, cross_index) / S_i / rho_i;
940 32835580 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
941 32835580 : PetscInt col_ct = ii_ch + _n_channels * iz_ind;
942 32835580 : LibmeshPetscCall(MatSetValues(
943 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
944 : }
945 28827860 : else if (_Wij(i_gap, cross_index) < 0.0) // _Wij=0 operations not necessary
946 : {
947 28103568 : if (iz == first_node)
948 : {
949 2674606 : u_star = (*_mdot_soln)(node_in_j) / S_j / rho_j;
950 5349212 : PetscScalar value_vec_ct = -1.0 * alpha *
951 2674606 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
952 2674606 : _Wij(i_gap, cross_index) * u_star;
953 2674606 : PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
954 2674606 : LibmeshPetscCall(VecSetValues(
955 : _amc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
956 : }
957 : else
958 : {
959 25428962 : PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
960 25428962 : _Wij(i_gap, cross_index) / S_j / rho_j;
961 25428962 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
962 25428962 : PetscInt col_ct = jj_ch + _n_channels * (iz_ind - 1);
963 25428962 : LibmeshPetscCall(MatSetValues(
964 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
965 : }
966 28103568 : PetscScalar value_ct = (1.0 - alpha) *
967 28103568 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
968 28103568 : _Wij(i_gap, cross_index) / S_j / rho_j;
969 28103568 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
970 28103568 : PetscInt col_ct = jj_ch + _n_channels * iz_ind;
971 28103568 : LibmeshPetscCall(MatSetValues(
972 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
973 : }
974 :
975 61663440 : if (iz == first_node)
976 : {
977 5824020 : PetscScalar value_vec_ct = -2.0 * alpha * (*_mdot_soln)(node_in)*_CT *
978 5824020 : _WijPrime(i_gap, cross_index) / (rho_interp * S_interp);
979 5824020 : value_vec_ct += alpha * (*_mdot_soln)(node_in_j)*_CT * _WijPrime(i_gap, cross_index) /
980 5824020 : (rho_j * S_j);
981 5824020 : value_vec_ct += alpha * (*_mdot_soln)(node_in_i)*_CT * _WijPrime(i_gap, cross_index) /
982 5824020 : (rho_i * S_i);
983 5824020 : PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
984 5824020 : LibmeshPetscCall(
985 : VecSetValues(_amc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
986 : }
987 : else
988 : {
989 : PetscScalar value_center_ct =
990 55839420 : 2.0 * alpha * _CT * _WijPrime(i_gap, cross_index) / (rho_interp * S_interp);
991 55839420 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
992 55839420 : PetscInt col_ct = i_ch + _n_channels * (iz_ind - 1);
993 55839420 : LibmeshPetscCall(MatSetValues(
994 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
995 :
996 : PetscScalar value_left_ct =
997 55839420 : -1.0 * alpha * _CT * _WijPrime(i_gap, cross_index) / (rho_j * S_j);
998 55839420 : row_ct = i_ch + _n_channels * iz_ind;
999 55839420 : col_ct = jj_ch + _n_channels * (iz_ind - 1);
1000 55839420 : LibmeshPetscCall(MatSetValues(
1001 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
1002 :
1003 : PetscScalar value_right_ct =
1004 55839420 : -1.0 * alpha * _CT * _WijPrime(i_gap, cross_index) / (rho_i * S_i);
1005 55839420 : row_ct = i_ch + _n_channels * iz_ind;
1006 55839420 : col_ct = ii_ch + _n_channels * (iz_ind - 1);
1007 55839420 : LibmeshPetscCall(MatSetValues(
1008 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
1009 : }
1010 :
1011 : PetscScalar value_center_ct =
1012 61663440 : 2.0 * (1.0 - alpha) * _CT * _WijPrime(i_gap, cross_index) / (rho_interp * S_interp);
1013 61663440 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1014 61663440 : PetscInt col_ct = i_ch + _n_channels * iz_ind;
1015 61663440 : LibmeshPetscCall(MatSetValues(
1016 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
1017 :
1018 : PetscScalar value_left_ct =
1019 61663440 : -1.0 * (1.0 - alpha) * _CT * _WijPrime(i_gap, cross_index) / (rho_j * S_j);
1020 61663440 : row_ct = i_ch + _n_channels * iz_ind;
1021 61663440 : col_ct = jj_ch + _n_channels * iz_ind;
1022 61663440 : LibmeshPetscCall(MatSetValues(
1023 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
1024 :
1025 : PetscScalar value_right_ct =
1026 61663440 : -1.0 * (1.0 - alpha) * _CT * _WijPrime(i_gap, cross_index) / (rho_i * S_i);
1027 61663440 : row_ct = i_ch + _n_channels * iz_ind;
1028 61663440 : col_ct = ii_ch + _n_channels * iz_ind;
1029 61663440 : LibmeshPetscCall(MatSetValues(
1030 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
1031 61663440 : counter++;
1032 : }
1033 :
1034 : /// Friction term
1035 : PetscScalar mdot_interp =
1036 19016850 : computeInterpolatedValue((*_mdot_soln)(node_out), (*_mdot_soln)(node_in), Pe);
1037 19016850 : auto Re = ((mdot_interp / S_interp) * Dh_i / mu_interp);
1038 19016850 : _friction_args = FrictionStruct(i_ch, Re, S_interp, w_perim_interp);
1039 19016850 : Real ff = _friction_closure->computeFrictionFactor(_friction_args);
1040 19016850 : if (_ff_soln)
1041 18994800 : _ff_soln->set(node_out, ff);
1042 : /// Upwind local form loss
1043 : auto ki = 0.0;
1044 19016850 : if ((*_mdot_soln)(node_out) >= 0)
1045 19016850 : ki = k_grid[i_ch][iz - 1];
1046 : else
1047 0 : ki = k_grid[i_ch][iz];
1048 19016850 : auto coef = (ff * dz / Dh_i + ki) * 0.5 * std::abs((*_mdot_soln)(node_out)) /
1049 19016850 : (S_interp * rho_interp);
1050 19016850 : if (iz == first_node)
1051 : {
1052 1785870 : PetscScalar value_vec = -1.0 * alpha * coef * (*_mdot_soln)(node_in);
1053 1785870 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
1054 1785870 : LibmeshPetscCall(
1055 : VecSetValues(_amc_friction_force_rhs, 1, &row_vec, &value_vec, ADD_VALUES));
1056 : }
1057 : else
1058 : {
1059 17230980 : PetscInt row = i_ch + _n_channels * iz_ind;
1060 17230980 : PetscInt col = i_ch + _n_channels * (iz_ind - 1);
1061 17230980 : PetscScalar value = alpha * coef;
1062 17230980 : LibmeshPetscCall(
1063 : MatSetValues(_amc_friction_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1064 : }
1065 :
1066 : // Adding diagonal elements
1067 19016850 : PetscInt row = i_ch + _n_channels * iz_ind;
1068 19016850 : PetscInt col = i_ch + _n_channels * iz_ind;
1069 19016850 : PetscScalar value = (1.0 - alpha) * coef;
1070 19016850 : LibmeshPetscCall(
1071 : MatSetValues(_amc_friction_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1072 :
1073 : /// Gravity force
1074 19016850 : PetscScalar value_vec = _dir_grav * -1.0 * _g_grav * rho_interp * dz * S_interp;
1075 19016850 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
1076 19016850 : LibmeshPetscCall(VecSetValues(_amc_gravity_rhs, 1, &row_vec, &value_vec, ADD_VALUES));
1077 : }
1078 488550 : }
1079 : /// Assembling system
1080 47374 : LibmeshPetscCall(MatZeroEntries(_amc_sys_mdot_mat));
1081 47374 : LibmeshPetscCall(VecZeroEntries(_amc_sys_mdot_rhs));
1082 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1083 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1084 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1085 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1086 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
1087 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
1088 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_friction_force_mat, MAT_FINAL_ASSEMBLY));
1089 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_friction_force_mat, MAT_FINAL_ASSEMBLY));
1090 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1091 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1092 : // Matrix
1093 : #if !PETSC_VERSION_LESS_THAN(3, 15, 0)
1094 47374 : LibmeshPetscCall(
1095 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_time_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1096 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1097 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1098 47374 : LibmeshPetscCall(
1099 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_advective_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1100 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1101 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1102 47374 : LibmeshPetscCall(
1103 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_cross_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1104 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1105 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1106 47374 : LibmeshPetscCall(
1107 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_friction_force_mat, UNKNOWN_NONZERO_PATTERN));
1108 : #else
1109 : LibmeshPetscCall(
1110 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_time_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1111 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1112 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1113 : LibmeshPetscCall(
1114 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_advective_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1115 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1116 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1117 : LibmeshPetscCall(
1118 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_cross_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1119 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1120 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1121 : LibmeshPetscCall(
1122 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_friction_force_mat, DIFFERENT_NONZERO_PATTERN));
1123 : #endif
1124 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1125 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1126 : // RHS
1127 47374 : LibmeshPetscCall(VecAXPY(_amc_sys_mdot_rhs, 1.0, _amc_time_derivative_rhs));
1128 47374 : LibmeshPetscCall(VecAXPY(_amc_sys_mdot_rhs, 1.0, _amc_advective_derivative_rhs));
1129 47374 : LibmeshPetscCall(VecAXPY(_amc_sys_mdot_rhs, 1.0, _amc_cross_derivative_rhs));
1130 47374 : LibmeshPetscCall(VecAXPY(_amc_sys_mdot_rhs, 1.0, _amc_friction_force_rhs));
1131 47374 : LibmeshPetscCall(VecAXPY(_amc_sys_mdot_rhs, 1.0, _amc_gravity_rhs));
1132 47374 : if (_segregated_bool)
1133 : {
1134 : // Assembly the matrix system
1135 45662 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
1136 : _prod, *_mdot_soln, first_node, last_node, _n_channels));
1137 : Vec ls;
1138 45662 : LibmeshPetscCall(VecDuplicate(_amc_sys_mdot_rhs, &ls));
1139 45662 : LibmeshPetscCall(MatMult(_amc_sys_mdot_mat, _prod, ls));
1140 45662 : LibmeshPetscCall(VecAXPY(ls, -1.0, _amc_sys_mdot_rhs));
1141 : PetscScalar * xx;
1142 45662 : LibmeshPetscCall(VecGetArray(ls, &xx));
1143 481932 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1144 : {
1145 436270 : auto iz_ind = iz - first_node;
1146 16264090 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1147 : {
1148 : // Setting nodes
1149 15827820 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1150 15827820 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1151 :
1152 : // inlet, outlet, and interpolated axial surface area
1153 15827820 : auto S_in = (*_S_flow_soln)(node_in);
1154 15827820 : auto S_out = (*_S_flow_soln)(node_out);
1155 15827820 : auto S_interp = computeInterpolatedValue(S_out, S_in, 0.5);
1156 :
1157 : // Setting solutions
1158 15827820 : if (S_interp != 0)
1159 : {
1160 15827820 : auto DP = (1 / S_interp) * xx[iz_ind * _n_channels + i_ch];
1161 15827820 : _DP(i_ch, iz) = DP;
1162 15827820 : if (_DP_soln)
1163 15827820 : _DP_soln->set(node_out, DP);
1164 : }
1165 : else
1166 : {
1167 : auto DP = 0.0;
1168 0 : _DP(i_ch, iz) = DP;
1169 0 : if (_DP_soln)
1170 0 : _DP_soln->set(node_out, DP);
1171 : }
1172 : }
1173 : }
1174 45662 : LibmeshPetscCall(VecDestroy(&ls));
1175 : }
1176 : }
1177 65569 : }
1178 :
1179 : void
1180 65569 : SubChannel1PhaseProblem::computeP(int iblock)
1181 : {
1182 65569 : const unsigned int last_node = (iblock + 1) * _block_size;
1183 65569 : const unsigned int first_node = iblock * _block_size + 1;
1184 65569 : if (!_implicit_bool)
1185 : {
1186 18195 : if (!_staggered_pressure_bool)
1187 : {
1188 284829 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1189 : {
1190 : // Calculate pressure in the inlet of the cell assuming known outlet
1191 5814790 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1192 : {
1193 5544720 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1194 5544720 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1195 : // update Pressure solution
1196 5544720 : _P_soln->set(node_in, (*_P_soln)(node_out) + _DP(i_ch, iz));
1197 : }
1198 : }
1199 : }
1200 : else
1201 : {
1202 37796 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1203 : {
1204 : // Calculate pressure in the inlet of the cell assuming known outlet
1205 1271320 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1206 : {
1207 1236960 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1208 1236960 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1209 : // update Pressure solution
1210 : // Note: assuming uniform axial discretization in the curren code
1211 : // We will need to update this later if we allow non-uniform refinements in the axial
1212 : // direction
1213 : PetscScalar Pe = 0.5;
1214 1236960 : auto alpha = computeInterpolationCoefficients(Pe);
1215 1236960 : if (iz == last_node)
1216 : {
1217 123696 : _P_soln->set(node_in, (*_P_soln)(node_out) + _DP(i_ch, iz) / 2.0);
1218 : }
1219 : else
1220 : {
1221 1113264 : _P_soln->set(node_in,
1222 1113264 : (*_P_soln)(node_out) + (1.0 - alpha) * _DP(i_ch, iz) +
1223 1113264 : alpha * _DP(i_ch, iz - 1));
1224 : }
1225 : }
1226 : }
1227 : }
1228 : }
1229 : else
1230 : {
1231 47374 : if (!_staggered_pressure_bool)
1232 : {
1233 47374 : LibmeshPetscCall(VecZeroEntries(_amc_pressure_force_rhs));
1234 535924 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1235 : {
1236 488550 : auto iz_ind = iz - first_node;
1237 : // Calculate pressure in the inlet of the cell assuming known outlet
1238 19505400 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1239 : {
1240 19016850 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1241 19016850 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1242 :
1243 : // inlet, outlet, and interpolated axial surface area
1244 19016850 : auto S_in = (*_S_flow_soln)(node_in);
1245 19016850 : auto S_out = (*_S_flow_soln)(node_out);
1246 19016850 : auto S_interp = computeInterpolatedValue(S_out, S_in, 0.5);
1247 :
1248 : // Creating matrix of coefficients
1249 19016850 : PetscInt row = i_ch + _n_channels * iz_ind;
1250 19016850 : PetscInt col = i_ch + _n_channels * iz_ind;
1251 19016850 : PetscScalar value = -1.0 * S_interp;
1252 19016850 : LibmeshPetscCall(
1253 : MatSetValues(_amc_pressure_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1254 :
1255 19016850 : if (iz == last_node)
1256 : {
1257 1785870 : PetscScalar value = -1.0 * (*_P_soln)(node_out)*S_interp;
1258 1785870 : PetscInt row = i_ch + _n_channels * iz_ind;
1259 1785870 : LibmeshPetscCall(VecSetValues(_amc_pressure_force_rhs, 1, &row, &value, ADD_VALUES));
1260 : }
1261 : else
1262 : {
1263 17230980 : PetscInt row = i_ch + _n_channels * iz_ind;
1264 17230980 : PetscInt col = i_ch + _n_channels * (iz_ind + 1);
1265 17230980 : PetscScalar value = 1.0 * S_interp;
1266 17230980 : LibmeshPetscCall(
1267 : MatSetValues(_amc_pressure_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1268 : }
1269 :
1270 19016850 : if (_segregated_bool)
1271 : {
1272 15827820 : auto dp_out = _DP(i_ch, iz);
1273 15827820 : PetscScalar value_v = -1.0 * dp_out * S_interp;
1274 15827820 : PetscInt row_v = i_ch + _n_channels * iz_ind;
1275 15827820 : LibmeshPetscCall(
1276 : VecSetValues(_amc_pressure_force_rhs, 1, &row_v, &value_v, ADD_VALUES));
1277 : }
1278 : }
1279 : }
1280 : // Solving pressure problem
1281 47374 : LibmeshPetscCall(MatAssemblyBegin(_amc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1282 47374 : LibmeshPetscCall(MatAssemblyEnd(_amc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1283 47374 : if (_segregated_bool)
1284 : {
1285 : KSP ksploc;
1286 : PC pc;
1287 : Vec sol;
1288 45662 : LibmeshPetscCall(VecDuplicate(_amc_pressure_force_rhs, &sol));
1289 45662 : LibmeshPetscCall(KSPCreate(PETSC_COMM_SELF, &ksploc));
1290 45662 : LibmeshPetscCall(KSPSetOperators(ksploc, _amc_pressure_force_mat, _amc_pressure_force_mat));
1291 45662 : LibmeshPetscCall(KSPGetPC(ksploc, &pc));
1292 45662 : LibmeshPetscCall(PCSetType(pc, PCJACOBI));
1293 45662 : LibmeshPetscCall(KSPSetTolerances(ksploc, _rtol, _atol, _dtol, _maxit));
1294 45662 : LibmeshPetscCall(KSPSetFromOptions(ksploc));
1295 45662 : LibmeshPetscCall(KSPSolve(ksploc, _amc_pressure_force_rhs, sol));
1296 : PetscScalar * xx;
1297 45662 : LibmeshPetscCall(VecGetArray(sol, &xx));
1298 : // update Pressure solution
1299 481932 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1300 : {
1301 436270 : auto iz_ind = iz - first_node;
1302 16264090 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1303 : {
1304 15827820 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1305 15827820 : PetscScalar value = xx[iz_ind * _n_channels + i_ch];
1306 15827820 : _P_soln->set(node_in, value);
1307 : }
1308 : }
1309 45662 : LibmeshPetscCall(VecZeroEntries(_amc_pressure_force_rhs));
1310 45662 : LibmeshPetscCall(KSPDestroy(&ksploc));
1311 45662 : LibmeshPetscCall(VecDestroy(&sol));
1312 : }
1313 : }
1314 : else
1315 : {
1316 0 : LibmeshPetscCall(VecZeroEntries(_amc_pressure_force_rhs));
1317 0 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1318 : {
1319 0 : auto iz_ind = iz - first_node;
1320 : // Calculate pressure in the inlet of the cell assuming known outlet
1321 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1322 : {
1323 0 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1324 0 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1325 :
1326 : // inlet, outlet, and interpolated axial surface area
1327 0 : auto S_in = (*_S_flow_soln)(node_in);
1328 0 : auto S_out = (*_S_flow_soln)(node_out);
1329 0 : auto S_interp = computeInterpolatedValue(S_out, S_in, 0.5);
1330 :
1331 : // Creating matrix of coefficients
1332 0 : PetscInt row = i_ch + _n_channels * iz_ind;
1333 0 : PetscInt col = i_ch + _n_channels * iz_ind;
1334 0 : PetscScalar value = -1.0 * S_interp;
1335 0 : LibmeshPetscCall(
1336 : MatSetValues(_amc_pressure_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1337 :
1338 0 : if (iz == last_node)
1339 : {
1340 0 : PetscScalar value = -1.0 * (*_P_soln)(node_out)*S_interp;
1341 0 : PetscInt row = i_ch + _n_channels * iz_ind;
1342 0 : LibmeshPetscCall(VecSetValues(_amc_pressure_force_rhs, 1, &row, &value, ADD_VALUES));
1343 :
1344 0 : auto dp_out = _DP(i_ch, iz);
1345 0 : PetscScalar value_v = -1.0 * dp_out / 2.0 * S_interp;
1346 0 : PetscInt row_v = i_ch + _n_channels * iz_ind;
1347 0 : LibmeshPetscCall(
1348 : VecSetValues(_amc_pressure_force_rhs, 1, &row_v, &value_v, ADD_VALUES));
1349 : }
1350 : else
1351 : {
1352 0 : PetscInt row = i_ch + _n_channels * iz_ind;
1353 0 : PetscInt col = i_ch + _n_channels * (iz_ind + 1);
1354 0 : PetscScalar value = 1.0 * S_interp;
1355 0 : LibmeshPetscCall(
1356 : MatSetValues(_amc_pressure_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1357 :
1358 0 : if (_segregated_bool)
1359 : {
1360 0 : auto dp_in = _DP(i_ch, iz - 1);
1361 0 : auto dp_out = _DP(i_ch, iz);
1362 0 : auto dp_interp = computeInterpolatedValue(dp_out, dp_in, 0.5);
1363 0 : PetscScalar value_v = -1.0 * dp_interp * S_interp;
1364 0 : PetscInt row_v = i_ch + _n_channels * iz_ind;
1365 0 : LibmeshPetscCall(
1366 : VecSetValues(_amc_pressure_force_rhs, 1, &row_v, &value_v, ADD_VALUES));
1367 : }
1368 : }
1369 : }
1370 : }
1371 : // Solving pressure problem
1372 0 : LibmeshPetscCall(MatAssemblyBegin(_amc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1373 0 : LibmeshPetscCall(MatAssemblyEnd(_amc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1374 0 : if (_verbose_subchannel)
1375 0 : _console << "Block: " << iblock << " - Axial momentum pressure force matrix assembled"
1376 0 : << std::endl;
1377 :
1378 0 : if (_segregated_bool)
1379 : {
1380 : KSP ksploc;
1381 : PC pc;
1382 : Vec sol;
1383 0 : LibmeshPetscCall(VecDuplicate(_amc_pressure_force_rhs, &sol));
1384 0 : LibmeshPetscCall(KSPCreate(PETSC_COMM_SELF, &ksploc));
1385 0 : LibmeshPetscCall(KSPSetOperators(ksploc, _amc_pressure_force_mat, _amc_pressure_force_mat));
1386 0 : LibmeshPetscCall(KSPGetPC(ksploc, &pc));
1387 0 : LibmeshPetscCall(PCSetType(pc, PCJACOBI));
1388 0 : LibmeshPetscCall(KSPSetTolerances(ksploc, _rtol, _atol, _dtol, _maxit));
1389 0 : LibmeshPetscCall(KSPSetFromOptions(ksploc));
1390 0 : LibmeshPetscCall(KSPSolve(ksploc, _amc_pressure_force_rhs, sol));
1391 : PetscScalar * xx;
1392 0 : LibmeshPetscCall(VecGetArray(sol, &xx));
1393 : // update Pressure solution
1394 0 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1395 : {
1396 0 : auto iz_ind = iz - first_node;
1397 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1398 : {
1399 0 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1400 0 : PetscScalar value = xx[iz_ind * _n_channels + i_ch];
1401 0 : _P_soln->set(node_in, value);
1402 : }
1403 : }
1404 0 : LibmeshPetscCall(VecZeroEntries(_amc_pressure_force_rhs));
1405 0 : LibmeshPetscCall(KSPDestroy(&ksploc));
1406 0 : LibmeshPetscCall(VecDestroy(&sol));
1407 : }
1408 : }
1409 : }
1410 65569 : }
1411 :
1412 : void
1413 1838 : SubChannel1PhaseProblem::computeT(int iblock)
1414 : {
1415 1838 : const unsigned int last_node = (iblock + 1) * _block_size;
1416 1838 : const unsigned int first_node = iblock * _block_size + 1;
1417 58648 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1418 : {
1419 3236150 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1420 : {
1421 3179340 : auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
1422 3179340 : _T_soln->set(node, _fp->T_from_p_h((*_P_soln)(node) + _P_out, (*_h_soln)(node)));
1423 : }
1424 : }
1425 1838 : }
1426 :
1427 : void
1428 2014 : SubChannel1PhaseProblem::computeRho(int iblock)
1429 : {
1430 2014 : const unsigned int last_node = (iblock + 1) * _block_size;
1431 2014 : const unsigned int first_node = iblock * _block_size + 1;
1432 2014 : if (iblock == 0)
1433 : {
1434 121912 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1435 : {
1436 119898 : auto * node = _subchannel_mesh.getChannelNode(i_ch, 0);
1437 119898 : _rho_soln->set(node, _fp->rho_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node)));
1438 : }
1439 : }
1440 64434 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1441 : {
1442 3365720 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1443 : {
1444 3303300 : auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
1445 3303300 : _rho_soln->set(node, _fp->rho_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node)));
1446 : }
1447 : }
1448 2014 : }
1449 :
1450 : void
1451 2014 : SubChannel1PhaseProblem::computeMu(int iblock)
1452 : {
1453 2014 : const unsigned int last_node = (iblock + 1) * _block_size;
1454 2014 : const unsigned int first_node = iblock * _block_size + 1;
1455 2014 : if (iblock == 0)
1456 : {
1457 121912 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1458 : {
1459 119898 : auto * node = _subchannel_mesh.getChannelNode(i_ch, 0);
1460 119898 : _mu_soln->set(node, _fp->mu_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node)));
1461 : }
1462 : }
1463 64434 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1464 : {
1465 3365720 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1466 : {
1467 3303300 : auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
1468 3303300 : _mu_soln->set(node, _fp->mu_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node)));
1469 : }
1470 : }
1471 2014 : }
1472 :
1473 : void
1474 65569 : SubChannel1PhaseProblem::computeWijResidual(int iblock)
1475 : {
1476 65569 : const unsigned int last_node = (iblock + 1) * _block_size;
1477 65569 : const unsigned int first_node = iblock * _block_size + 1;
1478 : // Cross flow residual
1479 65569 : if (!_implicit_bool)
1480 : {
1481 18195 : const Real & pitch = _subchannel_mesh.getPitch();
1482 322625 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1483 : {
1484 304430 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
1485 11016470 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
1486 : {
1487 10712040 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
1488 : unsigned int i_ch = chans.first;
1489 : unsigned int j_ch = chans.second;
1490 10712040 : auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1491 10712040 : auto * node_out_i = _subchannel_mesh.getChannelNode(i_ch, iz);
1492 10712040 : auto * node_in_j = _subchannel_mesh.getChannelNode(j_ch, iz - 1);
1493 10712040 : auto * node_out_j = _subchannel_mesh.getChannelNode(j_ch, iz);
1494 10712040 : auto rho_i = (*_rho_soln)(node_in_i);
1495 10712040 : auto rho_j = (*_rho_soln)(node_in_j);
1496 10712040 : auto Si = (*_S_flow_soln)(node_in_i);
1497 10712040 : auto Sj = (*_S_flow_soln)(node_in_j);
1498 10712040 : auto Sij = dz * _subchannel_mesh.getGapWidth(iz, i_gap);
1499 10712040 : auto Lij = pitch;
1500 : // total local form loss in the ij direction
1501 10712040 : auto friction_term = _kij * _Wij(i_gap, iz) * std::abs(_Wij(i_gap, iz));
1502 10712040 : auto DPij = (*_P_soln)(node_in_i) - (*_P_soln)(node_in_j);
1503 : // Figure out donor cell density
1504 : auto rho_star = 0.0;
1505 10712040 : if (_Wij(i_gap, iz) > 0.0)
1506 : rho_star = rho_i;
1507 5579737 : else if (_Wij(i_gap, iz) < 0.0)
1508 : rho_star = rho_j;
1509 : else
1510 892464 : rho_star = (rho_i + rho_j) / 2.0;
1511 : auto mass_term_out =
1512 10712040 : (*_mdot_soln)(node_out_i) / (*_S_flow_soln)(node_out_i) / (*_rho_soln)(node_out_i) +
1513 10712040 : (*_mdot_soln)(node_out_j) / (*_S_flow_soln)(node_out_j) / (*_rho_soln)(node_out_j);
1514 : auto mass_term_in =
1515 10712040 : (*_mdot_soln)(node_in_i) / Si / rho_i + (*_mdot_soln)(node_in_j) / Sj / rho_j;
1516 10712040 : auto term_out = Sij * rho_star * (Lij / dz) * mass_term_out * _Wij(i_gap, iz);
1517 10712040 : auto term_in = Sij * rho_star * (Lij / dz) * mass_term_in * _Wij(i_gap, iz - 1);
1518 10712040 : auto inertia_term = term_out - term_in;
1519 10712040 : auto pressure_term = 2 * Utility::pow<2>(Sij) * DPij * rho_star;
1520 : auto time_term =
1521 10712040 : _TR * 2.0 * (_Wij(i_gap, iz) - _Wij_old(i_gap, iz)) * Lij * Sij * rho_star / _dt;
1522 :
1523 10712040 : _Wij_residual_matrix(i_gap, iz - 1 - iblock * _block_size) =
1524 10712040 : time_term + friction_term + inertia_term - pressure_term;
1525 : }
1526 : }
1527 : }
1528 : else
1529 : {
1530 : // Initializing to zero the elements of the lateral momentum assembly
1531 47374 : LibmeshPetscCall(MatZeroEntries(_cmc_time_derivative_mat));
1532 47374 : LibmeshPetscCall(MatZeroEntries(_cmc_advective_derivative_mat));
1533 47374 : LibmeshPetscCall(MatZeroEntries(_cmc_friction_force_mat));
1534 47374 : LibmeshPetscCall(MatZeroEntries(_cmc_pressure_force_mat));
1535 47374 : LibmeshPetscCall(VecZeroEntries(_cmc_time_derivative_rhs));
1536 47374 : LibmeshPetscCall(VecZeroEntries(_cmc_advective_derivative_rhs));
1537 47374 : LibmeshPetscCall(VecZeroEntries(_cmc_friction_force_rhs));
1538 47374 : LibmeshPetscCall(VecZeroEntries(_cmc_pressure_force_rhs));
1539 47374 : LibmeshPetscCall(MatZeroEntries(_cmc_sys_Wij_mat));
1540 47374 : LibmeshPetscCall(VecZeroEntries(_cmc_sys_Wij_rhs));
1541 47374 : const Real & pitch = _subchannel_mesh.getPitch();
1542 535924 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1543 : {
1544 488550 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
1545 488550 : auto iz_ind = iz - first_node;
1546 31320270 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
1547 : {
1548 30831720 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
1549 : unsigned int i_ch = chans.first;
1550 : unsigned int j_ch = chans.second;
1551 30831720 : auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1552 30831720 : auto * node_out_i = _subchannel_mesh.getChannelNode(i_ch, iz);
1553 30831720 : auto * node_in_j = _subchannel_mesh.getChannelNode(j_ch, iz - 1);
1554 30831720 : auto * node_out_j = _subchannel_mesh.getChannelNode(j_ch, iz);
1555 :
1556 : // inlet, outlet, and interpolated densities
1557 30831720 : auto rho_i_in = (*_rho_soln)(node_in_i);
1558 30831720 : auto rho_i_out = (*_rho_soln)(node_out_i);
1559 30831720 : auto rho_i_interp = computeInterpolatedValue(rho_i_out, rho_i_in, 0.5);
1560 30831720 : auto rho_j_in = (*_rho_soln)(node_in_j);
1561 30831720 : auto rho_j_out = (*_rho_soln)(node_out_j);
1562 30831720 : auto rho_j_interp = computeInterpolatedValue(rho_j_out, rho_j_in, 0.5);
1563 :
1564 : // inlet, outlet, and interpolated areas
1565 30831720 : auto S_i_in = (*_S_flow_soln)(node_in_i);
1566 30831720 : auto S_i_out = (*_S_flow_soln)(node_out_i);
1567 30831720 : auto S_j_in = (*_S_flow_soln)(node_in_j);
1568 30831720 : auto S_j_out = (*_S_flow_soln)(node_out_j);
1569 :
1570 : // Cross-sectional gap area
1571 30831720 : auto Sij = dz * _subchannel_mesh.getGapWidth(iz, i_gap);
1572 30831720 : auto Lij = pitch;
1573 :
1574 : // Figure out donor cell density
1575 : auto rho_star = 0.0;
1576 30831720 : if (_Wij(i_gap, iz) > 0.0)
1577 : rho_star = rho_i_interp;
1578 14413930 : else if (_Wij(i_gap, iz) < 0.0)
1579 : rho_star = rho_j_interp;
1580 : else
1581 362146 : rho_star = (rho_i_interp + rho_j_interp) / 2.0;
1582 :
1583 : // Assembling time derivative
1584 30831720 : PetscScalar time_factor = _TR * Lij * Sij * rho_star / _dt;
1585 30831720 : PetscInt row_td = i_gap + _n_gaps * iz_ind;
1586 30831720 : PetscInt col_td = i_gap + _n_gaps * iz_ind;
1587 30831720 : PetscScalar value_td = time_factor;
1588 30831720 : LibmeshPetscCall(MatSetValues(
1589 : _cmc_time_derivative_mat, 1, &row_td, 1, &col_td, &value_td, INSERT_VALUES));
1590 30831720 : PetscScalar value_td_rhs = time_factor * _Wij_old(i_gap, iz);
1591 30831720 : LibmeshPetscCall(
1592 : VecSetValues(_cmc_time_derivative_rhs, 1, &row_td, &value_td_rhs, INSERT_VALUES));
1593 :
1594 : // Assembling inertial term
1595 : PetscScalar Pe = 0.5;
1596 30831720 : auto alpha = computeInterpolationCoefficients(Pe);
1597 30831720 : auto mass_term_out = (*_mdot_soln)(node_out_i) / S_i_out / rho_i_out +
1598 30831720 : (*_mdot_soln)(node_out_j) / S_j_out / rho_j_out;
1599 30831720 : auto mass_term_in = (*_mdot_soln)(node_in_i) / S_i_in / rho_i_in +
1600 30831720 : (*_mdot_soln)(node_in_j) / S_j_in / rho_j_in;
1601 30831720 : auto term_out = Sij * rho_star * (Lij / dz) * mass_term_out / 2.0;
1602 30831720 : auto term_in = Sij * rho_star * (Lij / dz) * mass_term_in / 2.0;
1603 30831720 : if (iz == first_node)
1604 : {
1605 2912010 : PetscInt row_ad = i_gap + _n_gaps * iz_ind;
1606 2912010 : PetscScalar value_ad = term_in * alpha * _Wij(i_gap, iz - 1);
1607 2912010 : LibmeshPetscCall(
1608 : VecSetValues(_cmc_advective_derivative_rhs, 1, &row_ad, &value_ad, ADD_VALUES));
1609 :
1610 2912010 : PetscInt col_ad = i_gap + _n_gaps * iz_ind;
1611 2912010 : value_ad = -1.0 * term_in * (1.0 - alpha) + term_out * alpha;
1612 2912010 : LibmeshPetscCall(MatSetValues(
1613 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
1614 :
1615 2912010 : col_ad = i_gap + _n_gaps * (iz_ind + 1);
1616 2912010 : value_ad = term_out * (1.0 - alpha);
1617 2912010 : LibmeshPetscCall(MatSetValues(
1618 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
1619 : }
1620 27919710 : else if (iz == last_node)
1621 : {
1622 2912010 : PetscInt row_ad = i_gap + _n_gaps * iz_ind;
1623 2912010 : PetscInt col_ad = i_gap + _n_gaps * (iz_ind - 1);
1624 2912010 : PetscScalar value_ad = -1.0 * term_in * alpha;
1625 2912010 : LibmeshPetscCall(MatSetValues(
1626 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
1627 :
1628 2912010 : col_ad = i_gap + _n_gaps * iz_ind;
1629 2912010 : value_ad = -1.0 * term_in * (1.0 - alpha) + term_out * alpha;
1630 2912010 : LibmeshPetscCall(MatSetValues(
1631 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
1632 :
1633 2912010 : value_ad = -1.0 * term_out * (1.0 - alpha) * _Wij(i_gap, iz);
1634 2912010 : LibmeshPetscCall(
1635 : VecSetValues(_cmc_advective_derivative_rhs, 1, &row_ad, &value_ad, ADD_VALUES));
1636 : }
1637 : else
1638 : {
1639 25007700 : PetscInt row_ad = i_gap + _n_gaps * iz_ind;
1640 25007700 : PetscInt col_ad = i_gap + _n_gaps * (iz_ind - 1);
1641 25007700 : PetscScalar value_ad = -1.0 * term_in * alpha;
1642 25007700 : LibmeshPetscCall(MatSetValues(
1643 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
1644 :
1645 25007700 : col_ad = i_gap + _n_gaps * iz_ind;
1646 25007700 : value_ad = -1.0 * term_in * (1.0 - alpha) + term_out * alpha;
1647 25007700 : LibmeshPetscCall(MatSetValues(
1648 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
1649 :
1650 25007700 : col_ad = i_gap + _n_gaps * (iz_ind + 1);
1651 25007700 : value_ad = term_out * (1.0 - alpha);
1652 25007700 : LibmeshPetscCall(MatSetValues(
1653 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
1654 : }
1655 : // Assembling friction force
1656 30831720 : PetscInt row_ff = i_gap + _n_gaps * iz_ind;
1657 30831720 : PetscInt col_ff = i_gap + _n_gaps * iz_ind;
1658 30831720 : PetscScalar value_ff = _kij * std::abs(_Wij(i_gap, iz)) / 2.0;
1659 30831720 : LibmeshPetscCall(MatSetValues(
1660 : _cmc_friction_force_mat, 1, &row_ff, 1, &col_ff, &value_ff, INSERT_VALUES));
1661 :
1662 : // Assembling pressure force
1663 30831720 : alpha = computeInterpolationCoefficients(Pe);
1664 :
1665 30831720 : if (!_staggered_pressure_bool)
1666 : {
1667 30831720 : PetscScalar pressure_factor = Utility::pow<2>(Sij) * rho_star;
1668 30831720 : PetscInt row_pf = i_gap + _n_gaps * iz_ind;
1669 30831720 : PetscInt col_pf = i_ch + _n_channels * iz_ind;
1670 30831720 : PetscScalar value_pf = -1.0 * alpha * pressure_factor;
1671 30831720 : LibmeshPetscCall(
1672 : MatSetValues(_cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
1673 30831720 : col_pf = j_ch + _n_channels * iz_ind;
1674 30831720 : value_pf = alpha * pressure_factor;
1675 30831720 : LibmeshPetscCall(
1676 : MatSetValues(_cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
1677 :
1678 30831720 : if (iz == last_node)
1679 : {
1680 2912010 : PetscInt row_pf = i_gap + _n_gaps * iz_ind;
1681 2912010 : PetscScalar value_pf = (1.0 - alpha) * pressure_factor * (*_P_soln)(node_out_i);
1682 2912010 : LibmeshPetscCall(
1683 : VecSetValues(_cmc_pressure_force_rhs, 1, &row_pf, &value_pf, ADD_VALUES));
1684 2912010 : value_pf = -1.0 * (1.0 - alpha) * pressure_factor * (*_P_soln)(node_out_j);
1685 2912010 : LibmeshPetscCall(
1686 : VecSetValues(_cmc_pressure_force_rhs, 1, &row_pf, &value_pf, ADD_VALUES));
1687 : }
1688 : else
1689 : {
1690 27919710 : row_pf = i_gap + _n_gaps * iz_ind;
1691 27919710 : col_pf = i_ch + _n_channels * (iz_ind + 1);
1692 27919710 : value_pf = -1.0 * (1.0 - alpha) * pressure_factor;
1693 27919710 : LibmeshPetscCall(MatSetValues(
1694 : _cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
1695 27919710 : col_pf = j_ch + _n_channels * (iz_ind + 1);
1696 27919710 : value_pf = (1.0 - alpha) * pressure_factor;
1697 27919710 : LibmeshPetscCall(MatSetValues(
1698 : _cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
1699 : }
1700 : }
1701 : else
1702 : {
1703 0 : PetscScalar pressure_factor = Utility::pow<2>(Sij) * rho_star;
1704 0 : PetscInt row_pf = i_gap + _n_gaps * iz_ind;
1705 0 : PetscInt col_pf = i_ch + _n_channels * iz_ind;
1706 0 : PetscScalar value_pf = -1.0 * pressure_factor;
1707 0 : LibmeshPetscCall(
1708 : MatSetValues(_cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
1709 0 : col_pf = j_ch + _n_channels * iz_ind;
1710 0 : value_pf = pressure_factor;
1711 0 : LibmeshPetscCall(
1712 : MatSetValues(_cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
1713 : }
1714 : }
1715 : }
1716 : /// Assembling system
1717 47374 : LibmeshPetscCall(MatZeroEntries(_cmc_sys_Wij_mat));
1718 47374 : LibmeshPetscCall(VecZeroEntries(_cmc_sys_Wij_rhs));
1719 47374 : LibmeshPetscCall(MatAssemblyBegin(_cmc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1720 47374 : LibmeshPetscCall(MatAssemblyEnd(_cmc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1721 47374 : LibmeshPetscCall(MatAssemblyBegin(_cmc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1722 47374 : LibmeshPetscCall(MatAssemblyEnd(_cmc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1723 47374 : LibmeshPetscCall(MatAssemblyBegin(_cmc_friction_force_mat, MAT_FINAL_ASSEMBLY));
1724 47374 : LibmeshPetscCall(MatAssemblyEnd(_cmc_friction_force_mat, MAT_FINAL_ASSEMBLY));
1725 47374 : LibmeshPetscCall(MatAssemblyBegin(_cmc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1726 47374 : LibmeshPetscCall(MatAssemblyEnd(_cmc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1727 47374 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1728 47374 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1729 : // Matrix
1730 : #if !PETSC_VERSION_LESS_THAN(3, 15, 0)
1731 47374 : LibmeshPetscCall(
1732 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_time_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1733 47374 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1734 47374 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1735 47374 : LibmeshPetscCall(
1736 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_advective_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1737 47374 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1738 47374 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1739 47374 : LibmeshPetscCall(
1740 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_friction_force_mat, UNKNOWN_NONZERO_PATTERN));
1741 : #else
1742 : LibmeshPetscCall(
1743 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_time_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1744 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1745 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1746 : LibmeshPetscCall(
1747 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_advective_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1748 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1749 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1750 : LibmeshPetscCall(
1751 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_friction_force_mat, DIFFERENT_NONZERO_PATTERN));
1752 : #endif
1753 47374 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1754 47374 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
1755 : // RHS
1756 47374 : LibmeshPetscCall(VecAXPY(_cmc_sys_Wij_rhs, 1.0, _cmc_time_derivative_rhs));
1757 47374 : LibmeshPetscCall(VecAXPY(_cmc_sys_Wij_rhs, 1.0, _cmc_advective_derivative_rhs));
1758 47374 : LibmeshPetscCall(VecAXPY(_cmc_sys_Wij_rhs, 1.0, _cmc_friction_force_rhs));
1759 :
1760 47374 : if (_segregated_bool)
1761 : {
1762 : // Assembly the matrix system
1763 : Vec sol_holder_P;
1764 45662 : LibmeshPetscCall(createPetscVector(sol_holder_P, _block_size * _n_gaps));
1765 : Vec sol_holder_W;
1766 45662 : LibmeshPetscCall(createPetscVector(sol_holder_W, _block_size * _n_gaps));
1767 45662 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
1768 : _prodp, *_P_soln, first_node - 1, last_node - 1, _n_channels));
1769 45662 : LibmeshPetscCall(populateVectorFromDense<libMesh::DenseMatrix<Real>>(
1770 : _Wij_vec, _Wij, first_node, last_node, _n_gaps));
1771 45662 : LibmeshPetscCall(MatMult(_cmc_sys_Wij_mat, _Wij_vec, sol_holder_W));
1772 45662 : LibmeshPetscCall(VecAXPY(sol_holder_W, -1.0, _cmc_sys_Wij_rhs));
1773 45662 : LibmeshPetscCall(MatMult(_cmc_pressure_force_mat, _prodp, sol_holder_P));
1774 45662 : LibmeshPetscCall(VecAXPY(sol_holder_P, -1.0, _cmc_pressure_force_rhs));
1775 45662 : LibmeshPetscCall(VecAXPY(sol_holder_W, 1.0, sol_holder_P));
1776 : PetscScalar * xx;
1777 45662 : LibmeshPetscCall(VecGetArray(sol_holder_W, &xx));
1778 481932 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1779 : {
1780 436270 : auto iz_ind = iz - first_node;
1781 26612470 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
1782 : {
1783 26176200 : _Wij_residual_matrix(i_gap, iz - 1 - iblock * _block_size) = xx[iz_ind * _n_gaps + i_gap];
1784 : }
1785 : }
1786 45662 : LibmeshPetscCall(VecDestroy(&sol_holder_P));
1787 45662 : LibmeshPetscCall(VecDestroy(&sol_holder_W));
1788 : }
1789 : }
1790 65569 : }
1791 :
1792 : void
1793 67281 : SubChannel1PhaseProblem::computeWijPrime(int iblock)
1794 : {
1795 67281 : const unsigned int last_node = (iblock + 1) * _block_size;
1796 67281 : const unsigned int first_node = iblock * _block_size + 1;
1797 912541 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1798 : {
1799 845260 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
1800 47044540 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
1801 : {
1802 46199280 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
1803 : unsigned int i_ch = chans.first;
1804 : unsigned int j_ch = chans.second;
1805 46199280 : auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1806 46199280 : auto * node_out_i = _subchannel_mesh.getChannelNode(i_ch, iz);
1807 46199280 : auto * node_in_j = _subchannel_mesh.getChannelNode(j_ch, iz - 1);
1808 46199280 : auto * node_out_j = _subchannel_mesh.getChannelNode(j_ch, iz);
1809 46199280 : auto Si_in = (*_S_flow_soln)(node_in_i);
1810 46199280 : auto Sj_in = (*_S_flow_soln)(node_in_j);
1811 46199280 : auto Si_out = (*_S_flow_soln)(node_out_i);
1812 46199280 : auto Sj_out = (*_S_flow_soln)(node_out_j);
1813 46199280 : auto gap = _subchannel_mesh.getGapWidth(iz, i_gap);
1814 46199280 : auto Sij = dz * gap;
1815 : auto avg_massflux =
1816 46199280 : 0.5 * (((*_mdot_soln)(node_in_i) + (*_mdot_soln)(node_in_j)) / (Si_in + Sj_in) +
1817 46199280 : ((*_mdot_soln)(node_out_i) + (*_mdot_soln)(node_out_j)) / (Si_out + Sj_out));
1818 46199280 : auto beta = computeMixingParameter(i_gap, iz);
1819 :
1820 46199280 : if (!_implicit_bool)
1821 : {
1822 10712040 : _WijPrime(i_gap, iz) = beta * avg_massflux * Sij;
1823 : }
1824 : else
1825 : {
1826 35487240 : auto iz_ind = iz - first_node;
1827 35487240 : PetscScalar base_value = beta * 0.5 * Sij;
1828 :
1829 : // Bottom values
1830 35487240 : if (iz == first_node)
1831 : {
1832 3084300 : PetscScalar value_tl = -1.0 * base_value / (Si_in + Sj_in) *
1833 3084300 : ((*_mdot_soln)(node_in_i) + (*_mdot_soln)(node_in_j));
1834 3084300 : PetscInt row = i_gap + _n_gaps * iz_ind;
1835 3084300 : LibmeshPetscCall(
1836 : VecSetValues(_amc_turbulent_cross_flows_rhs, 1, &row, &value_tl, INSERT_VALUES));
1837 : }
1838 : else
1839 : {
1840 32402940 : PetscScalar value_tl = base_value / (Si_in + Sj_in);
1841 32402940 : PetscInt row = i_gap + _n_gaps * iz_ind;
1842 :
1843 32402940 : PetscInt col_ich = i_ch + _n_channels * (iz_ind - 1);
1844 32402940 : LibmeshPetscCall(MatSetValues(
1845 : _amc_turbulent_cross_flows_mat, 1, &row, 1, &col_ich, &value_tl, INSERT_VALUES));
1846 :
1847 32402940 : PetscInt col_jch = j_ch + _n_channels * (iz_ind - 1);
1848 32402940 : LibmeshPetscCall(MatSetValues(
1849 : _amc_turbulent_cross_flows_mat, 1, &row, 1, &col_jch, &value_tl, INSERT_VALUES));
1850 : }
1851 :
1852 : // Top values
1853 35487240 : PetscScalar value_bl = base_value / (Si_out + Sj_out);
1854 35487240 : PetscInt row = i_gap + _n_gaps * iz_ind;
1855 :
1856 35487240 : PetscInt col_ich = i_ch + _n_channels * iz_ind;
1857 35487240 : LibmeshPetscCall(MatSetValues(
1858 : _amc_turbulent_cross_flows_mat, 1, &row, 1, &col_ich, &value_bl, INSERT_VALUES));
1859 :
1860 35487240 : PetscInt col_jch = j_ch + _n_channels * iz_ind;
1861 35487240 : LibmeshPetscCall(MatSetValues(
1862 : _amc_turbulent_cross_flows_mat, 1, &row, 1, &col_jch, &value_bl, INSERT_VALUES));
1863 : }
1864 : }
1865 : }
1866 :
1867 67281 : if (_implicit_bool)
1868 : {
1869 49086 : LibmeshPetscCall(MatAssemblyBegin(_amc_turbulent_cross_flows_mat, MAT_FINAL_ASSEMBLY));
1870 49086 : LibmeshPetscCall(MatAssemblyEnd(_amc_turbulent_cross_flows_mat, MAT_FINAL_ASSEMBLY));
1871 :
1872 : /// Update turbulent crossflow
1873 : Vec loc_prod;
1874 : Vec loc_Wij;
1875 49086 : LibmeshPetscCall(VecDuplicate(_amc_sys_mdot_rhs, &loc_prod));
1876 49086 : LibmeshPetscCall(VecDuplicate(_Wij_vec, &loc_Wij));
1877 49086 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
1878 : loc_prod, *_mdot_soln, first_node, last_node, _n_channels));
1879 49086 : LibmeshPetscCall(MatMult(_amc_turbulent_cross_flows_mat, loc_prod, loc_Wij));
1880 49086 : LibmeshPetscCall(VecAXPY(loc_Wij, -1.0, _amc_turbulent_cross_flows_rhs));
1881 49086 : LibmeshPetscCall(populateDenseFromVector<libMesh::DenseMatrix<Real>>(
1882 : loc_Wij, _WijPrime, first_node, last_node, _n_gaps));
1883 49086 : LibmeshPetscCall(VecDestroy(&loc_prod));
1884 49086 : LibmeshPetscCall(VecDestroy(&loc_Wij));
1885 : }
1886 67281 : }
1887 :
1888 : Real
1889 46199280 : SubChannel1PhaseProblem::computeMixingParameter(unsigned int i_gap, unsigned int iz) const
1890 : {
1891 46199280 : auto beta = _mixing_closure->computeMixingParameter(i_gap, iz);
1892 46199280 : if (!std::isfinite(beta) || beta < 0.0)
1893 0 : mooseError(name(),
1894 : ": Mixing closure returned invalid beta = ",
1895 : beta,
1896 : " for gap ",
1897 : i_gap,
1898 : " at axial index ",
1899 : iz,
1900 : ". Beta must be finite and non-negative.");
1901 :
1902 46199280 : return beta;
1903 : }
1904 :
1905 : Real
1906 1763760 : SubChannel1PhaseProblem::computeSweepFlowMixingParameter(unsigned int i_gap, unsigned int iz) const
1907 : {
1908 1763760 : auto beta = _mixing_closure->computeSweepFlowMixingParameter(i_gap, iz);
1909 1763760 : if (!std::isfinite(beta) || beta < 0.0)
1910 0 : mooseError(name(),
1911 : ": Mixing closure returned invalid sweep-flow coefficient = ",
1912 : beta,
1913 : " for gap ",
1914 : i_gap,
1915 : " at axial index ",
1916 : iz,
1917 : ". sweep-flow coefficient must be finite and non-negative.");
1918 :
1919 1763760 : return beta;
1920 : }
1921 :
1922 : libMesh::DenseVector<Real>
1923 63857 : SubChannel1PhaseProblem::residualFunction(int iblock, libMesh::DenseVector<Real> solution)
1924 : {
1925 63857 : const unsigned int last_node = (iblock + 1) * _block_size;
1926 63857 : const unsigned int first_node = iblock * _block_size + 1;
1927 63857 : libMesh::DenseVector<Real> Wij_residual_vector(_n_gaps * _block_size, 0.0);
1928 : // Assign the solution to the cross-flow matrix
1929 : int i = 0;
1930 804557 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1931 : {
1932 37628940 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
1933 : {
1934 36888240 : _Wij(i_gap, iz) = solution(i);
1935 36888240 : i++;
1936 : }
1937 : }
1938 :
1939 : // Calculating sum of crossflows
1940 63857 : computeSumWij(iblock);
1941 : // Solving axial flux
1942 63857 : computeMdot(iblock);
1943 : // Calculation of turbulent Crossflow
1944 63857 : computeWijPrime(iblock);
1945 : // Solving for Pressure Drop
1946 63857 : computeDP(iblock);
1947 : // Solving for pressure
1948 63857 : computeP(iblock);
1949 : // Populating lateral crossflow residual matrix
1950 63857 : computeWijResidual(iblock);
1951 :
1952 : // Turn the residual matrix into a residual vector
1953 804557 : for (unsigned int iz = 0; iz < _block_size; iz++)
1954 : {
1955 37628940 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
1956 : {
1957 36888240 : int i = _n_gaps * iz + i_gap; // column wise transfer
1958 36888240 : Wij_residual_vector(i) = _Wij_residual_matrix(i_gap, iz);
1959 : }
1960 : }
1961 63857 : return Wij_residual_vector;
1962 : }
1963 :
1964 : PetscErrorCode
1965 312 : SubChannel1PhaseProblem::petscSnesSolver(int iblock,
1966 : const libMesh::DenseVector<Real> & solution,
1967 : libMesh::DenseVector<Real> & root)
1968 : {
1969 : SNES snes;
1970 : KSP ksp;
1971 : PC pc;
1972 : Vec x, r;
1973 : PetscScalar * xx;
1974 :
1975 : PetscFunctionBegin;
1976 312 : LibmeshPetscCall(SNESCreate(PETSC_COMM_SELF, &snes));
1977 312 : LibmeshPetscCall(VecCreate(PETSC_COMM_SELF, &x));
1978 312 : LibmeshPetscCall(VecSetSizes(x, PETSC_DECIDE, _block_size * _n_gaps));
1979 312 : LibmeshPetscCall(VecSetFromOptions(x));
1980 312 : LibmeshPetscCall(VecDuplicate(x, &r));
1981 :
1982 : #if PETSC_VERSION_LESS_THAN(3, 13, 0)
1983 : LibmeshPetscCall(PetscOptionsSetValue(PETSC_NULL, "-snes_mf", PETSC_NULL));
1984 : #else
1985 312 : LibmeshPetscCall(SNESSetUseMatrixFree(snes, PETSC_FALSE, PETSC_TRUE));
1986 : #endif
1987 : Ctx ctx;
1988 312 : ctx.iblock = iblock;
1989 312 : ctx.schp = this;
1990 312 : LibmeshPetscCall(SNESSetFunction(snes, r, formFunction, &ctx));
1991 312 : LibmeshPetscCall(SNESGetKSP(snes, &ksp));
1992 312 : LibmeshPetscCall(KSPGetPC(ksp, &pc));
1993 312 : LibmeshPetscCall(PCSetType(pc, PCNONE));
1994 312 : LibmeshPetscCall(KSPSetTolerances(ksp, _rtol, _atol, _dtol, _maxit));
1995 312 : LibmeshPetscCall(SNESSetFromOptions(snes));
1996 312 : LibmeshPetscCall(VecGetArray(x, &xx));
1997 219732 : for (unsigned int i = 0; i < _block_size * _n_gaps; i++)
1998 : {
1999 219420 : xx[i] = solution(i);
2000 : }
2001 312 : LibmeshPetscCall(VecRestoreArray(x, &xx));
2002 :
2003 312 : LibmeshPetscCall(SNESSolve(snes, NULL, x));
2004 312 : LibmeshPetscCall(VecGetArray(x, &xx));
2005 219732 : for (unsigned int i = 0; i < _block_size * _n_gaps; i++)
2006 219420 : root(i) = xx[i];
2007 :
2008 312 : LibmeshPetscCall(VecRestoreArray(x, &xx));
2009 312 : LibmeshPetscCall(VecDestroy(&x));
2010 312 : LibmeshPetscCall(VecDestroy(&r));
2011 312 : LibmeshPetscCall(SNESDestroy(&snes));
2012 312 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
2013 : }
2014 :
2015 : PetscErrorCode
2016 1639 : SubChannel1PhaseProblem::solveAndPopulateEnthalpy(
2017 : Mat A, Vec rhs, unsigned int first_node, unsigned int last_node, const char * ksp_prefix)
2018 : {
2019 : PetscFunctionBegin;
2020 :
2021 : // Create solution vector with rhs layout
2022 1639 : Vec x = nullptr;
2023 1639 : LibmeshPetscCall(VecDuplicate(rhs, &x));
2024 :
2025 : // KSP setup
2026 1639 : KSP ksp = nullptr;
2027 1639 : PC pc = nullptr;
2028 1639 : LibmeshPetscCall(KSPCreate(PETSC_COMM_SELF, &ksp));
2029 1639 : LibmeshPetscCall(KSPSetOperators(ksp, A, A));
2030 1639 : LibmeshPetscCall(KSPGetPC(ksp, &pc));
2031 1639 : LibmeshPetscCall(PCSetType(pc, PCJACOBI));
2032 1639 : LibmeshPetscCall(KSPSetTolerances(ksp, _rtol, _atol, _dtol, _maxit));
2033 1639 : if (ksp_prefix && *ksp_prefix)
2034 1639 : LibmeshPetscCall(KSPSetOptionsPrefix(ksp, ksp_prefix));
2035 1639 : LibmeshPetscCall(KSPSetFromOptions(ksp));
2036 :
2037 : // Solve
2038 1639 : LibmeshPetscCall(KSPSolve(ksp, rhs, x));
2039 :
2040 : // Scatter to _h_soln with sanity checks
2041 1639 : PetscScalar * xx = nullptr;
2042 1639 : LibmeshPetscCall(VecGetArray(x, &xx));
2043 53564 : for (unsigned int iz = first_node; iz <= last_node; ++iz)
2044 : {
2045 51925 : const unsigned int iz_ind = iz - first_node;
2046 3198625 : for (unsigned int i_ch = 0; i_ch < _n_channels; ++i_ch)
2047 : {
2048 3146700 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
2049 3146700 : const PetscScalar h_out = xx[iz_ind * _n_channels + i_ch];
2050 3146700 : if (h_out < 0.0)
2051 0 : mooseError(
2052 : name(), " : Calculation of negative Enthalpy h_out = ", h_out, " Axial Level = ", iz);
2053 3146700 : _h_soln->set(node_out, h_out);
2054 : }
2055 : }
2056 1639 : LibmeshPetscCall(VecRestoreArray(x, &xx));
2057 :
2058 : // Cleanup
2059 1639 : LibmeshPetscCall(KSPDestroy(&ksp));
2060 1639 : LibmeshPetscCall(VecDestroy(&x));
2061 :
2062 1639 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
2063 : }
2064 :
2065 : Real
2066 2990430 : SubChannel1PhaseProblem::computeAddedHeatDuct(unsigned int i_ch, unsigned int iz) const
2067 : {
2068 : mooseAssert(iz > 0, "Trapezoidal rule requires starting at index 1 at least");
2069 2990430 : if (_duct_mesh_exist)
2070 : {
2071 1150800 : auto subch_type = _subchannel_mesh.getSubchannelType(i_ch);
2072 1150800 : if (subch_type == EChannelType::EDGE || subch_type == EChannelType::CORNER)
2073 : {
2074 279600 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
2075 279600 : auto * node_in_chan = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
2076 279600 : auto * node_out_chan = _subchannel_mesh.getChannelNode(i_ch, iz);
2077 279600 : auto * node_in_duct = _subchannel_mesh.getDuctNodeFromChannel(node_in_chan);
2078 279600 : auto * node_out_duct = _subchannel_mesh.getDuctNodeFromChannel(node_out_chan);
2079 279600 : auto heat_rate_in = (*_duct_heat_flux_soln)(node_in_duct);
2080 279600 : auto heat_rate_out = (*_duct_heat_flux_soln)(node_out_duct);
2081 279600 : auto width = getSubChannelPeripheralDuctWidth(i_ch);
2082 279600 : return 0.5 * (heat_rate_in + heat_rate_out) * dz * width;
2083 : }
2084 : else
2085 : {
2086 : return 0.0;
2087 : }
2088 : }
2089 : else
2090 : {
2091 : return 0.0;
2092 : }
2093 : }
2094 :
2095 : PetscErrorCode
2096 1712 : SubChannel1PhaseProblem::implicitPetscSolve(int iblock)
2097 : {
2098 : PetscFunctionBegin;
2099 : // ---------- helper functions -----------------------------
2100 44512 : auto V = [&](const std::string & s)
2101 : {
2102 44512 : if (_verbose_subchannel)
2103 33098 : _console << s << std::endl;
2104 46224 : };
2105 :
2106 15408 : auto DupMatAssembled = [&](Mat src, Mat * dst)
2107 : {
2108 15408 : if (src)
2109 : {
2110 10272 : LibmeshPetscCall(MatDuplicate(src, MAT_COPY_VALUES, dst));
2111 10272 : LibmeshPetscCall(MatAssemblyBegin(*dst, MAT_FINAL_ASSEMBLY));
2112 10272 : LibmeshPetscCall(MatAssemblyEnd(*dst, MAT_FINAL_ASSEMBLY));
2113 : }
2114 : else
2115 5136 : *dst = NULL;
2116 15408 : };
2117 :
2118 5136 : auto DupVecCopy = [&](Vec src, Vec * dst)
2119 : {
2120 5136 : LibmeshPetscCall(VecDuplicate(src, dst));
2121 5136 : LibmeshPetscCall(VecCopy(src, *dst));
2122 5136 : };
2123 :
2124 : const PetscInt Q = 3; // [mass conservation, axial momentum, cross momentum]
2125 :
2126 : // small indexer
2127 15408 : auto Idx = [&](PetscInt r, PetscInt c) { return Q * r + c; };
2128 :
2129 : // arrays that MUST be declared before lambdas use them
2130 1712 : std::vector<Mat> mat_array(Q * Q, NULL);
2131 1712 : std::vector<Vec> vec_array(Q, NULL);
2132 :
2133 : // generic assembler for one governing equation row in the nested matrix
2134 5136 : auto AssembleEquation = [&](PetscInt f,
2135 : Mat A0,
2136 : Mat A1,
2137 : Mat A2, // three blocks in row f (can be nullptr)
2138 : Vec rhs, // base RHS for equation f
2139 : Vec rhs_add, // optional extra RHS to add (can be nullptr)
2140 : const char * label) // e.g. "Mass", "Lin mom", "Cross mom"
2141 : {
2142 5136 : DupMatAssembled(A0, &mat_array[Idx(f, 0)]);
2143 5136 : DupMatAssembled(A1, &mat_array[Idx(f, 1)]);
2144 5136 : DupMatAssembled(A2, &mat_array[Idx(f, 2)]);
2145 5136 : DupVecCopy(rhs, &vec_array[f]);
2146 5136 : if (rhs_add)
2147 3424 : LibmeshPetscCall(VecAXPY(vec_array[f], 1.0, rhs_add));
2148 10272 : V(std::string(label) + " system assembled");
2149 5136 : };
2150 :
2151 : // -----------------------------------------------------------------------------
2152 : // Helper lambda that applies per-equation under-relaxation by modifying BOTH the
2153 : // matrix block and the RHS for that equation.
2154 : //
2155 : // Specifically, with A_ff for the equation and D = diag(A_ff):
2156 : // 1) Matrix diagonal scaling: D <- D / alpha, then A_ff's diagonal is replaced
2157 : // with D. For alpha < 1 this increases diagonal dominance (more damping).
2158 : // 2) RHS blending with the previous solution x_old:
2159 : // rhs_f <- rhs_f + (1 - alpha) * (D / alpha) * x_old
2160 : // where x_old is provided by the caller via `populate(work)`.
2161 : //
2162 : // Net effect: the solved x satisfies
2163 : // A_ff x = rhs_f_original + ((1 - alpha)/alpha) * D * (x_old - x),
2164 : // which damps updates toward x_old without changing the converged solution.
2165 : // -----------------------------------------------------------------------------
2166 : auto RelaxEquation =
2167 5136 : [&](Mat A_ff, Vec rhs_f, Vec like_vec, Vec work, PetscScalar alpha, auto && populate)
2168 : {
2169 5136 : Vec d = nullptr;
2170 5136 : LibmeshPetscCall(VecDuplicate(like_vec, &d));
2171 :
2172 : // 1) A_ff: diag <- diag / alpha
2173 5136 : LibmeshPetscCall(MatGetDiagonal(A_ff, d));
2174 5136 : LibmeshPetscCall(VecScale(d, 1.0 / alpha));
2175 5136 : LibmeshPetscCall(MatDiagonalSet(A_ff, d, INSERT_VALUES));
2176 :
2177 : // 2) work <- x_old (caller-provided populator)
2178 5136 : LibmeshPetscCall(populate(work));
2179 :
2180 : // 3) rhs_f += (1 - alpha) * (diag .* work)
2181 5136 : LibmeshPetscCall(VecScale(d, (1.0 - alpha)));
2182 5136 : LibmeshPetscCall(VecPointwiseMult(work, work, d));
2183 5136 : LibmeshPetscCall(VecAXPY(rhs_f, 1.0, work));
2184 :
2185 5136 : LibmeshPetscCall(VecDestroy(&d));
2186 6848 : };
2187 :
2188 : // indices
2189 1712 : const unsigned int first_node = iblock * _block_size + 1;
2190 1712 : const unsigned int last_node = (iblock + 1) * _block_size;
2191 :
2192 : // ---------- assemble per-block operators -----------------
2193 1712 : computeSumWij(iblock);
2194 1712 : computeMdot(iblock);
2195 1712 : computeWijPrime(iblock);
2196 1712 : computeDP(iblock);
2197 1712 : computeP(iblock);
2198 1712 : computeWijResidual(iblock);
2199 :
2200 1712 : V("Starting nested system.");
2201 :
2202 : // Populate nested matrix with the individual physics
2203 : // equation 0: Mass conservation
2204 1712 : AssembleEquation(/*f=*/0,
2205 : /*A0=*/_mc_axial_convection_mat,
2206 : /*A1=*/nullptr,
2207 : /*A2=*/_mc_sumWij_mat,
2208 : /*rhs=*/_mc_axial_convection_rhs,
2209 : /*rhs_add=*/nullptr,
2210 : /*label=*/"Mass");
2211 :
2212 : // equation 1: Axial momentum conservation
2213 1712 : AssembleEquation(/*f=*/1,
2214 : /*A0=*/_amc_sys_mdot_mat,
2215 : /*A1=*/_amc_pressure_force_mat,
2216 : /*A2=*/nullptr,
2217 : /*rhs=*/_amc_pressure_force_rhs,
2218 : /*rhs_add=*/_amc_sys_mdot_rhs,
2219 : /*label=*/"Lin mom");
2220 :
2221 : // equation 2: Cross momentum conservation
2222 1712 : AssembleEquation(/*f=*/2,
2223 : /*A0=*/nullptr,
2224 : /*A1=*/_cmc_pressure_force_mat,
2225 : /*A2=*/_cmc_sys_Wij_mat,
2226 : /*rhs=*/_cmc_sys_Wij_rhs,
2227 : /*rhs_add=*/_cmc_pressure_force_rhs,
2228 : /*label=*/"Cross mom");
2229 :
2230 : // ========================== Relaxation ====================
2231 : if (true)
2232 : {
2233 1712 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
2234 : _prod, *_mdot_soln, first_node, last_node, _n_channels));
2235 :
2236 : Vec mdot_estimate;
2237 1712 : LibmeshPetscCall(createPetscVector(mdot_estimate, _block_size * _n_channels));
2238 : Vec pmat_diag;
2239 1712 : LibmeshPetscCall(createPetscVector(pmat_diag, _block_size * _n_channels));
2240 : Vec p_estimate;
2241 1712 : LibmeshPetscCall(createPetscVector(p_estimate, _block_size * _n_channels));
2242 : Vec unity_vec;
2243 1712 : LibmeshPetscCall(createPetscVector(unity_vec, _block_size * _n_channels));
2244 1712 : LibmeshPetscCall(VecSet(unity_vec, 1.0));
2245 : Vec sol_holder_P;
2246 1712 : LibmeshPetscCall(createPetscVector(sol_holder_P, _block_size * _n_gaps));
2247 : Vec unity_vec_Wij;
2248 1712 : LibmeshPetscCall(createPetscVector(unity_vec_Wij, _block_size * _n_gaps));
2249 1712 : LibmeshPetscCall(VecSet(unity_vec_Wij, 1.0));
2250 : Vec _Wij_loc_vec;
2251 1712 : LibmeshPetscCall(createPetscVector(_Wij_loc_vec, _block_size * _n_gaps));
2252 : Vec _Wij_old_loc_vec;
2253 1712 : LibmeshPetscCall(createPetscVector(_Wij_old_loc_vec, _block_size * _n_gaps));
2254 :
2255 : // ---- scale estimates ----
2256 : // mdot_estimate = A(1,0) * mdot
2257 1712 : LibmeshPetscCall(MatMult(mat_array[Q /* (1,0) */], _prod, mdot_estimate));
2258 :
2259 : // p_estimate = mdot_est / (diag(A(1,1)) + eps)
2260 1712 : LibmeshPetscCall(MatGetDiagonal(mat_array[Q + 1], pmat_diag));
2261 1712 : LibmeshPetscCall(VecAXPY(pmat_diag, 1e-10, unity_vec));
2262 1712 : LibmeshPetscCall(VecPointwiseDivide(p_estimate, mdot_estimate, pmat_diag));
2263 :
2264 : // sol_holder_P = A(2,1) * p_estimate - rhs_cmc_pressure
2265 1712 : LibmeshPetscCall(MatMult(mat_array[2 * Q + 1], p_estimate, sol_holder_P));
2266 1712 : LibmeshPetscCall(VecAXPY(sol_holder_P, -1.0, _cmc_pressure_force_rhs));
2267 :
2268 : // sumWij_loc from sol_holder_P (accumulate)
2269 : Vec sumWij_loc;
2270 1712 : LibmeshPetscCall(createPetscVector(sumWij_loc, _block_size * _n_channels));
2271 53992 : for (unsigned int iz = first_node; iz <= last_node; ++iz)
2272 : {
2273 52280 : const auto iz_ind = iz - first_node;
2274 3241310 : for (unsigned int i_ch = 0; i_ch < _n_channels; ++i_ch)
2275 : {
2276 3189030 : PetscScalar sumWij = 0.0;
2277 : unsigned int counter = 0;
2278 12500070 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
2279 : {
2280 9311040 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
2281 : unsigned int i_ch_loc = chans.first;
2282 9311040 : PetscInt row_vec = i_ch_loc + _n_channels * iz_ind;
2283 : PetscScalar loc_Wij_value;
2284 9311040 : LibmeshPetscCall(VecGetValues(sol_holder_P, 1, &row_vec, &loc_Wij_value));
2285 9311040 : sumWij += _subchannel_mesh.getCrossflowSign(i_ch, counter) * loc_Wij_value;
2286 9311040 : counter++;
2287 : }
2288 3189030 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
2289 3189030 : LibmeshPetscCall(VecSetValues(sumWij_loc, 1, &row_vec, &sumWij, INSERT_VALUES));
2290 : }
2291 : }
2292 1712 : LibmeshPetscCall(VecAssemblyBegin(sumWij_loc));
2293 1712 : LibmeshPetscCall(VecAssemblyEnd(sumWij_loc));
2294 :
2295 : // ---- robust scale measurements ----
2296 : PetscScalar min_mdot;
2297 1712 : LibmeshPetscCall(VecAbs(_prod));
2298 1712 : LibmeshPetscCall(VecMin(_prod, NULL, &min_mdot));
2299 1712 : V("Minimum estimated mdot: " + std::to_string(min_mdot));
2300 :
2301 1712 : LibmeshPetscCall(VecAbs(sumWij_loc));
2302 1712 : LibmeshPetscCall(VecMax(sumWij_loc, NULL, &_max_sumWij));
2303 3418 : _max_sumWij = std::max(1e-10, _max_sumWij);
2304 1712 : V("Maximum estimated Wij: " + std::to_string(_max_sumWij));
2305 :
2306 1712 : LibmeshPetscCall(populateVectorFromDense<libMesh::DenseMatrix<Real>>(
2307 : _Wij_loc_vec, _Wij, first_node, last_node, _n_gaps));
2308 1712 : LibmeshPetscCall(VecAbs(_Wij_loc_vec));
2309 1712 : LibmeshPetscCall(populateVectorFromDense<libMesh::DenseMatrix<Real>>(
2310 : _Wij_old_loc_vec, _Wij_old, first_node, last_node, _n_gaps));
2311 1712 : LibmeshPetscCall(VecAbs(_Wij_old_loc_vec));
2312 1712 : LibmeshPetscCall(VecAXPY(_Wij_loc_vec, -1.0, _Wij_old_loc_vec));
2313 :
2314 : PetscScalar relax_factor;
2315 1712 : LibmeshPetscCall(VecAbs(_Wij_loc_vec));
2316 : #if !PETSC_VERSION_LESS_THAN(3, 16, 0)
2317 1712 : LibmeshPetscCall(VecMean(_Wij_loc_vec, &relax_factor));
2318 : #else
2319 : VecSum(_Wij_loc_vec, &relax_factor);
2320 : relax_factor /= _block_size * _n_gaps;
2321 : #endif
2322 1712 : relax_factor = relax_factor / _max_sumWij + 0.5;
2323 1712 : V("Relax base value: " + std::to_string(relax_factor));
2324 :
2325 : // ---- crossflow resistance inflation ----
2326 : const PetscScalar resistance_relaxation = 0.9;
2327 1712 : _added_K = _max_sumWij / min_mdot;
2328 1712 : V("New cross resistance: " + std::to_string(_added_K));
2329 1712 : _added_K = (_added_K * resistance_relaxation + (1.0 - resistance_relaxation) * _added_K_old) *
2330 : relax_factor;
2331 1712 : V("Relaxed cross resistance: " + std::to_string(_added_K));
2332 :
2333 : // Snap-up lower-bounding
2334 1712 : if (_added_K < 10 && _added_K >= 1.0)
2335 315 : _added_K = 1.0;
2336 1712 : if (_added_K < 1.0 && _added_K >= 0.1)
2337 823 : _added_K = 0.5;
2338 1712 : if (_added_K < 0.1 && _added_K >= 0.01)
2339 214 : _added_K = 1. / 3.;
2340 1712 : if (_added_K < 1e-2 && _added_K >= 1e-3)
2341 0 : _added_K = 0.1;
2342 1712 : V("Actual added cross resistance: " + std::to_string(_added_K));
2343 1712 : LibmeshPetscCall(VecScale(unity_vec_Wij, _added_K));
2344 1712 : _added_K_old = _added_K;
2345 :
2346 1712 : LibmeshPetscCall(MatDiagonalSet(mat_array[2 * Q + 2], unity_vec_Wij, ADD_VALUES));
2347 :
2348 : // ---- cleanup temp vectors used above ----
2349 1712 : LibmeshPetscCall(VecDestroy(&mdot_estimate));
2350 1712 : LibmeshPetscCall(VecDestroy(&pmat_diag));
2351 1712 : LibmeshPetscCall(VecDestroy(&unity_vec));
2352 1712 : LibmeshPetscCall(VecDestroy(&p_estimate));
2353 1712 : LibmeshPetscCall(VecDestroy(&sol_holder_P));
2354 1712 : LibmeshPetscCall(VecDestroy(&unity_vec_Wij));
2355 1712 : LibmeshPetscCall(VecDestroy(&sumWij_loc));
2356 1712 : LibmeshPetscCall(VecDestroy(&_Wij_loc_vec));
2357 1712 : LibmeshPetscCall(VecDestroy(&_Wij_old_loc_vec));
2358 :
2359 : // ---- per-equation under-relaxation ----
2360 : const PetscScalar relaxation_factor_mdot = 1.0;
2361 : const PetscScalar relaxation_factor_P = 1.0;
2362 : const PetscScalar relaxation_factor_Wij = 0.1;
2363 :
2364 3424 : V("Relax mdot: " + std::to_string(relaxation_factor_mdot));
2365 3424 : V("Relax P: " + std::to_string(relaxation_factor_P));
2366 1712 : V("Relax Wij: " + std::to_string(relaxation_factor_Wij));
2367 :
2368 : // mdot
2369 1712 : RelaxEquation(mat_array[Idx(0, 0)],
2370 : vec_array[0],
2371 : vec_array[0],
2372 : _prod,
2373 : relaxation_factor_mdot,
2374 1712 : [&](Vec dst)
2375 : {
2376 1712 : return populateVectorFromHandle<SolutionHandle>(
2377 1712 : dst, *_mdot_soln, first_node, last_node, _n_channels);
2378 : });
2379 1712 : V("mdot relaxed");
2380 :
2381 : // pressure
2382 1712 : RelaxEquation(mat_array[Idx(1, 1)],
2383 : vec_array[1],
2384 : vec_array[1],
2385 : _prod,
2386 : relaxation_factor_P,
2387 1712 : [&](Vec dst)
2388 : {
2389 1712 : return populateVectorFromHandle<SolutionHandle>(
2390 1712 : dst, *_P_soln, first_node, last_node, _n_channels);
2391 : });
2392 1712 : V("P relaxed");
2393 :
2394 : // crossflow
2395 1712 : RelaxEquation(mat_array[Idx(2, 2)],
2396 : vec_array[2],
2397 : vec_array[2],
2398 : _Wij_vec,
2399 : relaxation_factor_Wij,
2400 1712 : [&](Vec dst)
2401 : {
2402 1712 : return populateVectorFromDense<libMesh::DenseMatrix<Real>>(
2403 1712 : dst, _Wij, first_node, last_node, _n_gaps);
2404 : });
2405 1712 : V("Wij relaxed");
2406 : }
2407 3424 : V("Linear solver relaxed");
2408 :
2409 : // ======================== Create and configure KSP =========================
2410 : Mat A_nest;
2411 : Vec b_nest;
2412 : Vec x_nest;
2413 1712 : LibmeshPetscCall(MatCreateNest(PETSC_COMM_SELF, Q, NULL, Q, NULL, mat_array.data(), &A_nest));
2414 1712 : LibmeshPetscCall(VecCreateNest(PETSC_COMM_SELF, Q, NULL, vec_array.data(), &b_nest));
2415 1712 : V("Nested system created");
2416 :
2417 : KSP ksp;
2418 : PC pc;
2419 1712 : LibmeshPetscCall(KSPCreate(PETSC_COMM_SELF, &ksp));
2420 1712 : LibmeshPetscCall(KSPSetType(ksp, KSPFGMRES));
2421 1712 : LibmeshPetscCall(KSPSetOperators(ksp, A_nest, A_nest));
2422 1712 : LibmeshPetscCall(KSPGetPC(ksp, &pc));
2423 1712 : LibmeshPetscCall(PCSetType(pc, PCFIELDSPLIT));
2424 1712 : LibmeshPetscCall(KSPSetTolerances(ksp, _rtol, _atol, _dtol, _maxit));
2425 :
2426 : // split equations
2427 1712 : std::vector<IS> rows(Q);
2428 1712 : LibmeshPetscCall(MatNestGetISs(A_nest, rows.data(), NULL));
2429 6848 : for (PetscInt j = 0; j < Q; ++j)
2430 : {
2431 : IS part;
2432 5136 : LibmeshPetscCall(ISDuplicate(rows[j], &part));
2433 5136 : LibmeshPetscCall(PCFieldSplitSetIS(pc, NULL, part));
2434 5136 : LibmeshPetscCall(ISDestroy(&part));
2435 : }
2436 1712 : V("Linear solver assembled");
2437 :
2438 : // ============================== Solve =====================================
2439 1712 : LibmeshPetscCall(VecDuplicate(b_nest, &x_nest));
2440 1712 : LibmeshPetscCall(VecSet(x_nest, 0.0));
2441 1712 : LibmeshPetscCall(KSPSolve(ksp, b_nest, x_nest));
2442 :
2443 : // destroy solver containers first
2444 1712 : LibmeshPetscCall(VecDestroy(&b_nest));
2445 1712 : LibmeshPetscCall(MatDestroy(&A_nest));
2446 1712 : LibmeshPetscCall(KSPDestroy(&ksp));
2447 17120 : for (PetscInt i = 0; i < Q * Q; i++)
2448 15408 : LibmeshPetscCall(MatDestroy(&mat_array[i]));
2449 6848 : for (PetscInt i = 0; i < Q; i++)
2450 5136 : LibmeshPetscCall(VecDestroy(&vec_array[i]));
2451 1712 : V("Solver elements destroyed");
2452 :
2453 : // ====================== Extract & scatter the solution =====================
2454 : Vec sol_mdot, sol_p, sol_Wij;
2455 1712 : V("Vectors to hold solution created");
2456 : PetscInt num_vecs;
2457 : Vec * loc_vecs;
2458 1712 : LibmeshPetscCall(VecNestGetSubVecs(x_nest, &num_vecs, &loc_vecs));
2459 1712 : LibmeshPetscCall(VecDuplicate(_mc_axial_convection_rhs, &sol_mdot));
2460 1712 : LibmeshPetscCall(VecCopy(loc_vecs[0], sol_mdot));
2461 1712 : LibmeshPetscCall(VecDuplicate(_amc_sys_mdot_rhs, &sol_p));
2462 1712 : LibmeshPetscCall(VecCopy(loc_vecs[1], sol_p));
2463 1712 : LibmeshPetscCall(VecDuplicate(_cmc_sys_Wij_rhs, &sol_Wij));
2464 1712 : LibmeshPetscCall(VecCopy(loc_vecs[2], sol_Wij));
2465 1712 : V("Solution from coupled solver copied to solution vectors");
2466 :
2467 : // mass flow
2468 1712 : LibmeshPetscCall(populateSolutionChan<SolutionHandle>(
2469 : sol_mdot, *_mdot_soln, first_node, last_node, _n_channels));
2470 :
2471 : // pressure
2472 : {
2473 : PetscScalar * sol_p_array;
2474 1712 : LibmeshPetscCall(VecGetArray(sol_p, &sol_p_array));
2475 53992 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
2476 : {
2477 52280 : const auto iz_ind = iz - first_node;
2478 3241310 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
2479 : {
2480 3189030 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
2481 3189030 : PetscScalar value = sol_p_array[iz_ind * _n_channels + i_ch];
2482 3189030 : _P_soln->set(node_in, value);
2483 : }
2484 : }
2485 1712 : LibmeshPetscCall(VecRestoreArray(sol_p, &sol_p_array));
2486 : }
2487 :
2488 : // crossflow dense + sumWij + correction factor
2489 1712 : LibmeshPetscCall(populateDenseFromVector<libMesh::DenseMatrix<Real>>(
2490 : sol_Wij, _Wij, first_node, last_node, _n_gaps));
2491 :
2492 1712 : LibmeshPetscCall(MatMult(_mc_sumWij_mat, sol_Wij, _prod));
2493 1712 : LibmeshPetscCall(populateSolutionChan<SolutionHandle>(
2494 : _prod, *_SumWij_soln, first_node, last_node, _n_channels));
2495 :
2496 1712 : LibmeshPetscCall(VecAbs(_prod));
2497 1712 : LibmeshPetscCall(VecMax(_prod, NULL, &_max_sumWij_new));
2498 1712 : V("Maximum estimated Wij new: " + std::to_string(_max_sumWij_new));
2499 1712 : _correction_factor = _max_sumWij_new / _max_sumWij;
2500 1712 : V("Correction factor: " + std::to_string(_correction_factor));
2501 1712 : V("Solutions assigned to MOOSE variables.");
2502 :
2503 : // cleanup solution objects
2504 1712 : LibmeshPetscCall(VecDestroy(&x_nest));
2505 1712 : LibmeshPetscCall(VecDestroy(&sol_mdot));
2506 1712 : LibmeshPetscCall(VecDestroy(&sol_p));
2507 1712 : LibmeshPetscCall(VecDestroy(&sol_Wij));
2508 1712 : V("Solutions destroyed.");
2509 :
2510 1712 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
2511 1712 : }
2512 :
2513 : void
2514 333 : SubChannel1PhaseProblem::externalSolve()
2515 : {
2516 333 : _console << "Executing subchannel solver\n";
2517 333 : _dt = (isTransient() ? dt() : _one);
2518 333 : _TR = isTransient();
2519 :
2520 : // The subchannel solver hardcodes a first-order backward (implicit) Euler time discretization, so
2521 : // any other time integrator a user selects is silently ignored. Warn once if one is requested.
2522 333 : if (!_time_integrator_checked)
2523 : {
2524 261 : _time_integrator_checked = true;
2525 261 : if (isTransient())
2526 17 : if (auto * transient = dynamic_cast<TransientBase *>(_app.getExecutioner()))
2527 34 : for (const auto * ti : transient->getTimeIntegrators())
2528 17 : if (!dynamic_cast<const ImplicitEuler *>(ti))
2529 5 : mooseWarning("The subchannel solver always uses implicit (backward) Euler time "
2530 : "integration; the requested '",
2531 : ti->type(),
2532 : "' time integrator is ignored.");
2533 : }
2534 :
2535 333 : initializeSolution();
2536 : // Small helper functions to reduce repetition
2537 : // Verbose print helper (no-op unless _verbose_subchannel is true)
2538 10943 : auto V = [&](const std::string & s)
2539 : {
2540 10943 : if (_verbose_subchannel)
2541 7382 : _console << s << std::endl;
2542 11276 : };
2543 333 : V("Solution initialized");
2544 333 : Real P_error = 1.0;
2545 333 : unsigned int P_it = 0;
2546 : unsigned int P_it_max;
2547 :
2548 333 : if (_segregated_bool)
2549 171 : P_it_max = 20 * _n_blocks;
2550 : else
2551 : P_it_max = 100;
2552 :
2553 333 : if ((_n_blocks == 1) && (_segregated_bool))
2554 : P_it_max = 5;
2555 :
2556 1902 : while ((P_error > _P_tol && P_it < P_it_max))
2557 : {
2558 1569 : P_it += 1;
2559 1569 : if (P_it == P_it_max && _n_blocks != 1)
2560 : {
2561 0 : _console << "Reached maximum number of axial pressure iterations" << std::endl;
2562 0 : _converged = false;
2563 : }
2564 1569 : _console << "Solving Outer Iteration : " << P_it << std::endl;
2565 1569 : auto P_L2norm_old_axial = _P_soln->L2norm();
2566 3138 : for (unsigned int iblock = 0; iblock < _n_blocks; iblock++)
2567 : {
2568 1569 : int last_level = (iblock + 1) * _block_size;
2569 1569 : int first_level = iblock * _block_size + 1;
2570 1569 : Real T_block_error = 1.0;
2571 : auto T_it = 0;
2572 1569 : _console << "Solving Block: " << iblock << " From first level: " << first_level
2573 1569 : << " to last level: " << last_level << std::endl;
2574 4263 : while (T_block_error > _T_tol && T_it < _T_maxit)
2575 : {
2576 2694 : T_it += 1;
2577 2694 : if (T_it == _T_maxit)
2578 : {
2579 0 : _console << "Reached maximum number of temperature iterations for block: " << iblock
2580 0 : << std::endl;
2581 0 : _converged = false;
2582 : }
2583 2694 : auto T_L2norm_old_block = _T_soln->L2norm();
2584 : // We are only computing quantities on rank 0
2585 2694 : if (processor_id() > 0)
2586 670 : goto aux_close;
2587 :
2588 2024 : if (_segregated_bool)
2589 : {
2590 312 : computeWijFromSolve(iblock);
2591 312 : if (_compute_power)
2592 : {
2593 226 : computeh(iblock);
2594 226 : computeT(iblock);
2595 : }
2596 : }
2597 : else
2598 : {
2599 1712 : LibmeshPetscCall(implicitPetscSolve(iblock));
2600 1712 : computeWijPrime(iblock);
2601 1712 : V("Done with main solve.");
2602 1712 : if (_compute_power)
2603 : {
2604 1612 : computeh(iblock);
2605 1612 : computeT(iblock);
2606 : }
2607 3424 : V("Done with thermal solve.");
2608 : }
2609 :
2610 2024 : V("Start updating thermophysical properties.");
2611 2024 : if (_compute_density)
2612 2014 : computeRho(iblock);
2613 2024 : if (_compute_viscosity)
2614 2014 : computeMu(iblock);
2615 4048 : V("Done updating thermophysical properties.");
2616 :
2617 : // We must do a global assembly to make sure data is parallel consistent before we do things
2618 : // like compute L2 norms
2619 2694 : aux_close:
2620 2694 : _aux->solution().close();
2621 :
2622 2694 : auto T_L2norm_new = _T_soln->L2norm();
2623 2694 : T_block_error =
2624 2694 : std::abs((T_L2norm_new - T_L2norm_old_block) / (T_L2norm_old_block + 1E-14));
2625 2694 : _console << "T_block_error: " << T_block_error << std::endl;
2626 :
2627 : // All processes must have the same iteration count
2628 2694 : comm().max(T_block_error);
2629 : }
2630 : }
2631 1569 : auto P_L2norm_new_axial = _P_soln->L2norm();
2632 1569 : P_error =
2633 1569 : std::abs((P_L2norm_new_axial - P_L2norm_old_axial) / (P_L2norm_old_axial + _P_out + 1E-14));
2634 1569 : _console << "P_error :" << P_error << std::endl;
2635 1569 : V("Iteration: " + std::to_string(P_it));
2636 3138 : V("Maximum iterations: " + std::to_string(P_it_max));
2637 : }
2638 : // update old crossflow matrix
2639 333 : _Wij_old = _Wij;
2640 333 : _console << "Finished executing subchannel solver\n";
2641 :
2642 : // set SumWij at the inlet equal to the one on the first axial level (for visualization purposes)
2643 11999 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
2644 : {
2645 11666 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, 0);
2646 11666 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, 1);
2647 11666 : _SumWij_soln->set(node_in, (*_SumWij_soln)(node_out)); // kg/sec
2648 : }
2649 :
2650 333 : if (_pin_mesh_exist)
2651 : {
2652 : // Assign average HTC to subchannels. This is exact if all pins have the same diameter
2653 7127 : for (unsigned int iz = 0; iz < _n_cells + 1; ++iz)
2654 : {
2655 293583 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
2656 : {
2657 286780 : const auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
2658 286780 : auto mu = (*_mu_soln)(node);
2659 286780 : auto S = (*_S_flow_soln)(node);
2660 286780 : auto w_perim = (*_w_perim_soln)(node);
2661 286780 : auto Dh_i = 4.0 * S / w_perim;
2662 286780 : auto Re = (((*_mdot_soln)(node) / S) * Dh_i / mu);
2663 286780 : auto k = _fp->k_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
2664 286780 : auto cp = _fp->cp_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
2665 286780 : auto Pr = (*_mu_soln)(node)*cp / k;
2666 : // Create Friction structure
2667 286780 : _friction_args = FrictionStruct(i_ch, Re, S, w_perim);
2668 :
2669 : Real sumhw = 0.0;
2670 1014616 : for (auto i_pin : _subchannel_mesh.getChannelPins(i_ch))
2671 : {
2672 : // Create nusselt number structure
2673 727839 : _nusselt_args = NusseltStruct(Re, Pr, i_pin, iz, i_ch);
2674 :
2675 : // Compute HTC
2676 727839 : sumhw += _pin_HTC_closure->computeHTC(_friction_args, _nusselt_args, k);
2677 : }
2678 :
2679 : // Set HTC
2680 286777 : _HTC_soln->set(node, sumhw / _subchannel_mesh.getChannelPins(i_ch).size());
2681 : }
2682 : }
2683 : _HTC_soln->close();
2684 :
2685 321 : _console << "Commencing calculation of Pin surface temperature \n";
2686 5942 : for (unsigned int i_pin = 0; i_pin < _n_pins; i_pin++)
2687 : {
2688 148821 : for (unsigned int iz = 0; iz < _n_cells + 1; ++iz)
2689 : {
2690 143200 : const auto * pin_node = _subchannel_mesh.getPinNode(i_pin, iz);
2691 : Real sumTemp = 0.0;
2692 : // Calculate sum of pin surface temperatures that the channels around the pin see
2693 871036 : for (auto i_ch : _subchannel_mesh.getPinChannels(i_pin))
2694 : {
2695 727836 : const auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
2696 727836 : auto mu = (*_mu_soln)(node);
2697 727836 : auto S = (*_S_flow_soln)(node);
2698 727836 : auto w_perim = (*_w_perim_soln)(node);
2699 727836 : auto Dh_i = 4.0 * S / w_perim;
2700 727836 : auto Re = (((*_mdot_soln)(node) / S) * Dh_i / mu);
2701 727836 : auto k = _fp->k_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
2702 727836 : auto cp = _fp->cp_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
2703 727836 : auto Pr = (*_mu_soln)(node)*cp / k;
2704 : // Create Friction structure
2705 727836 : _friction_args = FrictionStruct(i_ch, Re, S, w_perim);
2706 : // Create nusselt number structure
2707 727836 : _nusselt_args = NusseltStruct(Re, Pr, i_pin, iz, i_ch);
2708 : // Compute HTC
2709 727836 : auto hw = _pin_HTC_closure->computeHTC(_friction_args, _nusselt_args, k);
2710 : // Compute surface temperature contribution from subchannel side
2711 727836 : sumTemp +=
2712 727836 : (*_q_prime_soln)(pin_node) / ((*_Dpin_soln)(pin_node)*M_PI * hw) + (*_T_soln)(node);
2713 : }
2714 143200 : if (_subchannel_mesh.getPinChannels(i_pin).size() > 0)
2715 143200 : _Tpin_soln->set(pin_node, sumTemp / _subchannel_mesh.getPinChannels(i_pin).size());
2716 : else
2717 0 : mooseError("Pin was not found for pin index: " + std::to_string(i_pin));
2718 : }
2719 : }
2720 : }
2721 :
2722 : /// Assigning temperatures to duct
2723 330 : if (_duct_mesh_exist && processor_id() == 0)
2724 : {
2725 52 : _console << "Commencing calculation of duct surface temperature " << std::endl;
2726 52 : auto duct_nodes = _subchannel_mesh.getDuctNodes();
2727 24472 : for (Node * dn : duct_nodes)
2728 : {
2729 24420 : auto * node_chan = _subchannel_mesh.getChannelNodeFromDuct(dn);
2730 24420 : auto mu = (*_mu_soln)(node_chan);
2731 24420 : auto S = (*_S_flow_soln)(node_chan);
2732 24420 : auto w_perim = (*_w_perim_soln)(node_chan);
2733 24420 : auto Dh_i = 4.0 * S / w_perim;
2734 24420 : auto Re = (((*_mdot_soln)(node_chan) / S) * Dh_i / mu);
2735 24420 : auto k = _fp->k_from_p_T((*_P_soln)(node_chan) + _P_out, (*_T_soln)(node_chan));
2736 24420 : auto cp = _fp->cp_from_p_T((*_P_soln)(node_chan) + _P_out, (*_T_soln)(node_chan));
2737 24420 : auto Pr = (*_mu_soln)(node_chan)*cp / k;
2738 :
2739 : // Create nusselt number structure (consistent with pin case)
2740 24420 : const libMesh::Point & node_point = *_subchannel_mesh.getChannelNodeFromDuct(dn);
2741 24420 : const unsigned int iz = _subchannel_mesh.getZIndex(node_point);
2742 24420 : const unsigned int i_ch = _subchannel_mesh.channelIndex(node_point);
2743 :
2744 : // Create nusselt number structure
2745 24420 : _nusselt_args = NusseltStruct(Re, Pr, std::numeric_limits<unsigned int>::max(), iz, i_ch);
2746 :
2747 : // Create Friction structure
2748 24420 : _friction_args = FrictionStruct(i_ch, Re, S, w_perim);
2749 :
2750 : // Compute HTC
2751 24420 : auto hw = _duct_HTC_closure->computeHTC(_friction_args, _nusselt_args, k);
2752 :
2753 : // Compute Channel Temperature
2754 24420 : auto T_chan = (*_duct_heat_flux_soln)(dn) / hw + (*_T_soln)(node_chan);
2755 24420 : _Tduct_soln->set(dn, T_chan);
2756 : }
2757 52 : }
2758 330 : _aux->solution().close();
2759 330 : _aux->update();
2760 :
2761 330 : if (processor_id() != 0)
2762 88 : return;
2763 : Real power_in = 0.0;
2764 : Real power_out = 0.0;
2765 : Real viscosity_in = 0.0;
2766 242 : Real mass_flow_in = 0.0;
2767 : Real mass_flow_out = 0.0;
2768 11530 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
2769 : {
2770 11288 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, 0);
2771 11288 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, _n_cells);
2772 11288 : const Real mdot_in = (*_mdot_soln)(node_in);
2773 11288 : power_in += mdot_in * (*_h_soln)(node_in);
2774 11288 : power_out += (*_mdot_soln)(node_out) * (*_h_soln)(node_out);
2775 11288 : viscosity_in += mdot_in * (*_mu_soln)(node_in);
2776 11288 : mass_flow_in += mdot_in;
2777 11288 : mass_flow_out += (*_mdot_soln)(node_out);
2778 : }
2779 242 : auto h_bulk_out = power_out / mass_flow_out;
2780 242 : auto T_bulk_out = _fp->T_from_p_h(_P_out, h_bulk_out);
2781 :
2782 242 : Real bulk_Dh = _subchannel_mesh.getAssemblyHydraulicDiameter();
2783 242 : Real inlet_mu = viscosity_in / mass_flow_in;
2784 242 : Real bulk_Re = mass_flow_in * bulk_Dh / (inlet_mu * _subchannel_mesh.getAssemblyFlowArea());
2785 242 : if (_verbose_subchannel)
2786 : {
2787 139 : _console << " ======================================= " << std::endl;
2788 139 : _console << " ======== Subchannel Print Outs ======== " << std::endl;
2789 139 : _console << " ======================================= " << std::endl;
2790 139 : _console << "Total flow area :" << _subchannel_mesh.getAssemblyFlowArea() << " m^2"
2791 139 : << std::endl;
2792 139 : _console << "Assembly hydraulic diameter :" << bulk_Dh << " m" << std::endl;
2793 139 : _console << "Assembly Re number :" << bulk_Re << " [-]" << std::endl;
2794 139 : _console << "Bulk coolant temperature at outlet :" << T_bulk_out << " K" << std::endl;
2795 139 : _console << "Power added to coolant is : " << power_out - power_in << " Watt" << std::endl;
2796 139 : _console << "Mass flow rate in is : " << mass_flow_in << " kg/sec" << std::endl;
2797 139 : _console << "Mass balance is : " << mass_flow_out - mass_flow_in << " kg/sec" << std::endl;
2798 139 : _console << "User defined outlet pressure is : " << _P_out << " Pa" << std::endl;
2799 139 : _console << " ======================================= " << std::endl;
2800 : }
2801 :
2802 242 : if (MooseUtils::absoluteFuzzyLessEqual((power_out - power_in), -1.0))
2803 0 : mooseWarning(
2804 0 : "Energy conservation equation might not be solved correctly, Power added to coolant: " +
2805 0 : std::to_string(power_out - power_in) + " Watt ");
2806 : }
2807 :
2808 : void
2809 671 : SubChannel1PhaseProblem::syncSolutions(Direction /*direction*/)
2810 : {
2811 671 : }
|