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