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 "InterWrapper1PhaseProblem.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 :
20 : /// problem information to be used in the PETSc problem
21 : struct problemInfo
22 : {
23 : int iblock;
24 : InterWrapper1PhaseProblem * schp;
25 : };
26 :
27 : /// creates the residual function to be used in the PETCs snes context
28 : PetscErrorCode
29 8586 : formFunctionIW(SNES, Vec x, Vec f, void * info)
30 : {
31 : const PetscScalar * xx;
32 : PetscScalar * ff;
33 : PetscInt size;
34 :
35 : PetscFunctionBegin;
36 : problemInfo * cc = static_cast<problemInfo *>(info);
37 8586 : LibmeshPetscCallQ(VecGetSize(x, &size));
38 :
39 8586 : libMesh::DenseVector<Real> solution_seed(size, 0.0);
40 8586 : LibmeshPetscCallQ(VecGetArrayRead(x, &xx));
41 3049506 : for (PetscInt i = 0; i < size; i++)
42 3040920 : solution_seed(i) = xx[i];
43 :
44 8586 : LibmeshPetscCallQ(VecRestoreArrayRead(x, &xx));
45 :
46 : libMesh::DenseVector<Real> Wij_residual_vector =
47 8586 : cc->schp->residualFunction(cc->iblock, solution_seed);
48 :
49 8586 : LibmeshPetscCallQ(VecGetArray(f, &ff));
50 3049506 : for (int i = 0; i < size; i++)
51 3040920 : ff[i] = Wij_residual_vector(i);
52 :
53 8586 : LibmeshPetscCallQ(VecRestoreArray(f, &ff));
54 8586 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
55 : }
56 :
57 : InputParameters
58 144 : InterWrapper1PhaseProblem::validParams()
59 : {
60 288 : MooseEnum schemes("upwind downwind central_difference exponential", "upwind");
61 144 : InputParameters params = ExternalProblem::validParams();
62 144 : params.addClassDescription("Base class of the interwrapper solvers");
63 288 : params.addRequiredParam<unsigned int>("n_blocks", "The number of blocks in the axial direction");
64 288 : params.addRequiredParam<Real>("beta",
65 : "Thermal diffusion coefficient used in turbulent crossflow");
66 288 : params.addRequiredParam<Real>("CT", "Turbulent modeling parameter");
67 288 : params.addParam<Real>("P_tol", 1e-6, "Pressure tolerance");
68 288 : params.addParam<Real>("T_tol", 1e-6, "Temperature tolerance");
69 288 : params.addParam<int>("T_maxit", 10, "Maximum number of iterations for inner temperature loop");
70 288 : params.addParam<PetscReal>("rtol", 1e-6, "Relative tolerance for ksp solver");
71 288 : params.addParam<PetscReal>("atol", 1e-6, "Absolute tolerance for ksp solver");
72 288 : params.addParam<PetscReal>("dtol", 1e5, "Divergence tolerance for ksp solver");
73 288 : params.addParam<PetscInt>("maxit", 1e4, "Maximum number of iterations for ksp solver");
74 288 : params.addParam<MooseEnum>(
75 : "interpolation_scheme", schemes, "Interpolation scheme used for the method.");
76 288 : params.addParam<bool>(
77 288 : "implicit", false, "Boolean to define the use of explicit or implicit solution.");
78 288 : params.addParam<bool>("staggered_pressure",
79 288 : false,
80 : "Boolean to define the use of the staggered pressure formulation.");
81 288 : params.addParam<bool>(
82 : "segregated",
83 288 : true,
84 : "Boolean to define whether to use a segregated solver (physics solved independently).");
85 288 : params.addParam<bool>(
86 288 : "monolithic_thermal", false, "Boolean to define whether to use thermal monolithic solve.");
87 288 : params.addRequiredParam<bool>("compute_density", "Flag that enables the calculation of density");
88 288 : params.addRequiredParam<bool>("compute_viscosity",
89 : "Flag that enables the calculation of viscosity");
90 288 : params.addRequiredParam<bool>(
91 : "compute_power",
92 : "Flag that informs whether we solve the Enthalpy/Temperature equations or not");
93 288 : params.addRequiredParam<Real>("P_out", "Outlet Pressure [Pa]");
94 288 : params.addRequiredParam<UserObjectName>("fp", "Fluid properties user object name");
95 144 : return params;
96 144 : }
97 :
98 72 : InterWrapper1PhaseProblem::InterWrapper1PhaseProblem(const InputParameters & params)
99 : : ExternalProblem(params),
100 144 : _subchannel_mesh(SCM::getConstMesh<InterWrapperMesh>(_mesh)),
101 144 : _n_blocks(getParam<unsigned int>("n_blocks")),
102 144 : _Wij(declareRestartableData<libMesh::DenseMatrix<Real>>("Wij")),
103 72 : _g_grav(9.81),
104 72 : _kij(_subchannel_mesh.getKij()),
105 72 : _n_cells(_subchannel_mesh.getNumOfAxialCells()),
106 72 : _n_gaps(_subchannel_mesh.getNumOfGapsPerLayer()),
107 72 : _n_assemblies(_subchannel_mesh.getNumOfAssemblies()),
108 72 : _n_channels(_subchannel_mesh.getNumOfChannels()),
109 72 : _block_size(_n_cells / _n_blocks),
110 72 : _z_grid(_subchannel_mesh.getZGrid()),
111 72 : _one(1.0),
112 72 : _TR(isTransient() ? 1. : 0.),
113 144 : _compute_density(getParam<bool>("compute_density")),
114 144 : _compute_viscosity(getParam<bool>("compute_viscosity")),
115 144 : _compute_power(getParam<bool>("compute_power")),
116 72 : _pin_mesh_exist(_subchannel_mesh.pinMeshExist()),
117 72 : _dt(isTransient() ? dt() : _one),
118 144 : _P_out(getParam<Real>("P_out")),
119 144 : _beta(getParam<Real>("beta")),
120 144 : _CT(getParam<Real>("CT")),
121 144 : _P_tol(getParam<Real>("P_tol")),
122 144 : _T_tol(getParam<Real>("T_tol")),
123 144 : _T_maxit(getParam<int>("T_maxit")),
124 144 : _rtol(getParam<PetscReal>("rtol")),
125 144 : _atol(getParam<PetscReal>("atol")),
126 144 : _dtol(getParam<PetscReal>("dtol")),
127 144 : _maxit(getParam<PetscInt>("maxit")),
128 144 : _interpolation_scheme(getParam<MooseEnum>("interpolation_scheme")),
129 144 : _implicit_bool(getParam<bool>("implicit")),
130 144 : _staggered_pressure_bool(getParam<bool>("staggered_pressure")),
131 144 : _segregated_bool(getParam<bool>("segregated")),
132 144 : _monolithic_thermal_bool(getParam<bool>("monolithic_thermal")),
133 72 : _fp(nullptr),
134 144 : _Tpin_soln(nullptr)
135 : {
136 : // Turbulent crossflow (stuff that live on the gaps)
137 72 : if (!_app.isRestarting() && !_app.isRecovering())
138 : {
139 72 : _Wij.resize(_n_gaps, _n_cells + 1);
140 72 : _Wij.zero();
141 : }
142 72 : _Wij_old.resize(_n_gaps, _n_cells + 1);
143 : _Wij_old.zero();
144 72 : _WijPrime.resize(_n_gaps, _n_cells + 1);
145 : _WijPrime.zero();
146 72 : _Wij_residual_matrix.resize(_n_gaps, _block_size);
147 : _Wij_residual_matrix.zero();
148 72 : _converged = true;
149 :
150 : // Mass conservation components
151 72 : LibmeshPetscCall(
152 : createPetscMatrix(_mc_sumWij_mat, _block_size * _n_channels, _block_size * _n_gaps));
153 72 : LibmeshPetscCall(createPetscVector(_Wij_vec, _block_size * _n_gaps));
154 72 : LibmeshPetscCall(createPetscVector(_prod, _block_size * _n_channels));
155 72 : LibmeshPetscCall(createPetscVector(_prodp, _block_size * _n_channels));
156 72 : LibmeshPetscCall(createPetscMatrix(
157 : _mc_axial_convection_mat, _block_size * _n_channels, _block_size * _n_channels));
158 72 : LibmeshPetscCall(createPetscVector(_mc_axial_convection_rhs, _block_size * _n_channels));
159 :
160 : // Axial momentum conservation components
161 72 : LibmeshPetscCall(createPetscMatrix(
162 : _amc_turbulent_cross_flows_mat, _block_size * _n_gaps, _block_size * _n_channels));
163 72 : LibmeshPetscCall(createPetscVector(_amc_turbulent_cross_flows_rhs, _block_size * _n_gaps));
164 72 : LibmeshPetscCall(createPetscMatrix(
165 : _amc_time_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
166 72 : LibmeshPetscCall(createPetscVector(_amc_time_derivative_rhs, _block_size * _n_channels));
167 72 : LibmeshPetscCall(createPetscMatrix(
168 : _amc_advective_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
169 72 : LibmeshPetscCall(createPetscVector(_amc_advective_derivative_rhs, _block_size * _n_channels));
170 72 : LibmeshPetscCall(createPetscMatrix(
171 : _amc_cross_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
172 72 : LibmeshPetscCall(createPetscVector(_amc_cross_derivative_rhs, _block_size * _n_channels));
173 72 : LibmeshPetscCall(createPetscMatrix(
174 : _amc_friction_force_mat, _block_size * _n_channels, _block_size * _n_channels));
175 72 : LibmeshPetscCall(createPetscVector(_amc_friction_force_rhs, _block_size * _n_channels));
176 72 : LibmeshPetscCall(createPetscVector(_amc_gravity_rhs, _block_size * _n_channels));
177 72 : LibmeshPetscCall(createPetscMatrix(
178 : _amc_pressure_force_mat, _block_size * _n_channels, _block_size * _n_channels));
179 72 : LibmeshPetscCall(createPetscVector(_amc_pressure_force_rhs, _block_size * _n_channels));
180 72 : LibmeshPetscCall(
181 : createPetscMatrix(_amc_sys_mdot_mat, _block_size * _n_channels, _block_size * _n_channels));
182 72 : LibmeshPetscCall(createPetscVector(_amc_sys_mdot_rhs, _block_size * _n_channels));
183 :
184 : // Lateral momentum conservation components
185 72 : LibmeshPetscCall(
186 : createPetscMatrix(_cmc_time_derivative_mat, _block_size * _n_gaps, _block_size * _n_gaps));
187 72 : LibmeshPetscCall(createPetscVector(_cmc_time_derivative_rhs, _block_size * _n_gaps));
188 72 : LibmeshPetscCall(createPetscMatrix(
189 : _cmc_advective_derivative_mat, _block_size * _n_gaps, _block_size * _n_gaps));
190 72 : LibmeshPetscCall(createPetscVector(_cmc_advective_derivative_rhs, _block_size * _n_gaps));
191 72 : LibmeshPetscCall(
192 : createPetscMatrix(_cmc_friction_force_mat, _block_size * _n_gaps, _block_size * _n_gaps));
193 72 : LibmeshPetscCall(createPetscVector(_cmc_friction_force_rhs, _block_size * _n_gaps));
194 72 : LibmeshPetscCall(
195 : createPetscMatrix(_cmc_pressure_force_mat, _block_size * _n_gaps, _block_size * _n_channels));
196 72 : LibmeshPetscCall(createPetscVector(_cmc_pressure_force_rhs, _block_size * _n_gaps));
197 72 : LibmeshPetscCall(
198 : createPetscMatrix(_cmc_sys_Wij_mat, _block_size * _n_gaps, _block_size * _n_gaps));
199 72 : LibmeshPetscCall(createPetscVector(_cmc_sys_Wij_rhs, _block_size * _n_gaps));
200 72 : LibmeshPetscCall(createPetscVector(_cmc_Wij_channel_dummy, _block_size * _n_channels));
201 :
202 : // Energy conservation components
203 72 : LibmeshPetscCall(createPetscMatrix(
204 : _hc_time_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
205 72 : LibmeshPetscCall(createPetscVector(_hc_time_derivative_rhs, _block_size * _n_channels));
206 72 : LibmeshPetscCall(createPetscMatrix(
207 : _hc_advective_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
208 72 : LibmeshPetscCall(createPetscVector(_hc_advective_derivative_rhs, _block_size * _n_channels));
209 72 : LibmeshPetscCall(createPetscMatrix(
210 : _hc_cross_derivative_mat, _block_size * _n_channels, _block_size * _n_channels));
211 72 : LibmeshPetscCall(createPetscVector(_hc_cross_derivative_rhs, _block_size * _n_channels));
212 72 : LibmeshPetscCall(createPetscVector(_hc_added_heat_rhs, _block_size * _n_channels));
213 72 : LibmeshPetscCall(
214 : createPetscMatrix(_hc_sys_h_mat, _block_size * _n_channels, _block_size * _n_channels));
215 72 : LibmeshPetscCall(createPetscVector(_hc_sys_h_rhs, _block_size * _n_channels));
216 :
217 72 : if ((_n_blocks == _n_cells) && _implicit_bool)
218 : {
219 0 : mooseError(name(),
220 : ": When implicit number of blocks can't be equal to number of cells. This will "
221 : "cause problems with the subchannel interpolation scheme.");
222 : }
223 72 : }
224 :
225 : void
226 54 : InterWrapper1PhaseProblem::initialSetup()
227 : {
228 54 : ExternalProblem::initialSetup();
229 108 : _fp = &getUserObject<SinglePhaseFluidProperties>(getParam<UserObjectName>("fp"));
230 108 : _mdot_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::MASS_FLOW_RATE));
231 108 : _SumWij_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::SUM_CROSSFLOW));
232 108 : _P_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::PRESSURE));
233 108 : _DP_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::PRESSURE_DROP));
234 108 : _h_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::ENTHALPY));
235 108 : _T_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::TEMPERATURE));
236 54 : if (_pin_mesh_exist)
237 0 : _Tpin_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::PIN_TEMPERATURE));
238 108 : _rho_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::DENSITY));
239 108 : _mu_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::VISCOSITY));
240 108 : _S_flow_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::SURFACE_AREA));
241 108 : _w_perim_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::WETTED_PERIMETER));
242 108 : _q_prime_soln = std::make_unique<SolutionHandle>(getVariable(0, SubChannelApp::LINEAR_HEAT_RATE));
243 54 : }
244 :
245 144 : InterWrapper1PhaseProblem::~InterWrapper1PhaseProblem()
246 : {
247 72 : PetscErrorCode ierr = cleanUp();
248 72 : if (ierr)
249 0 : mooseError(name(), ": Error in memory cleanup");
250 72 : }
251 :
252 : PetscErrorCode
253 72 : InterWrapper1PhaseProblem::cleanUp()
254 : {
255 : PetscFunctionBegin;
256 : // We need to clean up the petsc matrices/vectors
257 : // Mass conservation components
258 72 : LibmeshPetscCall(MatDestroy(&_mc_sumWij_mat));
259 72 : LibmeshPetscCall(VecDestroy(&_Wij_vec));
260 72 : LibmeshPetscCall(VecDestroy(&_prod));
261 72 : LibmeshPetscCall(VecDestroy(&_prodp));
262 72 : LibmeshPetscCall(MatDestroy(&_mc_axial_convection_mat));
263 72 : LibmeshPetscCall(VecDestroy(&_mc_axial_convection_rhs));
264 :
265 : // Axial momentum conservation components
266 72 : LibmeshPetscCall(MatDestroy(&_amc_turbulent_cross_flows_mat));
267 72 : LibmeshPetscCall(VecDestroy(&_amc_turbulent_cross_flows_rhs));
268 72 : LibmeshPetscCall(MatDestroy(&_amc_time_derivative_mat));
269 72 : LibmeshPetscCall(VecDestroy(&_amc_time_derivative_rhs));
270 72 : LibmeshPetscCall(MatDestroy(&_amc_advective_derivative_mat));
271 72 : LibmeshPetscCall(VecDestroy(&_amc_advective_derivative_rhs));
272 72 : LibmeshPetscCall(MatDestroy(&_amc_cross_derivative_mat));
273 72 : LibmeshPetscCall(VecDestroy(&_amc_cross_derivative_rhs));
274 72 : LibmeshPetscCall(MatDestroy(&_amc_friction_force_mat));
275 72 : LibmeshPetscCall(VecDestroy(&_amc_friction_force_rhs));
276 72 : LibmeshPetscCall(VecDestroy(&_amc_gravity_rhs));
277 72 : LibmeshPetscCall(MatDestroy(&_amc_pressure_force_mat));
278 72 : LibmeshPetscCall(VecDestroy(&_amc_pressure_force_rhs));
279 72 : LibmeshPetscCall(MatDestroy(&_amc_sys_mdot_mat));
280 72 : LibmeshPetscCall(VecDestroy(&_amc_sys_mdot_rhs));
281 :
282 : // Lateral momentum conservation components
283 72 : LibmeshPetscCall(MatDestroy(&_cmc_time_derivative_mat));
284 72 : LibmeshPetscCall(VecDestroy(&_cmc_time_derivative_rhs));
285 72 : LibmeshPetscCall(MatDestroy(&_cmc_advective_derivative_mat));
286 72 : LibmeshPetscCall(VecDestroy(&_cmc_advective_derivative_rhs));
287 72 : LibmeshPetscCall(MatDestroy(&_cmc_friction_force_mat));
288 72 : LibmeshPetscCall(VecDestroy(&_cmc_friction_force_rhs));
289 72 : LibmeshPetscCall(MatDestroy(&_cmc_pressure_force_mat));
290 72 : LibmeshPetscCall(VecDestroy(&_cmc_pressure_force_rhs));
291 72 : LibmeshPetscCall(MatDestroy(&_cmc_sys_Wij_mat));
292 72 : LibmeshPetscCall(VecDestroy(&_cmc_sys_Wij_rhs));
293 72 : LibmeshPetscCall(VecDestroy(&_cmc_Wij_channel_dummy));
294 :
295 : // Energy conservation components
296 72 : LibmeshPetscCall(MatDestroy(&_hc_time_derivative_mat));
297 72 : LibmeshPetscCall(VecDestroy(&_hc_time_derivative_rhs));
298 72 : LibmeshPetscCall(MatDestroy(&_hc_advective_derivative_mat));
299 72 : LibmeshPetscCall(VecDestroy(&_hc_advective_derivative_rhs));
300 72 : LibmeshPetscCall(MatDestroy(&_hc_cross_derivative_mat));
301 72 : LibmeshPetscCall(VecDestroy(&_hc_cross_derivative_rhs));
302 72 : LibmeshPetscCall(VecDestroy(&_hc_added_heat_rhs));
303 72 : LibmeshPetscCall(MatDestroy(&_hc_sys_h_mat));
304 72 : LibmeshPetscCall(VecDestroy(&_hc_sys_h_rhs));
305 :
306 72 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
307 : }
308 :
309 : bool
310 54 : InterWrapper1PhaseProblem::solverSystemConverged(const unsigned int)
311 : {
312 54 : return _converged;
313 : }
314 :
315 : PetscScalar
316 1033560 : InterWrapper1PhaseProblem::computeInterpolationCoefficients(PetscScalar Peclet)
317 : {
318 1033560 : if (_interpolation_scheme == "upwind")
319 : {
320 : return 1.0;
321 : }
322 26640 : else if (_interpolation_scheme == "downwind")
323 : {
324 : return 0.0;
325 : }
326 26640 : else if (_interpolation_scheme == "central_difference")
327 : {
328 : return 0.5;
329 : }
330 0 : else if (_interpolation_scheme == "exponential")
331 : {
332 0 : return ((Peclet - 1.0) * std::exp(Peclet) + 1) / (Peclet * (std::exp(Peclet) - 1.) + 1e-10);
333 : }
334 : else
335 : {
336 0 : mooseError(name(),
337 : ": Interpolation scheme should be a string: upwind, downwind, central_difference, "
338 : "exponential");
339 : }
340 : }
341 :
342 : PetscScalar
343 815400 : InterWrapper1PhaseProblem::computeInterpolatedValue(PetscScalar topValue,
344 : PetscScalar botValue,
345 : PetscScalar Peclet)
346 : {
347 815400 : PetscScalar alpha = computeInterpolationCoefficients(Peclet);
348 815400 : return alpha * botValue + (1.0 - alpha) * topValue;
349 : }
350 :
351 : PetscErrorCode
352 3852 : InterWrapper1PhaseProblem::createPetscVector(Vec & v, PetscInt n)
353 : {
354 : PetscFunctionBegin;
355 3852 : LibmeshPetscCall(VecCreate(PETSC_COMM_WORLD, &v));
356 3852 : LibmeshPetscCall(PetscObjectSetName((PetscObject)v, "Solution"));
357 3852 : LibmeshPetscCall(VecSetSizes(v, PETSC_DECIDE, n));
358 3852 : LibmeshPetscCall(VecSetFromOptions(v));
359 3852 : LibmeshPetscCall(VecZeroEntries(v));
360 3852 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
361 : }
362 :
363 : PetscErrorCode
364 1296 : InterWrapper1PhaseProblem::createPetscMatrix(Mat & M, PetscInt n, PetscInt m)
365 : {
366 : PetscFunctionBegin;
367 1296 : LibmeshPetscCall(MatCreate(PETSC_COMM_WORLD, &M));
368 1296 : LibmeshPetscCall(MatSetSizes(M, PETSC_DECIDE, PETSC_DECIDE, n, m));
369 1296 : LibmeshPetscCall(MatSetFromOptions(M));
370 1296 : LibmeshPetscCall(MatSetUp(M));
371 1296 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
372 : }
373 :
374 : template <class T>
375 : PetscErrorCode
376 1284 : InterWrapper1PhaseProblem::populateVectorFromDense(Vec & x,
377 : const T & loc_solution,
378 : const unsigned int first_axial_level,
379 : const unsigned int last_axial_level,
380 : const unsigned int cross_dimension)
381 : {
382 : PetscScalar * xx;
383 : PetscFunctionBegin;
384 1284 : LibmeshPetscCall(VecGetArray(x, &xx));
385 8136 : for (unsigned int iz = first_axial_level; iz < last_axial_level; iz++)
386 : {
387 6852 : unsigned int iz_ind = iz - first_axial_level;
388 417972 : for (unsigned int i_l = 0; i_l < cross_dimension; i_l++)
389 : {
390 411120 : xx[iz_ind * cross_dimension + i_l] = loc_solution(i_l, iz);
391 : }
392 : }
393 1284 : LibmeshPetscCall(VecRestoreArray(x, &xx));
394 1284 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
395 : }
396 :
397 : template <class T>
398 : PetscErrorCode
399 1404 : InterWrapper1PhaseProblem::populateVectorFromHandle(Vec & x,
400 : const T & loc_solution,
401 : const unsigned int first_axial_level,
402 : const unsigned int last_axial_level,
403 : const unsigned int cross_dimension)
404 : {
405 : PetscScalar * xx;
406 : PetscFunctionBegin;
407 1404 : LibmeshPetscCall(VecGetArray(x, &xx));
408 9564 : for (unsigned int iz = first_axial_level; iz < last_axial_level + 1; iz++)
409 : {
410 8160 : unsigned int iz_ind = iz - first_axial_level;
411 337920 : for (unsigned int i_l = 0; i_l < cross_dimension; i_l++)
412 : {
413 329760 : auto * loc_node = _subchannel_mesh.getChannelNode(i_l, iz);
414 329760 : xx[iz_ind * cross_dimension + i_l] = loc_solution(loc_node);
415 : }
416 : }
417 1404 : LibmeshPetscCall(VecRestoreArray(x, &xx));
418 1404 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
419 : }
420 :
421 : template <class T>
422 : PetscErrorCode
423 1248 : InterWrapper1PhaseProblem::populateSolutionChan(const Vec & x,
424 : T & loc_solution,
425 : const unsigned int first_axial_level,
426 : const unsigned int last_axial_level,
427 : const unsigned int cross_dimension)
428 : {
429 : PetscScalar * xx;
430 : PetscFunctionBegin;
431 1248 : LibmeshPetscCall(VecGetArray(x, &xx));
432 : Node * loc_node;
433 7848 : for (unsigned int iz = first_axial_level; iz < last_axial_level + 1; iz++)
434 : {
435 6600 : unsigned int iz_ind = iz - first_axial_level;
436 279480 : for (unsigned int i_l = 0; i_l < cross_dimension; i_l++)
437 : {
438 272880 : loc_node = _subchannel_mesh.getChannelNode(i_l, iz);
439 272880 : loc_solution.set(loc_node, xx[iz_ind * cross_dimension + i_l]);
440 : }
441 : }
442 1248 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
443 : }
444 :
445 : template <class T>
446 : PetscErrorCode
447 : InterWrapper1PhaseProblem::populateSolutionGap(const Vec & x,
448 : T & loc_solution,
449 : const unsigned int first_axial_level,
450 : const unsigned int last_axial_level,
451 : const unsigned int cross_dimension)
452 : {
453 : PetscScalar * xx;
454 : PetscFunctionBegin;
455 : LibmeshPetscCall(VecGetArray(x, &xx));
456 : for (unsigned int iz = first_axial_level; iz < last_axial_level + 1; iz++)
457 : {
458 : unsigned int iz_ind = iz - first_axial_level;
459 : for (unsigned int i_l = 0; i_l < cross_dimension; i_l++)
460 : {
461 : loc_solution(iz * cross_dimension + i_l) = xx[iz_ind * cross_dimension + i_l];
462 : }
463 : }
464 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
465 : }
466 :
467 : void
468 732 : InterWrapper1PhaseProblem::computeWijFromSolve(int iblock)
469 : {
470 732 : unsigned int last_node = (iblock + 1) * _block_size;
471 732 : unsigned int first_node = iblock * _block_size + 1;
472 : // Initial guess, port crossflow of block (iblock) into a vector that will act as my initial guess
473 732 : libMesh::DenseVector<Real> solution_seed(_n_gaps * _block_size, 0.0);
474 1932 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
475 : {
476 73200 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
477 : {
478 72000 : int i = _n_gaps * (iz - first_node) + i_gap; // column wise transfer
479 72000 : solution_seed(i) = _Wij(i_gap, iz);
480 : }
481 : }
482 :
483 : // Solving the combined lateral momentum equation for Wij using a PETSc solver and update vector
484 : // root
485 732 : libMesh::DenseVector<Real> root(_n_gaps * _block_size, 0.0);
486 732 : LibmeshPetscCall(petscSnesSolver(iblock, solution_seed, root));
487 :
488 : // Assign the solution to the cross-flow matrix
489 : int i = 0;
490 1932 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
491 : {
492 73200 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
493 : {
494 72000 : _Wij(i_gap, iz) = root(i);
495 72000 : i++;
496 : }
497 : }
498 732 : }
499 :
500 : void
501 8622 : InterWrapper1PhaseProblem::computeSumWij(int iblock)
502 : {
503 8622 : unsigned int last_node = (iblock + 1) * _block_size;
504 8622 : unsigned int first_node = iblock * _block_size + 1;
505 : // Add to solution vector if explicit
506 8622 : if (!_implicit_bool)
507 : {
508 55608 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
509 : {
510 1815654 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
511 : {
512 1768032 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
513 : Real sumWij = 0.0;
514 : // Calculate sum of crossflow into channel i from channels j around i
515 : unsigned int counter = 0;
516 7482672 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
517 : {
518 5714640 : sumWij += _subchannel_mesh.getCrossflowSign(i_ch, counter) * _Wij(i_gap, iz);
519 5714640 : counter++;
520 : }
521 : // The net crossflow coming out of cell i [kg/sec]
522 1768032 : _SumWij_soln->set(node_out, sumWij);
523 : }
524 : }
525 : }
526 : // Add to matrix if implicit
527 : else
528 : {
529 4056 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
530 : {
531 3420 : unsigned int iz_ind = iz - first_node;
532 144900 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
533 : {
534 : // Calculate sum of crossflow into channel i from channels j around i
535 : unsigned int counter = 0;
536 551880 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
537 : {
538 410400 : PetscInt row = i_ch + _n_channels * iz_ind;
539 410400 : PetscInt col = i_gap + _n_gaps * iz_ind;
540 410400 : PetscScalar value = _subchannel_mesh.getCrossflowSign(i_ch, counter);
541 410400 : LibmeshPetscCall(MatSetValues(_mc_sumWij_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
542 410400 : counter++;
543 : }
544 : }
545 : }
546 636 : LibmeshPetscCall(MatAssemblyBegin(_mc_sumWij_mat, MAT_FINAL_ASSEMBLY));
547 636 : LibmeshPetscCall(MatAssemblyEnd(_mc_sumWij_mat, MAT_FINAL_ASSEMBLY));
548 636 : if (_segregated_bool)
549 : {
550 : Vec loc_prod;
551 : Vec loc_Wij;
552 588 : LibmeshPetscCall(VecDuplicate(_amc_sys_mdot_rhs, &loc_prod));
553 588 : LibmeshPetscCall(VecDuplicate(_Wij_vec, &loc_Wij));
554 588 : LibmeshPetscCall(populateVectorFromDense<libMesh::DenseMatrix<Real>>(
555 : loc_Wij, _Wij, first_node, last_node + 1, _n_gaps));
556 588 : LibmeshPetscCall(MatMult(_mc_sumWij_mat, loc_Wij, loc_prod));
557 588 : LibmeshPetscCall(populateSolutionChan<SolutionHandle>(
558 : loc_prod, *_SumWij_soln, first_node, last_node, _n_channels));
559 588 : LibmeshPetscCall(VecDestroy(&loc_prod));
560 588 : LibmeshPetscCall(VecDestroy(&loc_Wij));
561 : }
562 : }
563 8622 : }
564 :
565 : void
566 8622 : InterWrapper1PhaseProblem::computeMdot(int iblock)
567 : {
568 8622 : unsigned int last_node = (iblock + 1) * _block_size;
569 8622 : unsigned int first_node = iblock * _block_size + 1;
570 8622 : if (!_implicit_bool)
571 : {
572 55608 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
573 : {
574 47622 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
575 1815654 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
576 : {
577 1768032 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
578 1768032 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
579 1768032 : auto volume = dz * (*_S_flow_soln)(node_in);
580 1768032 : auto time_term = _TR * ((*_rho_soln)(node_out)-_rho_soln->old(node_out)) * volume / _dt;
581 : // Wij positive out of i into j;
582 1768032 : auto mdot_out = (*_mdot_soln)(node_in) - (*_SumWij_soln)(node_out)-time_term;
583 1768032 : if (mdot_out < 0)
584 : {
585 0 : _console << "Wij = : " << _Wij << "\n";
586 0 : mooseError(name(),
587 : " : Calculation of negative mass flow mdot_out = : ",
588 : mdot_out,
589 : " Axial Level= : ",
590 : iz,
591 : " - Implicit solves are required for recirculating flow.");
592 : }
593 1768032 : _mdot_soln->set(node_out, mdot_out); // kg/sec
594 : }
595 : }
596 : }
597 : else
598 : {
599 4056 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
600 : {
601 3420 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
602 3420 : auto iz_ind = iz - first_node;
603 144900 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
604 : {
605 141480 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
606 141480 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
607 141480 : auto volume = dz * (*_S_flow_soln)(node_in);
608 :
609 : // Adding time derivative to the RHS
610 141480 : auto time_term = _TR * ((*_rho_soln)(node_out)-_rho_soln->old(node_out)) * volume / _dt;
611 141480 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
612 141480 : PetscScalar value_vec = -1.0 * time_term;
613 141480 : LibmeshPetscCall(
614 : VecSetValues(_mc_axial_convection_rhs, 1, &row_vec, &value_vec, INSERT_VALUES));
615 :
616 : // Imposing bottom boundary condition or adding of diagonal elements
617 141480 : if (iz == first_node)
618 : {
619 26496 : PetscScalar value_vec = (*_mdot_soln)(node_in);
620 26496 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
621 26496 : LibmeshPetscCall(
622 : VecSetValues(_mc_axial_convection_rhs, 1, &row_vec, &value_vec, ADD_VALUES));
623 : }
624 : else
625 : {
626 114984 : PetscInt row = i_ch + _n_channels * iz_ind;
627 114984 : PetscInt col = i_ch + _n_channels * (iz_ind - 1);
628 114984 : PetscScalar value = -1.0;
629 114984 : LibmeshPetscCall(
630 : MatSetValues(_mc_axial_convection_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
631 : }
632 :
633 : // Adding diagonal elements
634 141480 : PetscInt row = i_ch + _n_channels * iz_ind;
635 141480 : PetscInt col = i_ch + _n_channels * iz_ind;
636 141480 : PetscScalar value = 1.0;
637 141480 : LibmeshPetscCall(
638 : MatSetValues(_mc_axial_convection_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
639 :
640 : // Adding cross flows RHS
641 141480 : if (_segregated_bool)
642 : {
643 123480 : PetscScalar value_vec_2 = -1.0 * (*_SumWij_soln)(node_out);
644 123480 : PetscInt row_vec_2 = i_ch + _n_channels * iz_ind;
645 123480 : LibmeshPetscCall(
646 : VecSetValues(_mc_axial_convection_rhs, 1, &row_vec_2, &value_vec_2, ADD_VALUES));
647 : }
648 : }
649 : }
650 636 : LibmeshPetscCall(MatAssemblyBegin(_mc_axial_convection_mat, MAT_FINAL_ASSEMBLY));
651 636 : LibmeshPetscCall(MatAssemblyEnd(_mc_axial_convection_mat, MAT_FINAL_ASSEMBLY));
652 636 : _console << "Block: " << iblock << " - Mass conservation matrix assembled" << std::endl;
653 :
654 636 : if (_segregated_bool)
655 : {
656 : KSP ksploc;
657 : PC pc;
658 : Vec sol;
659 588 : LibmeshPetscCall(VecDuplicate(_mc_axial_convection_rhs, &sol));
660 588 : LibmeshPetscCall(KSPCreate(PETSC_COMM_WORLD, &ksploc));
661 588 : LibmeshPetscCall(KSPSetOperators(ksploc, _mc_axial_convection_mat, _mc_axial_convection_mat));
662 588 : LibmeshPetscCall(KSPGetPC(ksploc, &pc));
663 588 : LibmeshPetscCall(PCSetType(pc, PCJACOBI));
664 588 : LibmeshPetscCall(KSPSetTolerances(ksploc, _rtol, _atol, _dtol, _maxit));
665 588 : LibmeshPetscCall(KSPSetOptionsPrefix(ksploc, "mass_sys_"));
666 588 : LibmeshPetscCall(KSPSetFromOptions(ksploc));
667 588 : LibmeshPetscCall(KSPSolve(ksploc, _mc_axial_convection_rhs, sol));
668 588 : LibmeshPetscCall(populateSolutionChan<SolutionHandle>(
669 : sol, *_mdot_soln, first_node, last_node, _n_channels));
670 588 : LibmeshPetscCall(VecZeroEntries(_mc_axial_convection_rhs));
671 588 : LibmeshPetscCall(KSPDestroy(&ksploc));
672 588 : LibmeshPetscCall(VecDestroy(&sol));
673 : }
674 : }
675 8622 : }
676 :
677 : void
678 8658 : InterWrapper1PhaseProblem::computeWijPrime(int iblock)
679 : {
680 8658 : unsigned int last_node = (iblock + 1) * _block_size;
681 8658 : unsigned int first_node = iblock * _block_size + 1;
682 :
683 8658 : if (!_implicit_bool)
684 : {
685 55608 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
686 : {
687 47622 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
688 2904942 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
689 : {
690 2857320 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
691 : unsigned int i_ch = chans.first;
692 : unsigned int j_ch = chans.second;
693 2857320 : auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
694 2857320 : auto * node_out_i = _subchannel_mesh.getChannelNode(i_ch, iz);
695 2857320 : auto * node_in_j = _subchannel_mesh.getChannelNode(j_ch, iz - 1);
696 2857320 : auto * node_out_j = _subchannel_mesh.getChannelNode(j_ch, iz);
697 2857320 : auto Si_in = (*_S_flow_soln)(node_in_i);
698 2857320 : auto Sj_in = (*_S_flow_soln)(node_in_j);
699 2857320 : auto Si_out = (*_S_flow_soln)(node_out_i);
700 2857320 : auto Sj_out = (*_S_flow_soln)(node_out_j);
701 : // crossflow area between channels i,j (dz*gap_width)
702 2857320 : auto Sij = dz * _subchannel_mesh.getGapWidth(i_gap);
703 : // Calculation of Turbulent Crossflow
704 2857320 : _WijPrime(i_gap, iz) =
705 2857320 : _beta * 0.5 *
706 2857320 : (((*_mdot_soln)(node_in_i) + (*_mdot_soln)(node_in_j)) / (Si_in + Sj_in) +
707 2857320 : ((*_mdot_soln)(node_out_i) + (*_mdot_soln)(node_out_j)) / (Si_out + Sj_out)) *
708 : Sij;
709 : }
710 : }
711 : }
712 : else
713 : {
714 4452 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
715 : {
716 3780 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
717 3780 : auto iz_ind = iz - first_node;
718 230580 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
719 : {
720 226800 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
721 : unsigned int i_ch = chans.first;
722 : unsigned int j_ch = chans.second;
723 226800 : auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
724 226800 : auto * node_out_i = _subchannel_mesh.getChannelNode(i_ch, iz);
725 226800 : auto * node_in_j = _subchannel_mesh.getChannelNode(j_ch, iz - 1);
726 226800 : auto * node_out_j = _subchannel_mesh.getChannelNode(j_ch, iz);
727 226800 : auto Si_in = (*_S_flow_soln)(node_in_i);
728 226800 : auto Sj_in = (*_S_flow_soln)(node_in_j);
729 226800 : auto Si_out = (*_S_flow_soln)(node_out_i);
730 226800 : auto Sj_out = (*_S_flow_soln)(node_out_j);
731 : // crossflow area between channels i,j (dz*gap_width)
732 226800 : auto Sij = dz * _subchannel_mesh.getGapWidth(i_gap);
733 :
734 : // Base value - I don't want to write it every time
735 226800 : PetscScalar base_value = _beta * 0.5 * Sij;
736 :
737 : // Bottom values
738 226800 : if (iz == first_node)
739 : {
740 40320 : PetscScalar value_tl = -1.0 * base_value / (Si_in + Sj_in) *
741 40320 : ((*_mdot_soln)(node_in_i) + (*_mdot_soln)(node_in_j));
742 40320 : PetscInt row = i_gap + _n_gaps * iz_ind;
743 40320 : LibmeshPetscCall(
744 : VecSetValues(_amc_turbulent_cross_flows_rhs, 1, &row, &value_tl, INSERT_VALUES));
745 : }
746 : else
747 : {
748 186480 : PetscScalar value_tl = base_value / (Si_in + Sj_in);
749 186480 : PetscInt row = i_gap + _n_gaps * iz_ind;
750 :
751 186480 : PetscInt col_ich = i_ch + _n_channels * (iz_ind - 1);
752 186480 : LibmeshPetscCall(MatSetValues(
753 : _amc_turbulent_cross_flows_mat, 1, &row, 1, &col_ich, &value_tl, INSERT_VALUES));
754 :
755 186480 : PetscInt col_jch = j_ch + _n_channels * (iz_ind - 1);
756 186480 : LibmeshPetscCall(MatSetValues(
757 : _amc_turbulent_cross_flows_mat, 1, &row, 1, &col_jch, &value_tl, INSERT_VALUES));
758 : }
759 :
760 : // Top values
761 226800 : PetscScalar value_bl = base_value / (Si_out + Sj_out);
762 226800 : PetscInt row = i_gap + _n_gaps * iz_ind;
763 :
764 226800 : PetscInt col_ich = i_ch + _n_channels * iz_ind;
765 226800 : LibmeshPetscCall(MatSetValues(
766 : _amc_turbulent_cross_flows_mat, 1, &row, 1, &col_ich, &value_bl, INSERT_VALUES));
767 :
768 226800 : PetscInt col_jch = j_ch + _n_channels * iz_ind;
769 226800 : LibmeshPetscCall(MatSetValues(
770 : _amc_turbulent_cross_flows_mat, 1, &row, 1, &col_jch, &value_bl, INSERT_VALUES));
771 : }
772 : }
773 672 : LibmeshPetscCall(MatAssemblyBegin(_amc_turbulent_cross_flows_mat, MAT_FINAL_ASSEMBLY));
774 672 : LibmeshPetscCall(MatAssemblyEnd(_amc_turbulent_cross_flows_mat, MAT_FINAL_ASSEMBLY));
775 :
776 : /// Update turbulent crossflow
777 : Vec loc_prod;
778 : Vec loc_Wij;
779 672 : LibmeshPetscCall(VecDuplicate(_amc_sys_mdot_rhs, &loc_prod));
780 672 : LibmeshPetscCall(VecDuplicate(_Wij_vec, &loc_Wij));
781 672 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
782 : loc_prod, *_mdot_soln, first_node, last_node, _n_channels));
783 672 : LibmeshPetscCall(MatMult(_amc_turbulent_cross_flows_mat, loc_prod, loc_Wij));
784 672 : LibmeshPetscCall(VecAXPY(loc_Wij, -1.0, _amc_turbulent_cross_flows_rhs));
785 672 : LibmeshPetscCall(populateDenseFromVector<libMesh::DenseMatrix<Real>>(
786 : loc_Wij, _WijPrime, first_node, last_node, _n_gaps));
787 672 : LibmeshPetscCall(VecDestroy(&loc_prod));
788 672 : LibmeshPetscCall(VecDestroy(&loc_Wij));
789 : }
790 8658 : }
791 :
792 : void
793 7128 : InterWrapper1PhaseProblem::computeDP(int iblock)
794 : {
795 7128 : unsigned int last_node = (iblock + 1) * _block_size;
796 7128 : unsigned int first_node = iblock * _block_size + 1;
797 :
798 7128 : if (!_implicit_bool)
799 : {
800 45774 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
801 : {
802 38682 : auto k_grid = _subchannel_mesh.getKGrid();
803 38682 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
804 1431234 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
805 : {
806 1392552 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
807 1392552 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
808 1392552 : auto rho_in = (*_rho_soln)(node_in);
809 1392552 : auto rho_out = (*_rho_soln)(node_out);
810 1392552 : auto mu_in = (*_mu_soln)(node_in);
811 1392552 : auto S = (*_S_flow_soln)(node_in);
812 1392552 : auto w_perim = (*_w_perim_soln)(node_in);
813 : // hydraulic diameter in the i direction
814 1392552 : auto Dh_i = 4.0 * S / w_perim;
815 1392552 : auto time_term = _TR * ((*_mdot_soln)(node_out)-_mdot_soln->old(node_out)) * dz / _dt -
816 1392552 : dz * 2.0 * (*_mdot_soln)(node_out) * (rho_out - _rho_soln->old(node_out)) /
817 1392552 : rho_in / _dt;
818 : auto mass_term1 =
819 1392552 : std::pow((*_mdot_soln)(node_out), 2.0) * (1.0 / S / rho_out - 1.0 / S / rho_in);
820 1392552 : auto mass_term2 = -2.0 * (*_mdot_soln)(node_out) * (*_SumWij_soln)(node_out) / S / rho_in;
821 : auto crossflow_term = 0.0;
822 : auto turbulent_term = 0.0;
823 : unsigned int counter = 0;
824 6034392 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
825 : {
826 4641840 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
827 : unsigned int ii_ch = chans.first;
828 : unsigned int jj_ch = chans.second;
829 4641840 : auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
830 4641840 : auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
831 4641840 : auto * node_out_i = _subchannel_mesh.getChannelNode(ii_ch, iz);
832 4641840 : auto * node_out_j = _subchannel_mesh.getChannelNode(jj_ch, iz);
833 4641840 : auto rho_i = (*_rho_soln)(node_in_i);
834 4641840 : auto rho_j = (*_rho_soln)(node_in_j);
835 4641840 : auto Si = (*_S_flow_soln)(node_in_i);
836 4641840 : auto Sj = (*_S_flow_soln)(node_in_j);
837 : Real u_star = 0.0;
838 : // figure out donor axial velocity
839 4641840 : if (_Wij(i_gap, iz) > 0.0)
840 2047968 : u_star = (*_mdot_soln)(node_out_i) / Si / rho_i;
841 : else
842 2593872 : u_star = (*_mdot_soln)(node_out_j) / Sj / rho_j;
843 :
844 4641840 : crossflow_term +=
845 4641840 : _subchannel_mesh.getCrossflowSign(i_ch, counter) * _Wij(i_gap, iz) * u_star;
846 :
847 4641840 : turbulent_term += _WijPrime(i_gap, iz) * (2 * (*_mdot_soln)(node_out) / rho_in / S -
848 4641840 : (*_mdot_soln)(node_out_j) / Sj / rho_j -
849 4641840 : (*_mdot_soln)(node_out_i) / Si / rho_i);
850 4641840 : counter++;
851 : }
852 1392552 : turbulent_term *= _CT;
853 1392552 : auto Re = (((*_mdot_soln)(node_in) / S) * Dh_i / mu_in);
854 1392552 : auto fi = computeFrictionFactor(Re);
855 1392552 : auto ki = k_grid[i_ch][iz - 1];
856 1392552 : auto friction_term = (fi * dz / Dh_i + ki) * 0.5 *
857 1392552 : (std::pow((*_mdot_soln)(node_out), 2.0)) /
858 1392552 : (S * (*_rho_soln)(node_out));
859 1392552 : auto gravity_term = _g_grav * (*_rho_soln)(node_out)*dz * S;
860 1392552 : auto DP = std::pow(S, -1.0) * (time_term + mass_term1 + mass_term2 + crossflow_term +
861 1392552 : turbulent_term + friction_term + gravity_term); // Pa
862 1392552 : _DP_soln->set(node_out, DP);
863 : }
864 38682 : }
865 : }
866 : else
867 : {
868 36 : LibmeshPetscCall(MatZeroEntries(_amc_time_derivative_mat));
869 36 : LibmeshPetscCall(MatZeroEntries(_amc_advective_derivative_mat));
870 36 : LibmeshPetscCall(MatZeroEntries(_amc_cross_derivative_mat));
871 36 : LibmeshPetscCall(MatZeroEntries(_amc_friction_force_mat));
872 36 : LibmeshPetscCall(VecZeroEntries(_amc_time_derivative_rhs));
873 36 : LibmeshPetscCall(VecZeroEntries(_amc_advective_derivative_rhs));
874 36 : LibmeshPetscCall(VecZeroEntries(_amc_cross_derivative_rhs));
875 36 : LibmeshPetscCall(VecZeroEntries(_amc_friction_force_rhs));
876 36 : LibmeshPetscCall(VecZeroEntries(_amc_gravity_rhs));
877 36 : LibmeshPetscCall(MatZeroEntries(_amc_sys_mdot_mat));
878 36 : LibmeshPetscCall(VecZeroEntries(_amc_sys_mdot_rhs));
879 396 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
880 : {
881 360 : auto k_grid = _subchannel_mesh.getKGrid();
882 360 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
883 360 : auto iz_ind = iz - first_node;
884 13320 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
885 : {
886 : // inlet and outlet nodes
887 12960 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
888 12960 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
889 :
890 : // interpolation weight coefficient
891 : PetscScalar Pe = 0.5;
892 12960 : auto alpha = computeInterpolationCoefficients(Pe);
893 :
894 : // inlet, outlet, and interpolated density
895 12960 : auto rho_in = (*_rho_soln)(node_in);
896 12960 : auto rho_out = (*_rho_soln)(node_out);
897 12960 : auto rho_interp = computeInterpolatedValue(rho_out, rho_in, Pe);
898 :
899 : // inlet, outlet, and interpolated viscosity
900 12960 : auto mu_in = (*_mu_soln)(node_in);
901 12960 : auto mu_out = (*_mu_soln)(node_out);
902 12960 : auto mu_interp = computeInterpolatedValue(mu_out, mu_in, Pe);
903 :
904 : // inlet, outlet, and interpolated axial surface area
905 12960 : auto S_in = (*_S_flow_soln)(node_in);
906 12960 : auto S_out = (*_S_flow_soln)(node_out);
907 12960 : auto S_interp = computeInterpolatedValue(S_out, S_in, Pe);
908 :
909 : // inlet, outlet, and interpolated wetted perimeter
910 12960 : auto w_perim_in = (*_w_perim_soln)(node_in);
911 12960 : auto w_perim_out = (*_w_perim_soln)(node_out);
912 12960 : auto w_perim_interp = computeInterpolatedValue(w_perim_out, w_perim_in, Pe);
913 :
914 : // hydraulic diameter in the i direction
915 12960 : auto Dh_i = 4.0 * S_interp / w_perim_interp;
916 :
917 : /// Time derivative term
918 12960 : if (iz == first_node)
919 : {
920 1296 : PetscScalar value_vec_tt = -1.0 * _TR * alpha * (*_mdot_soln)(node_in)*dz / _dt;
921 1296 : PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
922 1296 : LibmeshPetscCall(
923 : VecSetValues(_amc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
924 : }
925 : else
926 : {
927 11664 : PetscInt row_tt = i_ch + _n_channels * iz_ind;
928 11664 : PetscInt col_tt = i_ch + _n_channels * (iz_ind - 1);
929 11664 : PetscScalar value_tt = _TR * alpha * dz / _dt;
930 11664 : LibmeshPetscCall(MatSetValues(
931 : _amc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
932 : }
933 :
934 : // Adding diagonal elements
935 12960 : PetscInt row_tt = i_ch + _n_channels * iz_ind;
936 12960 : PetscInt col_tt = i_ch + _n_channels * iz_ind;
937 12960 : PetscScalar value_tt = _TR * (1.0 - alpha) * dz / _dt;
938 12960 : LibmeshPetscCall(MatSetValues(
939 : _amc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
940 :
941 : // Adding RHS elements
942 : PetscScalar mdot_old_interp =
943 12960 : computeInterpolatedValue(_mdot_soln->old(node_out), _mdot_soln->old(node_in), Pe);
944 12960 : PetscScalar value_vec_tt = _TR * mdot_old_interp * dz / _dt;
945 12960 : PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
946 12960 : LibmeshPetscCall(
947 : VecSetValues(_amc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
948 :
949 : /// Advective derivative term
950 12960 : if (iz == first_node)
951 : {
952 1296 : PetscScalar value_vec_at = std::pow((*_mdot_soln)(node_in), 2.0) / (S_in * rho_in);
953 1296 : PetscInt row_vec_at = i_ch + _n_channels * iz_ind;
954 1296 : LibmeshPetscCall(VecSetValues(
955 : _amc_advective_derivative_rhs, 1, &row_vec_at, &value_vec_at, ADD_VALUES));
956 : }
957 : else
958 : {
959 11664 : PetscInt row_at = i_ch + _n_channels * iz_ind;
960 11664 : PetscInt col_at = i_ch + _n_channels * (iz_ind - 1);
961 11664 : PetscScalar value_at = -1.0 * std::abs((*_mdot_soln)(node_in)) / (S_in * rho_in);
962 11664 : LibmeshPetscCall(MatSetValues(
963 : _amc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
964 : }
965 :
966 : // Adding diagonal elements
967 12960 : PetscInt row_at = i_ch + _n_channels * iz_ind;
968 12960 : PetscInt col_at = i_ch + _n_channels * iz_ind;
969 12960 : PetscScalar value_at = std::abs((*_mdot_soln)(node_out)) / (S_out * rho_out);
970 12960 : LibmeshPetscCall(MatSetValues(
971 : _amc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
972 :
973 : /// Cross derivative term
974 : unsigned int counter = 0;
975 : unsigned int cross_index = iz; // iz-1;
976 56160 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
977 : {
978 43200 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
979 : unsigned int ii_ch = chans.first;
980 : unsigned int jj_ch = chans.second;
981 43200 : auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
982 43200 : auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
983 43200 : auto * node_out_i = _subchannel_mesh.getChannelNode(ii_ch, iz);
984 43200 : auto * node_out_j = _subchannel_mesh.getChannelNode(jj_ch, iz);
985 : auto rho_i =
986 43200 : computeInterpolatedValue((*_rho_soln)(node_out_i), (*_rho_soln)(node_in_i), Pe);
987 : auto rho_j =
988 43200 : computeInterpolatedValue((*_rho_soln)(node_out_j), (*_rho_soln)(node_in_j), Pe);
989 : auto S_i =
990 43200 : computeInterpolatedValue((*_S_flow_soln)(node_out_i), (*_S_flow_soln)(node_in_i), Pe);
991 : auto S_j =
992 43200 : computeInterpolatedValue((*_S_flow_soln)(node_out_j), (*_S_flow_soln)(node_in_j), Pe);
993 : auto u_star = 0.0;
994 : // figure out donor axial velocity
995 43200 : if (_Wij(i_gap, cross_index) > 0.0)
996 : {
997 10704 : if (iz == first_node)
998 : {
999 1200 : u_star = (*_mdot_soln)(node_in_i) / S_i / rho_i;
1000 2400 : PetscScalar value_vec_ct = -1.0 * alpha *
1001 1200 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1002 1200 : _Wij(i_gap, cross_index) * u_star;
1003 1200 : PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
1004 1200 : LibmeshPetscCall(VecSetValues(
1005 : _amc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
1006 : }
1007 : else
1008 : {
1009 9504 : PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1010 9504 : _Wij(i_gap, cross_index) / S_i / rho_i;
1011 9504 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1012 9504 : PetscInt col_ct = ii_ch + _n_channels * (iz_ind - 1);
1013 9504 : LibmeshPetscCall(MatSetValues(
1014 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1015 : }
1016 10704 : PetscScalar value_ct = (1.0 - alpha) *
1017 10704 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1018 10704 : _Wij(i_gap, cross_index) / S_i / rho_i;
1019 10704 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1020 10704 : PetscInt col_ct = ii_ch + _n_channels * iz_ind;
1021 10704 : LibmeshPetscCall(MatSetValues(
1022 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1023 : }
1024 32496 : else if (_Wij(i_gap, cross_index) < 0.0) // _Wij=0 operations not necessary
1025 : {
1026 10464 : if (iz == first_node)
1027 : {
1028 1152 : u_star = (*_mdot_soln)(node_in_j) / S_j / rho_j;
1029 2304 : PetscScalar value_vec_ct = -1.0 * alpha *
1030 1152 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1031 1152 : _Wij(i_gap, cross_index) * u_star;
1032 1152 : PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
1033 1152 : LibmeshPetscCall(VecSetValues(
1034 : _amc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
1035 : }
1036 : else
1037 : {
1038 9312 : PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1039 9312 : _Wij(i_gap, cross_index) / S_j / rho_j;
1040 9312 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1041 9312 : PetscInt col_ct = jj_ch + _n_channels * (iz_ind - 1);
1042 9312 : LibmeshPetscCall(MatSetValues(
1043 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1044 : }
1045 10464 : PetscScalar value_ct = (1.0 - alpha) *
1046 10464 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1047 10464 : _Wij(i_gap, cross_index) / S_j / rho_j;
1048 10464 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1049 10464 : PetscInt col_ct = jj_ch + _n_channels * iz_ind;
1050 10464 : LibmeshPetscCall(MatSetValues(
1051 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1052 : }
1053 :
1054 43200 : if (iz == first_node)
1055 : {
1056 4320 : PetscScalar value_vec_ct = -2.0 * alpha *
1057 4320 : (*_mdot_soln)(node_in)*_WijPrime(i_gap, cross_index) /
1058 4320 : (rho_interp * S_interp);
1059 4320 : value_vec_ct +=
1060 4320 : alpha * (*_mdot_soln)(node_in_j)*_WijPrime(i_gap, cross_index) / (rho_j * S_j);
1061 4320 : value_vec_ct +=
1062 4320 : alpha * (*_mdot_soln)(node_in_i)*_WijPrime(i_gap, cross_index) / (rho_i * S_i);
1063 4320 : PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
1064 4320 : LibmeshPetscCall(
1065 : VecSetValues(_amc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
1066 : }
1067 : else
1068 : {
1069 : PetscScalar value_center_ct =
1070 38880 : 2.0 * alpha * _WijPrime(i_gap, cross_index) / (rho_interp * S_interp);
1071 38880 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1072 38880 : PetscInt col_ct = i_ch + _n_channels * (iz_ind - 1);
1073 38880 : LibmeshPetscCall(MatSetValues(
1074 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
1075 :
1076 : PetscScalar value_left_ct =
1077 38880 : -1.0 * alpha * _WijPrime(i_gap, cross_index) / (rho_j * S_j);
1078 38880 : row_ct = i_ch + _n_channels * iz_ind;
1079 38880 : col_ct = jj_ch + _n_channels * (iz_ind - 1);
1080 38880 : LibmeshPetscCall(MatSetValues(
1081 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
1082 :
1083 : PetscScalar value_right_ct =
1084 38880 : -1.0 * alpha * _WijPrime(i_gap, cross_index) / (rho_i * S_i);
1085 38880 : row_ct = i_ch + _n_channels * iz_ind;
1086 38880 : col_ct = ii_ch + _n_channels * (iz_ind - 1);
1087 38880 : LibmeshPetscCall(MatSetValues(
1088 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
1089 : }
1090 :
1091 : PetscScalar value_center_ct =
1092 43200 : 2.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index) / (rho_interp * S_interp);
1093 43200 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1094 43200 : PetscInt col_ct = i_ch + _n_channels * iz_ind;
1095 43200 : LibmeshPetscCall(MatSetValues(
1096 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
1097 :
1098 : PetscScalar value_left_ct =
1099 43200 : -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index) / (rho_j * S_j);
1100 43200 : row_ct = i_ch + _n_channels * iz_ind;
1101 43200 : col_ct = jj_ch + _n_channels * iz_ind;
1102 43200 : LibmeshPetscCall(MatSetValues(
1103 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
1104 :
1105 : PetscScalar value_right_ct =
1106 43200 : -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index) / (rho_i * S_i);
1107 43200 : row_ct = i_ch + _n_channels * iz_ind;
1108 43200 : col_ct = ii_ch + _n_channels * iz_ind;
1109 43200 : LibmeshPetscCall(MatSetValues(
1110 : _amc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
1111 :
1112 43200 : counter++;
1113 : }
1114 :
1115 : /// Friction term
1116 : PetscScalar mdot_interp =
1117 12960 : computeInterpolatedValue((*_mdot_soln)(node_out), (*_mdot_soln)(node_in), Pe);
1118 12960 : auto Re = ((mdot_interp / S_interp) * Dh_i / mu_interp);
1119 12960 : auto fi = computeFrictionFactor(Re);
1120 12960 : auto ki = computeInterpolatedValue(k_grid[i_ch][iz], k_grid[i_ch][iz - 1], Pe);
1121 12960 : auto coef = (fi * dz / Dh_i + ki) * 0.5 * std::abs((*_mdot_soln)(node_out)) /
1122 12960 : (S_interp * rho_interp);
1123 12960 : if (iz == first_node)
1124 : {
1125 1296 : PetscScalar value_vec = -1.0 * alpha * coef * (*_mdot_soln)(node_in);
1126 1296 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
1127 1296 : LibmeshPetscCall(
1128 : VecSetValues(_amc_friction_force_rhs, 1, &row_vec, &value_vec, ADD_VALUES));
1129 : }
1130 : else
1131 : {
1132 11664 : PetscInt row = i_ch + _n_channels * iz_ind;
1133 11664 : PetscInt col = i_ch + _n_channels * (iz_ind - 1);
1134 11664 : PetscScalar value = alpha * coef;
1135 11664 : LibmeshPetscCall(
1136 : MatSetValues(_amc_friction_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1137 : }
1138 :
1139 : // Adding diagonal elements
1140 12960 : PetscInt row = i_ch + _n_channels * iz_ind;
1141 12960 : PetscInt col = i_ch + _n_channels * iz_ind;
1142 12960 : PetscScalar value = (1.0 - alpha) * coef;
1143 12960 : LibmeshPetscCall(
1144 : MatSetValues(_amc_friction_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1145 :
1146 : /// Gravity force
1147 12960 : PetscScalar value_vec = -1.0 * _g_grav * rho_interp * dz * S_interp;
1148 12960 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
1149 12960 : LibmeshPetscCall(VecSetValues(_amc_gravity_rhs, 1, &row_vec, &value_vec, ADD_VALUES));
1150 : }
1151 360 : }
1152 : /// Assembling system
1153 36 : LibmeshPetscCall(MatZeroEntries(_amc_sys_mdot_mat));
1154 36 : LibmeshPetscCall(VecZeroEntries(_amc_sys_mdot_rhs));
1155 36 : LibmeshPetscCall(MatAssemblyBegin(_amc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1156 36 : LibmeshPetscCall(MatAssemblyEnd(_amc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1157 36 : LibmeshPetscCall(MatAssemblyBegin(_amc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1158 36 : LibmeshPetscCall(MatAssemblyEnd(_amc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1159 36 : LibmeshPetscCall(MatAssemblyBegin(_amc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
1160 36 : LibmeshPetscCall(MatAssemblyEnd(_amc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
1161 36 : LibmeshPetscCall(MatAssemblyBegin(_amc_friction_force_mat, MAT_FINAL_ASSEMBLY));
1162 36 : LibmeshPetscCall(MatAssemblyEnd(_amc_friction_force_mat, MAT_FINAL_ASSEMBLY));
1163 36 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1164 36 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1165 : // Matrix
1166 : #if !PETSC_VERSION_LESS_THAN(3, 15, 0)
1167 36 : LibmeshPetscCall(
1168 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_time_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1169 36 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1170 36 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1171 36 : LibmeshPetscCall(
1172 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_advective_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1173 36 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1174 36 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1175 36 : LibmeshPetscCall(
1176 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_friction_force_mat, UNKNOWN_NONZERO_PATTERN));
1177 : #else
1178 : LibmeshPetscCall(
1179 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_time_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1180 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1181 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1182 : LibmeshPetscCall(
1183 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_advective_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1184 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1185 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1186 : LibmeshPetscCall(
1187 : MatAXPY(_amc_sys_mdot_mat, 1.0, _amc_friction_force_mat, DIFFERENT_NONZERO_PATTERN));
1188 : #endif
1189 36 : LibmeshPetscCall(MatAssemblyBegin(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1190 36 : LibmeshPetscCall(MatAssemblyEnd(_amc_sys_mdot_mat, MAT_FINAL_ASSEMBLY));
1191 36 : _console << "Block: " << iblock << " - Linear momentum conservation matrix assembled"
1192 36 : << std::endl;
1193 : // RHS
1194 36 : LibmeshPetscCall(VecAXPY(_amc_sys_mdot_rhs, 1.0, _amc_time_derivative_rhs));
1195 36 : LibmeshPetscCall(VecAXPY(_amc_sys_mdot_rhs, 1.0, _amc_advective_derivative_rhs));
1196 36 : LibmeshPetscCall(VecAXPY(_amc_sys_mdot_rhs, 1.0, _amc_friction_force_rhs));
1197 36 : LibmeshPetscCall(VecAXPY(_amc_sys_mdot_rhs, 1.0, _amc_gravity_rhs));
1198 :
1199 36 : if (_segregated_bool)
1200 : {
1201 : // Assembly the matrix system
1202 0 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
1203 : _prod, *_mdot_soln, first_node, last_node, _n_channels));
1204 : Vec ls;
1205 0 : LibmeshPetscCall(VecDuplicate(_amc_sys_mdot_rhs, &ls));
1206 0 : LibmeshPetscCall(MatMult(_amc_sys_mdot_mat, _prod, ls));
1207 0 : LibmeshPetscCall(VecAXPY(ls, -1.0, _amc_sys_mdot_rhs));
1208 : PetscScalar * xx;
1209 0 : LibmeshPetscCall(VecGetArray(ls, &xx));
1210 0 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1211 : {
1212 0 : auto iz_ind = iz - first_node;
1213 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1214 : {
1215 : // Setting nodes
1216 0 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1217 0 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1218 :
1219 : // inlet, outlet, and interpolated axial surface area
1220 0 : auto S_in = (*_S_flow_soln)(node_in);
1221 0 : auto S_out = (*_S_flow_soln)(node_out);
1222 0 : auto S_interp = computeInterpolatedValue(S_out, S_in);
1223 :
1224 : // Setting solutions
1225 0 : if (S_interp != 0)
1226 : {
1227 0 : auto DP = std::pow(S_interp, -1.0) * xx[iz_ind * _n_channels + i_ch];
1228 0 : _DP_soln->set(node_out, DP);
1229 : }
1230 : else
1231 : {
1232 : auto DP = 0.0;
1233 0 : _DP_soln->set(node_out, DP);
1234 : }
1235 : }
1236 : }
1237 0 : LibmeshPetscCall(VecDestroy(&ls));
1238 : }
1239 : }
1240 7128 : }
1241 :
1242 : void
1243 8622 : InterWrapper1PhaseProblem::computeP(int iblock)
1244 : {
1245 8622 : unsigned int last_node = (iblock + 1) * _block_size;
1246 8622 : unsigned int first_node = iblock * _block_size + 1;
1247 8622 : if (!_implicit_bool)
1248 : {
1249 7986 : if (!_staggered_pressure_bool)
1250 : {
1251 55608 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1252 : {
1253 : // Calculate pressure in the inlet of the cell assuming known outlet
1254 1815654 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1255 : {
1256 1768032 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1257 1768032 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1258 : // update Pressure solution
1259 1768032 : _P_soln->set(node_in, (*_P_soln)(node_out) + (*_DP_soln)(node_out));
1260 : }
1261 : }
1262 : }
1263 : else
1264 : {
1265 0 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1266 : {
1267 : // Calculate pressure in the inlet of the cell assuming known outlet
1268 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1269 : {
1270 0 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1271 0 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1272 : // update Pressure solution
1273 : // Note: assuming uniform axial discretization in the curren code
1274 : // We will need to update this later if we allow non-uniform refinements in the axial
1275 : // direction
1276 : PetscScalar Pe = 0.5;
1277 0 : auto alpha = computeInterpolationCoefficients(Pe);
1278 0 : if (iz == last_node)
1279 : {
1280 0 : _P_soln->set(node_in, (*_P_soln)(node_out) + (*_DP_soln)(node_out) / 2.0);
1281 : }
1282 : else
1283 : {
1284 0 : _P_soln->set(node_in,
1285 0 : (*_P_soln)(node_out) + (1.0 - alpha) * (*_DP_soln)(node_out) +
1286 0 : alpha * (*_DP_soln)(node_in));
1287 : }
1288 : }
1289 : }
1290 : }
1291 : }
1292 : else
1293 : {
1294 636 : if (!_staggered_pressure_bool)
1295 : {
1296 612 : LibmeshPetscCall(VecZeroEntries(_amc_pressure_force_rhs));
1297 3792 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1298 : {
1299 3180 : auto iz_ind = iz - first_node;
1300 : // Calculate pressure in the inlet of the cell assuming known outlet
1301 135660 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1302 : {
1303 132480 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1304 132480 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1305 :
1306 : // inlet, outlet, and interpolated axial surface area
1307 132480 : auto S_in = (*_S_flow_soln)(node_in);
1308 132480 : auto S_out = (*_S_flow_soln)(node_out);
1309 132480 : auto S_interp = computeInterpolatedValue(S_out, S_in);
1310 :
1311 : // Creating matrix of coefficients and RHS vector
1312 132480 : PetscInt row = i_ch + _n_channels * iz_ind;
1313 132480 : PetscInt col = i_ch + _n_channels * iz_ind;
1314 132480 : PetscScalar value = -1.0 * S_interp;
1315 132480 : LibmeshPetscCall(
1316 : MatSetValues(_amc_pressure_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1317 :
1318 132480 : if (iz == last_node)
1319 : {
1320 25596 : PetscScalar value = -1.0 * (*_P_soln)(node_out)*S_interp;
1321 25596 : PetscInt row = i_ch + _n_channels * iz_ind;
1322 25596 : LibmeshPetscCall(VecSetValues(_amc_pressure_force_rhs, 1, &row, &value, ADD_VALUES));
1323 : }
1324 : else
1325 : {
1326 106884 : PetscInt row = i_ch + _n_channels * iz_ind;
1327 106884 : PetscInt col = i_ch + _n_channels * (iz_ind + 1);
1328 106884 : PetscScalar value = 1.0 * S_interp;
1329 106884 : LibmeshPetscCall(
1330 : MatSetValues(_amc_pressure_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1331 : }
1332 :
1333 132480 : if (_segregated_bool)
1334 : {
1335 123480 : auto dp_out = (*_DP_soln)(node_out);
1336 123480 : PetscScalar value_v = -1.0 * dp_out * S_interp;
1337 123480 : PetscInt row_v = i_ch + _n_channels * iz_ind;
1338 123480 : LibmeshPetscCall(
1339 : VecSetValues(_amc_pressure_force_rhs, 1, &row_v, &value_v, ADD_VALUES));
1340 : }
1341 : }
1342 : }
1343 : // Solving pressure problem
1344 612 : LibmeshPetscCall(MatAssemblyBegin(_amc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1345 612 : LibmeshPetscCall(MatAssemblyEnd(_amc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1346 612 : if (_segregated_bool)
1347 : {
1348 : KSP ksploc;
1349 : PC pc;
1350 : Vec sol;
1351 588 : LibmeshPetscCall(VecDuplicate(_amc_pressure_force_rhs, &sol));
1352 588 : LibmeshPetscCall(KSPCreate(PETSC_COMM_WORLD, &ksploc));
1353 588 : LibmeshPetscCall(KSPSetOperators(ksploc, _amc_pressure_force_mat, _amc_pressure_force_mat));
1354 588 : LibmeshPetscCall(KSPGetPC(ksploc, &pc));
1355 588 : LibmeshPetscCall(PCSetType(pc, PCJACOBI));
1356 588 : LibmeshPetscCall(KSPSetTolerances(ksploc, _rtol, _atol, _dtol, _maxit));
1357 588 : LibmeshPetscCall(KSPSetOptionsPrefix(ksploc, "pressure_sys_"));
1358 588 : LibmeshPetscCall(KSPSetFromOptions(ksploc));
1359 588 : LibmeshPetscCall(KSPSolve(ksploc, _amc_pressure_force_rhs, sol));
1360 : PetscScalar * xx;
1361 588 : LibmeshPetscCall(VecGetArray(sol, &xx));
1362 : // update Pressure solution
1363 3528 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1364 : {
1365 2940 : auto iz_ind = iz - first_node;
1366 126420 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1367 : {
1368 123480 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1369 123480 : PetscScalar value = xx[iz_ind * _n_channels + i_ch];
1370 123480 : _P_soln->set(node_in, value);
1371 : }
1372 : }
1373 588 : LibmeshPetscCall(VecZeroEntries(_amc_pressure_force_rhs));
1374 588 : LibmeshPetscCall(KSPDestroy(&ksploc));
1375 588 : LibmeshPetscCall(VecDestroy(&sol));
1376 : }
1377 : }
1378 : else
1379 : {
1380 24 : LibmeshPetscCall(VecZeroEntries(_amc_pressure_force_rhs));
1381 264 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1382 : {
1383 240 : auto iz_ind = iz - first_node;
1384 : // Calculate pressure in the inlet of the cell assuming known outlet
1385 9240 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1386 : {
1387 9000 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1388 9000 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1389 :
1390 : // inlet, outlet, and interpolated axial surface area
1391 9000 : auto S_in = (*_S_flow_soln)(node_in);
1392 9000 : auto S_out = (*_S_flow_soln)(node_out);
1393 9000 : auto S_interp = computeInterpolatedValue(S_out, S_in);
1394 :
1395 : // Creating matrix of coefficients
1396 9000 : PetscInt row = i_ch + _n_channels * iz_ind;
1397 9000 : PetscInt col = i_ch + _n_channels * iz_ind;
1398 9000 : PetscScalar value = -1.0 * S_interp;
1399 9000 : LibmeshPetscCall(
1400 : MatSetValues(_amc_pressure_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1401 :
1402 9000 : if (iz == last_node)
1403 : {
1404 900 : PetscScalar value = -1.0 * (*_P_soln)(node_out)*S_interp;
1405 900 : PetscInt row = i_ch + _n_channels * iz_ind;
1406 900 : LibmeshPetscCall(VecSetValues(_amc_pressure_force_rhs, 1, &row, &value, ADD_VALUES));
1407 :
1408 900 : auto dp_out = (*_DP_soln)(node_out);
1409 900 : PetscScalar value_v = -1.0 * dp_out / 2.0 * S_interp;
1410 900 : PetscInt row_v = i_ch + _n_channels * iz_ind;
1411 900 : LibmeshPetscCall(
1412 : VecSetValues(_amc_pressure_force_rhs, 1, &row_v, &value_v, ADD_VALUES));
1413 : }
1414 : else
1415 : {
1416 8100 : PetscInt row = i_ch + _n_channels * iz_ind;
1417 8100 : PetscInt col = i_ch + _n_channels * (iz_ind + 1);
1418 8100 : PetscScalar value = 1.0 * S_interp;
1419 8100 : LibmeshPetscCall(
1420 : MatSetValues(_amc_pressure_force_mat, 1, &row, 1, &col, &value, INSERT_VALUES));
1421 :
1422 8100 : if (_segregated_bool)
1423 : {
1424 0 : auto dp_in = (*_DP_soln)(node_in);
1425 0 : auto dp_out = (*_DP_soln)(node_out);
1426 0 : auto dp_interp = computeInterpolatedValue(dp_out, dp_in);
1427 0 : PetscScalar value_v = -1.0 * dp_interp * S_interp;
1428 0 : PetscInt row_v = i_ch + _n_channels * iz_ind;
1429 0 : LibmeshPetscCall(
1430 : VecSetValues(_amc_pressure_force_rhs, 1, &row_v, &value_v, ADD_VALUES));
1431 : }
1432 : }
1433 : }
1434 : }
1435 : // Solving pressure problem
1436 24 : LibmeshPetscCall(MatAssemblyBegin(_amc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1437 24 : LibmeshPetscCall(MatAssemblyEnd(_amc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
1438 24 : _console << "Block: " << iblock << " - Axial momentum pressure force matrix assembled"
1439 24 : << std::endl;
1440 :
1441 24 : if (_segregated_bool)
1442 : {
1443 : KSP ksploc;
1444 : PC pc;
1445 : Vec sol;
1446 0 : LibmeshPetscCall(VecDuplicate(_amc_pressure_force_rhs, &sol));
1447 0 : LibmeshPetscCall(KSPCreate(PETSC_COMM_WORLD, &ksploc));
1448 0 : LibmeshPetscCall(KSPSetOperators(ksploc, _amc_pressure_force_mat, _amc_pressure_force_mat));
1449 0 : LibmeshPetscCall(KSPGetPC(ksploc, &pc));
1450 0 : LibmeshPetscCall(PCSetType(pc, PCJACOBI));
1451 0 : LibmeshPetscCall(KSPSetTolerances(ksploc, _rtol, _atol, _dtol, _maxit));
1452 0 : LibmeshPetscCall(KSPSetOptionsPrefix(ksploc, "axial_mom_sys_"));
1453 0 : LibmeshPetscCall(KSPSetFromOptions(ksploc));
1454 0 : LibmeshPetscCall(KSPSolve(ksploc, _amc_pressure_force_rhs, sol));
1455 : PetscScalar * xx;
1456 0 : LibmeshPetscCall(VecGetArray(sol, &xx));
1457 : // update Pressure solution
1458 0 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
1459 : {
1460 0 : auto iz_ind = iz - first_node;
1461 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1462 : {
1463 0 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1464 0 : PetscScalar value = xx[iz_ind * _n_channels + i_ch];
1465 0 : _P_soln->set(node_in, value);
1466 : }
1467 : }
1468 0 : LibmeshPetscCall(VecZeroEntries(_amc_pressure_force_rhs));
1469 0 : LibmeshPetscCall(KSPDestroy(&ksploc));
1470 0 : LibmeshPetscCall(VecDestroy(&sol));
1471 : }
1472 : }
1473 : }
1474 8622 : }
1475 :
1476 : Real
1477 0 : InterWrapper1PhaseProblem::computeAddedHeat(unsigned int i_ch, unsigned int iz)
1478 : {
1479 0 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
1480 0 : if (_pin_mesh_exist)
1481 : {
1482 : auto heat_rate_in = 0.0;
1483 : auto heat_rate_out = 0.0;
1484 0 : for (auto i_pin : _subchannel_mesh.getChannelPins(i_ch))
1485 : {
1486 0 : auto * node_in = _subchannel_mesh.getPinNode(i_pin, iz - 1);
1487 0 : auto * node_out = _subchannel_mesh.getPinNode(i_pin, iz);
1488 0 : heat_rate_out += 0.25 * (*_q_prime_soln)(node_out);
1489 0 : heat_rate_in += 0.25 * (*_q_prime_soln)(node_in);
1490 : }
1491 0 : return (heat_rate_in + heat_rate_out) * dz / 2.0;
1492 : }
1493 : else
1494 : {
1495 0 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1496 0 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1497 0 : return ((*_q_prime_soln)(node_out) + (*_q_prime_soln)(node_in)) * dz / 2.0;
1498 : }
1499 : }
1500 :
1501 : void
1502 0 : InterWrapper1PhaseProblem::computeh(int iblock)
1503 : {
1504 0 : unsigned int last_node = (iblock + 1) * _block_size;
1505 0 : unsigned int first_node = iblock * _block_size + 1;
1506 :
1507 0 : if (iblock == 0)
1508 : {
1509 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1510 : {
1511 0 : auto * node = _subchannel_mesh.getChannelNode(i_ch, 0);
1512 0 : auto h_out = _fp->h_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
1513 0 : if (h_out < 0)
1514 : {
1515 0 : mooseError(
1516 0 : name(), " : Calculation of negative Enthalpy h_out = : ", h_out, " Axial Level= : ", 0);
1517 : }
1518 0 : _h_soln->set(node, h_out);
1519 : }
1520 : }
1521 :
1522 0 : if (!_implicit_bool)
1523 : {
1524 0 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1525 : {
1526 0 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
1527 0 : auto heated_length = _subchannel_mesh.getHeatedLength();
1528 0 : auto unheated_length_entry = _subchannel_mesh.getHeatedLengthEntry();
1529 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1530 : {
1531 0 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1532 0 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1533 0 : auto mdot_in = (*_mdot_soln)(node_in);
1534 0 : auto h_in = (*_h_soln)(node_in); // J/kg
1535 0 : auto volume = dz * (*_S_flow_soln)(node_in);
1536 0 : auto mdot_out = (*_mdot_soln)(node_out);
1537 0 : auto h_out = 0.0;
1538 : Real sumWijh = 0.0;
1539 : Real sumWijPrimeDhij = 0.0;
1540 : Real added_enthalpy;
1541 0 : if (_z_grid[iz] > unheated_length_entry &&
1542 0 : _z_grid[iz] <= unheated_length_entry + heated_length)
1543 0 : added_enthalpy = computeAddedHeat(i_ch, iz);
1544 : else
1545 : added_enthalpy = 0.0;
1546 :
1547 : // Calculate sum of crossflow into channel i from channels j around i
1548 : unsigned int counter = 0;
1549 0 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
1550 : {
1551 0 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
1552 : unsigned int ii_ch = chans.first;
1553 : // i is always the smallest and first index in the mapping
1554 : unsigned int jj_ch = chans.second;
1555 0 : auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
1556 0 : auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
1557 : // Define donor enthalpy
1558 : auto h_star = 0.0;
1559 0 : if (_Wij(i_gap, iz) > 0.0)
1560 0 : h_star = (*_h_soln)(node_in_i);
1561 0 : else if (_Wij(i_gap, iz) < 0.0)
1562 0 : h_star = (*_h_soln)(node_in_j);
1563 : // take care of the sign by applying the map, use donor cell
1564 0 : sumWijh += _subchannel_mesh.getCrossflowSign(i_ch, counter) * _Wij(i_gap, iz) * h_star;
1565 0 : sumWijPrimeDhij += _WijPrime(i_gap, iz) * (2 * (*_h_soln)(node_in) -
1566 0 : (*_h_soln)(node_in_j) - (*_h_soln)(node_in_i));
1567 0 : counter++;
1568 : }
1569 :
1570 0 : h_out = (mdot_in * h_in - sumWijh - sumWijPrimeDhij + added_enthalpy +
1571 0 : _TR * _rho_soln->old(node_out) * _h_soln->old(node_out) * volume / _dt) /
1572 0 : (mdot_out + _TR * (*_rho_soln)(node_out)*volume / _dt);
1573 :
1574 0 : if (h_out < 0)
1575 : {
1576 0 : mooseError(name(),
1577 : " : Calculation of negative Enthalpy h_out = : ",
1578 : h_out,
1579 : " Axial Level= : ",
1580 : iz);
1581 : }
1582 0 : _h_soln->set(node_out, h_out); // J/kg
1583 : }
1584 : }
1585 : }
1586 : else
1587 : {
1588 0 : _console << "Setting matrices" << std::endl;
1589 0 : LibmeshPetscCall(MatZeroEntries(_hc_time_derivative_mat));
1590 0 : LibmeshPetscCall(MatZeroEntries(_hc_advective_derivative_mat));
1591 0 : LibmeshPetscCall(MatZeroEntries(_hc_cross_derivative_mat));
1592 0 : LibmeshPetscCall(VecZeroEntries(_hc_time_derivative_rhs));
1593 0 : LibmeshPetscCall(VecZeroEntries(_hc_advective_derivative_rhs));
1594 0 : LibmeshPetscCall(VecZeroEntries(_hc_cross_derivative_rhs));
1595 0 : LibmeshPetscCall(VecZeroEntries(_hc_added_heat_rhs));
1596 0 : LibmeshPetscCall(MatZeroEntries(_hc_sys_h_mat));
1597 0 : LibmeshPetscCall(VecZeroEntries(_hc_sys_h_rhs));
1598 :
1599 0 : _console << "Starting enthalpy assembly" << std::endl;
1600 0 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1601 : {
1602 0 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
1603 0 : auto heated_length = _subchannel_mesh.getHeatedLength();
1604 0 : auto unheated_length_entry = _subchannel_mesh.getHeatedLengthEntry();
1605 0 : auto iz_ind = iz - first_node;
1606 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1607 : {
1608 0 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1609 0 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1610 0 : auto volume = dz * (*_S_flow_soln)(node_in);
1611 :
1612 : // interpolation weight coefficient
1613 : PetscScalar Pe = 0.5;
1614 0 : auto alpha = computeInterpolationCoefficients(Pe);
1615 :
1616 : /// Time derivative term
1617 0 : if (iz == first_node)
1618 : {
1619 : PetscScalar value_vec_tt =
1620 0 : -1.0 * _TR * alpha * (*_rho_soln)(node_in) * (*_h_soln)(node_in)*volume / _dt;
1621 0 : PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
1622 0 : LibmeshPetscCall(
1623 : VecSetValues(_hc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
1624 : }
1625 : else
1626 : {
1627 0 : PetscInt row_tt = i_ch + _n_channels * iz_ind;
1628 0 : PetscInt col_tt = i_ch + _n_channels * (iz_ind - 1);
1629 0 : PetscScalar value_tt = _TR * alpha * (*_rho_soln)(node_in)*volume / _dt;
1630 0 : LibmeshPetscCall(MatSetValues(
1631 : _hc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
1632 : }
1633 :
1634 : // Adding diagonal elements
1635 0 : PetscInt row_tt = i_ch + _n_channels * iz_ind;
1636 0 : PetscInt col_tt = i_ch + _n_channels * iz_ind;
1637 0 : PetscScalar value_tt = _TR * (1.0 - alpha) * (*_rho_soln)(node_out)*volume / _dt;
1638 0 : LibmeshPetscCall(MatSetValues(
1639 : _hc_time_derivative_mat, 1, &row_tt, 1, &col_tt, &value_tt, INSERT_VALUES));
1640 :
1641 : // Adding RHS elements
1642 : PetscScalar rho_old_interp =
1643 0 : computeInterpolatedValue(_rho_soln->old(node_out), _rho_soln->old(node_in), Pe);
1644 : PetscScalar h_old_interp =
1645 0 : computeInterpolatedValue(_h_soln->old(node_out), _h_soln->old(node_in), Pe);
1646 :
1647 0 : PetscScalar value_vec_tt = _TR * rho_old_interp * h_old_interp * volume / _dt;
1648 0 : PetscInt row_vec_tt = i_ch + _n_channels * iz_ind;
1649 0 : LibmeshPetscCall(
1650 : VecSetValues(_hc_time_derivative_rhs, 1, &row_vec_tt, &value_vec_tt, ADD_VALUES));
1651 :
1652 : /// Advective derivative term
1653 0 : if (iz == first_node)
1654 : {
1655 0 : PetscScalar value_vec_at = (*_mdot_soln)(node_in) * (*_h_soln)(node_in);
1656 0 : PetscInt row_vec_at = i_ch + _n_channels * iz_ind;
1657 0 : LibmeshPetscCall(VecSetValues(
1658 : _hc_advective_derivative_rhs, 1, &row_vec_at, &value_vec_at, ADD_VALUES));
1659 : }
1660 : else
1661 : {
1662 0 : PetscInt row_at = i_ch + _n_channels * iz_ind;
1663 0 : PetscInt col_at = i_ch + _n_channels * (iz_ind - 1);
1664 0 : PetscScalar value_at = -1.0 * (*_mdot_soln)(node_in);
1665 0 : LibmeshPetscCall(MatSetValues(
1666 : _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
1667 : }
1668 :
1669 : // Adding diagonal elements
1670 0 : PetscInt row_at = i_ch + _n_channels * iz_ind;
1671 0 : PetscInt col_at = i_ch + _n_channels * iz_ind;
1672 0 : PetscScalar value_at = (*_mdot_soln)(node_out);
1673 0 : LibmeshPetscCall(MatSetValues(
1674 : _hc_advective_derivative_mat, 1, &row_at, 1, &col_at, &value_at, INSERT_VALUES));
1675 :
1676 : /// Cross derivative term
1677 : unsigned int counter = 0;
1678 : unsigned int cross_index = iz; // iz-1;
1679 0 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
1680 : {
1681 0 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
1682 : unsigned int ii_ch = chans.first;
1683 : unsigned int jj_ch = chans.second;
1684 0 : auto * node_in_i = _subchannel_mesh.getChannelNode(ii_ch, iz - 1);
1685 0 : auto * node_in_j = _subchannel_mesh.getChannelNode(jj_ch, iz - 1);
1686 :
1687 : PetscScalar h_star;
1688 : // figure out donor axial velocity
1689 0 : if (_Wij(i_gap, cross_index) > 0.0)
1690 : {
1691 0 : if (iz == first_node)
1692 : {
1693 0 : h_star = (*_h_soln)(node_in_i);
1694 0 : PetscScalar value_vec_ct = -1.0 * alpha *
1695 0 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1696 0 : _Wij(i_gap, cross_index) * h_star;
1697 0 : PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
1698 0 : LibmeshPetscCall(VecSetValues(
1699 : _hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
1700 : }
1701 : else
1702 : {
1703 0 : PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1704 0 : _Wij(i_gap, cross_index);
1705 0 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1706 0 : PetscInt col_ct = ii_ch + _n_channels * (iz_ind - 1);
1707 0 : LibmeshPetscCall(MatSetValues(
1708 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1709 : }
1710 0 : PetscScalar value_ct = (1.0 - alpha) *
1711 0 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1712 0 : _Wij(i_gap, cross_index);
1713 0 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1714 0 : PetscInt col_ct = ii_ch + _n_channels * iz_ind;
1715 0 : LibmeshPetscCall(MatSetValues(
1716 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1717 : }
1718 0 : else if (_Wij(i_gap, cross_index) < 0.0) // _Wij=0 operations not necessary
1719 : {
1720 0 : if (iz == first_node)
1721 : {
1722 0 : h_star = (*_h_soln)(node_in_j);
1723 0 : PetscScalar value_vec_ct = -1.0 * alpha *
1724 0 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1725 0 : _Wij(i_gap, cross_index) * h_star;
1726 0 : PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
1727 0 : LibmeshPetscCall(VecSetValues(
1728 : _hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
1729 : }
1730 : else
1731 : {
1732 0 : PetscScalar value_ct = alpha * _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1733 0 : _Wij(i_gap, cross_index);
1734 0 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1735 0 : PetscInt col_ct = jj_ch + _n_channels * (iz_ind - 1);
1736 0 : LibmeshPetscCall(MatSetValues(
1737 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1738 : }
1739 0 : PetscScalar value_ct = (1.0 - alpha) *
1740 0 : _subchannel_mesh.getCrossflowSign(i_ch, counter) *
1741 0 : _Wij(i_gap, cross_index);
1742 0 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1743 0 : PetscInt col_ct = jj_ch + _n_channels * iz_ind;
1744 0 : LibmeshPetscCall(MatSetValues(
1745 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_ct, ADD_VALUES));
1746 : }
1747 :
1748 : // Turbulent cross flows
1749 0 : if (iz == first_node)
1750 : {
1751 : PetscScalar value_vec_ct =
1752 0 : -2.0 * alpha * (*_mdot_soln)(node_in)*_WijPrime(i_gap, cross_index);
1753 0 : value_vec_ct = alpha * (*_mdot_soln)(node_in_j)*_WijPrime(i_gap, cross_index);
1754 0 : value_vec_ct += alpha * (*_mdot_soln)(node_in_i)*_WijPrime(i_gap, cross_index);
1755 0 : PetscInt row_vec_ct = i_ch + _n_channels * iz_ind;
1756 0 : LibmeshPetscCall(
1757 : VecSetValues(_hc_cross_derivative_rhs, 1, &row_vec_ct, &value_vec_ct, ADD_VALUES));
1758 : }
1759 : else
1760 : {
1761 0 : PetscScalar value_center_ct = 2.0 * alpha * _WijPrime(i_gap, cross_index);
1762 0 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1763 0 : PetscInt col_ct = i_ch + _n_channels * (iz_ind - 1);
1764 0 : LibmeshPetscCall(MatSetValues(
1765 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
1766 :
1767 0 : PetscScalar value_left_ct = -1.0 * alpha * _WijPrime(i_gap, cross_index);
1768 0 : row_ct = i_ch + _n_channels * iz_ind;
1769 0 : col_ct = jj_ch + _n_channels * (iz_ind - 1);
1770 0 : LibmeshPetscCall(MatSetValues(
1771 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
1772 :
1773 0 : PetscScalar value_right_ct = -1.0 * alpha * _WijPrime(i_gap, cross_index);
1774 0 : row_ct = i_ch + _n_channels * iz_ind;
1775 0 : col_ct = ii_ch + _n_channels * (iz_ind - 1);
1776 0 : LibmeshPetscCall(MatSetValues(
1777 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
1778 : }
1779 :
1780 0 : PetscScalar value_center_ct = 2.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
1781 0 : PetscInt row_ct = i_ch + _n_channels * iz_ind;
1782 0 : PetscInt col_ct = i_ch + _n_channels * iz_ind;
1783 0 : LibmeshPetscCall(MatSetValues(
1784 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_center_ct, ADD_VALUES));
1785 :
1786 0 : PetscScalar value_left_ct = -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
1787 0 : row_ct = i_ch + _n_channels * iz_ind;
1788 0 : col_ct = jj_ch + _n_channels * iz_ind;
1789 0 : LibmeshPetscCall(MatSetValues(
1790 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_left_ct, ADD_VALUES));
1791 :
1792 0 : PetscScalar value_right_ct = -1.0 * (1.0 - alpha) * _WijPrime(i_gap, cross_index);
1793 0 : row_ct = i_ch + _n_channels * iz_ind;
1794 0 : col_ct = ii_ch + _n_channels * iz_ind;
1795 0 : LibmeshPetscCall(MatSetValues(
1796 : _hc_cross_derivative_mat, 1, &row_ct, 1, &col_ct, &value_right_ct, ADD_VALUES));
1797 :
1798 0 : counter++;
1799 : }
1800 :
1801 : /// Added heat enthalpy
1802 : PetscScalar added_enthalpy;
1803 0 : if (_z_grid[iz] > unheated_length_entry &&
1804 0 : _z_grid[iz] <= unheated_length_entry + heated_length)
1805 0 : added_enthalpy = computeAddedHeat(i_ch, iz);
1806 : else
1807 0 : added_enthalpy = 0.0;
1808 :
1809 0 : PetscInt row_vec_ht = i_ch + _n_channels * iz_ind;
1810 0 : LibmeshPetscCall(
1811 : VecSetValues(_hc_added_heat_rhs, 1, &row_vec_ht, &added_enthalpy, ADD_VALUES));
1812 : }
1813 : }
1814 0 : _console << "Done with enthalpy assembly" << std::endl;
1815 :
1816 : /// Assembling system
1817 0 : LibmeshPetscCall(MatAssemblyBegin(_hc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1818 0 : LibmeshPetscCall(MatAssemblyEnd(_hc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
1819 0 : LibmeshPetscCall(MatAssemblyBegin(_hc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1820 0 : LibmeshPetscCall(MatAssemblyEnd(_hc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
1821 0 : LibmeshPetscCall(MatAssemblyBegin(_hc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
1822 0 : LibmeshPetscCall(MatAssemblyEnd(_hc_cross_derivative_mat, MAT_FINAL_ASSEMBLY));
1823 0 : LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1824 0 : LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1825 : // Matrix
1826 : #if !PETSC_VERSION_LESS_THAN(3, 15, 0)
1827 0 : LibmeshPetscCall(MatAXPY(_hc_sys_h_mat, 1.0, _hc_time_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1828 0 : LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1829 0 : LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1830 0 : LibmeshPetscCall(
1831 : MatAXPY(_hc_sys_h_mat, 1.0, _hc_advective_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1832 0 : LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1833 0 : LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1834 0 : LibmeshPetscCall(
1835 : MatAXPY(_hc_sys_h_mat, 1.0, _hc_cross_derivative_mat, UNKNOWN_NONZERO_PATTERN));
1836 : #else
1837 : LibmeshPetscCall(
1838 : MatAXPY(_hc_sys_h_mat, 1.0, _hc_time_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1839 : LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1840 : LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1841 : LibmeshPetscCall(
1842 : MatAXPY(_hc_sys_h_mat, 1.0, _hc_advective_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1843 : LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1844 : LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1845 : LibmeshPetscCall(
1846 : MatAXPY(_hc_sys_h_mat, 1.0, _hc_cross_derivative_mat, DIFFERENT_NONZERO_PATTERN));
1847 : #endif
1848 0 : LibmeshPetscCall(MatAssemblyBegin(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1849 0 : LibmeshPetscCall(MatAssemblyEnd(_hc_sys_h_mat, MAT_FINAL_ASSEMBLY));
1850 0 : _console << "Block: " << iblock << " - Enthalpy conservation matrix assembled" << std::endl;
1851 : // RHS
1852 0 : LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_time_derivative_rhs));
1853 0 : LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_advective_derivative_rhs));
1854 0 : LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_cross_derivative_rhs));
1855 0 : LibmeshPetscCall(VecAXPY(_hc_sys_h_rhs, 1.0, _hc_added_heat_rhs));
1856 :
1857 0 : if (_segregated_bool)
1858 : {
1859 : // Assembly the matrix system
1860 : KSP ksploc;
1861 : PC pc;
1862 : Vec sol;
1863 0 : LibmeshPetscCall(VecDuplicate(_hc_sys_h_rhs, &sol));
1864 0 : LibmeshPetscCall(KSPCreate(PETSC_COMM_WORLD, &ksploc));
1865 0 : LibmeshPetscCall(KSPSetOperators(ksploc, _hc_sys_h_mat, _hc_sys_h_mat));
1866 0 : LibmeshPetscCall(KSPGetPC(ksploc, &pc));
1867 0 : LibmeshPetscCall(PCSetType(pc, PCJACOBI));
1868 0 : LibmeshPetscCall(KSPSetTolerances(ksploc, _rtol, _atol, _dtol, _maxit));
1869 0 : LibmeshPetscCall(KSPSetOptionsPrefix(ksploc, "h_cons_sys_"));
1870 0 : LibmeshPetscCall(KSPSetFromOptions(ksploc));
1871 0 : LibmeshPetscCall(KSPSolve(ksploc, _hc_sys_h_rhs, sol));
1872 : PetscScalar * xx;
1873 0 : LibmeshPetscCall(VecGetArray(sol, &xx));
1874 :
1875 0 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1876 : {
1877 0 : auto iz_ind = iz - first_node;
1878 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1879 : {
1880 0 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
1881 0 : auto h_out = xx[iz_ind * _n_channels + i_ch];
1882 0 : if (h_out < 0)
1883 : {
1884 0 : mooseError(name(),
1885 : " : Calculation of negative Enthalpy h_out = : ",
1886 : h_out,
1887 : " Axial Level= : ",
1888 : iz);
1889 : }
1890 0 : _h_soln->set(node_out, h_out);
1891 : }
1892 : }
1893 0 : LibmeshPetscCall(KSPDestroy(&ksploc));
1894 0 : LibmeshPetscCall(VecDestroy(&sol));
1895 : }
1896 : }
1897 0 : }
1898 :
1899 : void
1900 0 : InterWrapper1PhaseProblem::computeT(int iblock)
1901 : {
1902 0 : unsigned int last_node = (iblock + 1) * _block_size;
1903 0 : unsigned int first_node = iblock * _block_size + 1;
1904 :
1905 0 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1906 : {
1907 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1908 : {
1909 0 : auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
1910 0 : _T_soln->set(node, _fp->T_from_p_h((*_P_soln)(node) + _P_out, (*_h_soln)(node)));
1911 : }
1912 : }
1913 0 : }
1914 :
1915 : void
1916 744 : InterWrapper1PhaseProblem::computeRho(int iblock)
1917 : {
1918 744 : unsigned int last_node = (iblock + 1) * _block_size;
1919 744 : unsigned int first_node = iblock * _block_size + 1;
1920 :
1921 744 : if (iblock == 0)
1922 : {
1923 5064 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1924 : {
1925 4932 : auto * node = _subchannel_mesh.getChannelNode(i_ch, 0);
1926 4932 : _rho_soln->set(node, _fp->rho_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node)));
1927 : }
1928 : }
1929 :
1930 2064 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1931 : {
1932 50640 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1933 : {
1934 49320 : auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
1935 49320 : _rho_soln->set(node, _fp->rho_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node)));
1936 : }
1937 : }
1938 744 : }
1939 :
1940 : void
1941 744 : InterWrapper1PhaseProblem::computeMu(int iblock)
1942 : {
1943 744 : unsigned int last_node = (iblock + 1) * _block_size;
1944 744 : unsigned int first_node = iblock * _block_size + 1;
1945 :
1946 744 : if (iblock == 0)
1947 : {
1948 5064 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1949 : {
1950 4932 : auto * node = _subchannel_mesh.getChannelNode(i_ch, 0);
1951 4932 : _mu_soln->set(node, _fp->mu_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node)));
1952 : }
1953 : }
1954 :
1955 2064 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
1956 : {
1957 50640 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
1958 : {
1959 49320 : auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
1960 49320 : _mu_soln->set(node, _fp->mu_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node)));
1961 : }
1962 : }
1963 744 : }
1964 :
1965 : void
1966 8622 : InterWrapper1PhaseProblem::computeWij(int iblock)
1967 : {
1968 : // Cross flow residual
1969 8622 : if (!_implicit_bool)
1970 : {
1971 7986 : unsigned int last_node = (iblock + 1) * _block_size;
1972 7986 : unsigned int first_node = iblock * _block_size;
1973 :
1974 7986 : const Real & pitch = _subchannel_mesh.getPitch();
1975 55608 : for (unsigned int iz = first_node + 1; iz < last_node + 1; iz++)
1976 : {
1977 47622 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
1978 2904942 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
1979 : {
1980 2857320 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
1981 : unsigned int i_ch = chans.first;
1982 : unsigned int j_ch = chans.second;
1983 2857320 : auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
1984 2857320 : auto * node_out_i = _subchannel_mesh.getChannelNode(i_ch, iz);
1985 2857320 : auto * node_in_j = _subchannel_mesh.getChannelNode(j_ch, iz - 1);
1986 2857320 : auto * node_out_j = _subchannel_mesh.getChannelNode(j_ch, iz);
1987 2857320 : auto rho_i = (*_rho_soln)(node_in_i);
1988 2857320 : auto rho_j = (*_rho_soln)(node_in_j);
1989 2857320 : auto Si = (*_S_flow_soln)(node_in_i);
1990 2857320 : auto Sj = (*_S_flow_soln)(node_in_j);
1991 2857320 : auto Sij = dz * _subchannel_mesh.getGapWidth(i_gap);
1992 2857320 : auto Lij = pitch;
1993 : // total local form loss in the ij direction
1994 2857320 : auto friction_term = _kij * _Wij(i_gap, iz) * std::abs(_Wij(i_gap, iz));
1995 2857320 : auto DPij = (*_P_soln)(node_in_i) - (*_P_soln)(node_in_j);
1996 : // Figure out donor cell density
1997 : auto rho_star = 0.0;
1998 2857320 : if (_Wij(i_gap, iz) > 0.0)
1999 : rho_star = rho_i;
2000 1667532 : else if (_Wij(i_gap, iz) < 0.0)
2001 : rho_star = rho_j;
2002 : else
2003 441444 : rho_star = (rho_i + rho_j) / 2.0;
2004 :
2005 : auto mass_term_out =
2006 2857320 : (*_mdot_soln)(node_out_i) / Si / rho_i + (*_mdot_soln)(node_out_j) / Sj / rho_j;
2007 : auto mass_term_in =
2008 2857320 : (*_mdot_soln)(node_in_i) / Si / rho_i + (*_mdot_soln)(node_in_j) / Sj / rho_j;
2009 2857320 : auto term_out = Sij * rho_star * (Lij / dz) * mass_term_out * _Wij(i_gap, iz);
2010 2857320 : auto term_in = Sij * rho_star * (Lij / dz) * mass_term_in * _Wij(i_gap, iz - 1);
2011 2857320 : auto inertia_term = term_out - term_in;
2012 2857320 : auto pressure_term = 2 * std::pow(Sij, 2.0) * DPij * rho_star;
2013 : auto time_term =
2014 2857320 : _TR * 2.0 * (_Wij(i_gap, iz) - _Wij_old(i_gap, iz)) * Lij * Sij * rho_star / _dt;
2015 :
2016 2857320 : _Wij_residual_matrix(i_gap, iz - 1 - iblock * _block_size) =
2017 2857320 : time_term + friction_term + inertia_term - pressure_term;
2018 : }
2019 : }
2020 : }
2021 : else
2022 : {
2023 :
2024 : // Initializing to zero the elements of the lateral momentum assembly
2025 636 : LibmeshPetscCall(MatZeroEntries(_cmc_time_derivative_mat));
2026 636 : LibmeshPetscCall(MatZeroEntries(_cmc_advective_derivative_mat));
2027 636 : LibmeshPetscCall(MatZeroEntries(_cmc_friction_force_mat));
2028 636 : LibmeshPetscCall(MatZeroEntries(_cmc_pressure_force_mat));
2029 636 : LibmeshPetscCall(VecZeroEntries(_cmc_time_derivative_rhs));
2030 636 : LibmeshPetscCall(VecZeroEntries(_cmc_advective_derivative_rhs));
2031 636 : LibmeshPetscCall(VecZeroEntries(_cmc_friction_force_rhs));
2032 636 : LibmeshPetscCall(VecZeroEntries(_cmc_pressure_force_rhs));
2033 636 : LibmeshPetscCall(MatZeroEntries(_cmc_sys_Wij_mat));
2034 636 : LibmeshPetscCall(VecZeroEntries(_cmc_sys_Wij_rhs));
2035 :
2036 636 : unsigned int last_node = (iblock + 1) * _block_size;
2037 636 : unsigned int first_node = iblock * _block_size;
2038 :
2039 636 : const Real & pitch = _subchannel_mesh.getPitch();
2040 4056 : for (unsigned int iz = first_node + 1; iz < last_node + 1; iz++)
2041 : {
2042 3420 : auto dz = _z_grid[iz] - _z_grid[iz - 1];
2043 3420 : auto iz_ind = iz - first_node - 1;
2044 208620 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
2045 : {
2046 205200 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
2047 : unsigned int i_ch = chans.first;
2048 : unsigned int j_ch = chans.second;
2049 205200 : auto * node_in_i = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
2050 205200 : auto * node_out_i = _subchannel_mesh.getChannelNode(i_ch, iz);
2051 205200 : auto * node_in_j = _subchannel_mesh.getChannelNode(j_ch, iz - 1);
2052 205200 : auto * node_out_j = _subchannel_mesh.getChannelNode(j_ch, iz);
2053 :
2054 : // inlet, outlet, and interpolated densities
2055 205200 : auto rho_i_in = (*_rho_soln)(node_in_i);
2056 205200 : auto rho_i_out = (*_rho_soln)(node_out_i);
2057 205200 : auto rho_i_interp = computeInterpolatedValue(rho_i_out, rho_i_in);
2058 205200 : auto rho_j_in = (*_rho_soln)(node_in_j);
2059 205200 : auto rho_j_out = (*_rho_soln)(node_out_j);
2060 205200 : auto rho_j_interp = computeInterpolatedValue(rho_j_out, rho_j_in);
2061 :
2062 : // inlet, outlet, and interpolated areas
2063 205200 : auto S_i_in = (*_S_flow_soln)(node_in_i);
2064 205200 : auto S_i_out = (*_S_flow_soln)(node_out_i);
2065 205200 : auto S_j_in = (*_S_flow_soln)(node_in_j);
2066 205200 : auto S_j_out = (*_S_flow_soln)(node_out_j);
2067 :
2068 : // Cross-sectional gap area
2069 205200 : auto Sij = dz * _subchannel_mesh.getGapWidth(i_gap);
2070 205200 : auto Lij = pitch;
2071 :
2072 : // Figure out donor cell density
2073 : auto rho_star = 0.0;
2074 205200 : if (_Wij(i_gap, iz) > 0.0)
2075 : rho_star = rho_i_interp;
2076 173688 : else if (_Wij(i_gap, iz) < 0.0)
2077 : rho_star = rho_j_interp;
2078 : else
2079 73836 : rho_star = (rho_i_interp + rho_j_interp) / 2.0;
2080 :
2081 : // Assembling time derivative
2082 205200 : PetscScalar time_factor = _TR * Lij * Sij * rho_star / _dt;
2083 205200 : PetscInt row_td = i_gap + _n_gaps * iz_ind;
2084 205200 : PetscInt col_td = i_gap + _n_gaps * iz_ind;
2085 205200 : PetscScalar value_td = time_factor;
2086 205200 : LibmeshPetscCall(MatSetValues(
2087 : _cmc_time_derivative_mat, 1, &row_td, 1, &col_td, &value_td, INSERT_VALUES));
2088 205200 : PetscScalar value_td_rhs = time_factor * _Wij_old(i_gap, iz);
2089 205200 : LibmeshPetscCall(
2090 : VecSetValues(_cmc_time_derivative_rhs, 1, &row_td, &value_td_rhs, INSERT_VALUES));
2091 :
2092 : // Assembling inertial term
2093 : auto alpha = 0.0; // hard code downwind;
2094 205200 : auto mass_term_out = (*_mdot_soln)(node_out_i) / S_i_out / rho_i_out +
2095 205200 : (*_mdot_soln)(node_out_j) / S_j_out / rho_j_out;
2096 205200 : auto mass_term_in = (*_mdot_soln)(node_in_i) / S_i_in / rho_i_in +
2097 205200 : (*_mdot_soln)(node_in_j) / S_j_in / rho_j_in;
2098 205200 : auto term_out = Sij * rho_star * (Lij / dz) * mass_term_out / 2.0;
2099 205200 : auto term_in = Sij * rho_star * (Lij / dz) * mass_term_in / 2.0;
2100 205200 : if (iz == first_node + 1)
2101 : {
2102 38160 : PetscInt row_ad = i_gap + _n_gaps * iz_ind;
2103 38160 : PetscScalar value_ad = term_in * alpha * _Wij(i_gap, iz - 1);
2104 38160 : LibmeshPetscCall(
2105 : VecSetValues(_cmc_advective_derivative_rhs, 1, &row_ad, &value_ad, ADD_VALUES));
2106 :
2107 38160 : PetscInt col_ad = i_gap + _n_gaps * iz_ind;
2108 38160 : value_ad = -1.0 * term_in * (1.0 - alpha) + term_out * alpha;
2109 38160 : LibmeshPetscCall(MatSetValues(
2110 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
2111 :
2112 38160 : col_ad = i_gap + _n_gaps * (iz_ind + 1);
2113 38160 : value_ad = term_out * (1.0 - alpha);
2114 38160 : LibmeshPetscCall(MatSetValues(
2115 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
2116 : }
2117 167040 : else if (iz == last_node)
2118 : {
2119 38160 : PetscInt row_ad = i_gap + _n_gaps * iz_ind;
2120 38160 : PetscInt col_ad = i_gap + _n_gaps * (iz_ind - 1);
2121 38160 : PetscScalar value_ad = -1.0 * term_in * alpha;
2122 38160 : LibmeshPetscCall(MatSetValues(
2123 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
2124 :
2125 38160 : col_ad = i_gap + _n_gaps * iz_ind;
2126 38160 : value_ad = -1.0 * term_in * (1.0 - alpha) + term_out * alpha;
2127 38160 : LibmeshPetscCall(MatSetValues(
2128 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
2129 :
2130 38160 : value_ad = -1.0 * term_out * (1.0 - alpha) * _Wij(i_gap, iz);
2131 38160 : LibmeshPetscCall(
2132 : VecSetValues(_cmc_advective_derivative_rhs, 1, &row_ad, &value_ad, ADD_VALUES));
2133 : }
2134 : else
2135 : {
2136 128880 : PetscInt row_ad = i_gap + _n_gaps * iz_ind;
2137 128880 : PetscInt col_ad = i_gap + _n_gaps * (iz_ind - 1);
2138 128880 : PetscScalar value_ad = -1.0 * term_in * alpha;
2139 128880 : LibmeshPetscCall(MatSetValues(
2140 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
2141 :
2142 128880 : col_ad = i_gap + _n_gaps * iz_ind;
2143 128880 : value_ad = -1.0 * term_in * (1.0 - alpha) + term_out * alpha;
2144 128880 : LibmeshPetscCall(MatSetValues(
2145 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
2146 :
2147 128880 : col_ad = i_gap + _n_gaps * (iz_ind + 1);
2148 128880 : value_ad = term_out * (1.0 - alpha);
2149 128880 : LibmeshPetscCall(MatSetValues(
2150 : _cmc_advective_derivative_mat, 1, &row_ad, 1, &col_ad, &value_ad, INSERT_VALUES));
2151 : }
2152 : // Assembling friction force
2153 205200 : PetscInt row_ff = i_gap + _n_gaps * iz_ind;
2154 205200 : PetscInt col_ff = i_gap + _n_gaps * iz_ind;
2155 205200 : PetscScalar value_ff = _kij * std::abs(_Wij(i_gap, iz)) / 2.0;
2156 205200 : LibmeshPetscCall(MatSetValues(
2157 : _cmc_friction_force_mat, 1, &row_ff, 1, &col_ff, &value_ff, INSERT_VALUES));
2158 :
2159 : // Assembling pressure force
2160 205200 : alpha = computeInterpolationCoefficients(0.5);
2161 :
2162 205200 : if (!_staggered_pressure_bool)
2163 : {
2164 190800 : PetscScalar pressure_factor = std::pow(Sij, 2.0) * rho_star;
2165 190800 : PetscInt row_pf = i_gap + _n_gaps * iz_ind;
2166 190800 : PetscInt col_pf = i_ch + _n_channels * iz_ind;
2167 190800 : PetscScalar value_pf = -1.0 * alpha * pressure_factor;
2168 190800 : LibmeshPetscCall(
2169 : MatSetValues(_cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
2170 190800 : col_pf = j_ch + _n_channels * iz_ind;
2171 190800 : value_pf = alpha * pressure_factor;
2172 190800 : LibmeshPetscCall(
2173 : MatSetValues(_cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
2174 :
2175 190800 : if (iz == last_node)
2176 : {
2177 36720 : PetscInt row_pf = i_gap + _n_gaps * iz_ind;
2178 36720 : PetscScalar value_pf = (1.0 - alpha) * pressure_factor * (*_P_soln)(node_out_i);
2179 36720 : LibmeshPetscCall(
2180 : VecSetValues(_cmc_pressure_force_rhs, 1, &row_pf, &value_pf, ADD_VALUES));
2181 36720 : value_pf = -1.0 * (1.0 - alpha) * pressure_factor * (*_P_soln)(node_out_j);
2182 36720 : LibmeshPetscCall(
2183 : VecSetValues(_cmc_pressure_force_rhs, 1, &row_pf, &value_pf, ADD_VALUES));
2184 : }
2185 : else
2186 : {
2187 154080 : row_pf = i_gap + _n_gaps * iz_ind;
2188 154080 : col_pf = i_ch + _n_channels * (iz_ind + 1);
2189 154080 : value_pf = -1.0 * (1.0 - alpha) * pressure_factor;
2190 154080 : LibmeshPetscCall(MatSetValues(
2191 : _cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
2192 154080 : col_pf = j_ch + _n_channels * (iz_ind + 1);
2193 154080 : value_pf = (1.0 - alpha) * pressure_factor;
2194 154080 : LibmeshPetscCall(MatSetValues(
2195 : _cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
2196 : }
2197 : }
2198 : else
2199 : {
2200 14400 : PetscScalar pressure_factor = std::pow(Sij, 2.0) * rho_star;
2201 14400 : PetscInt row_pf = i_gap + _n_gaps * iz_ind;
2202 14400 : PetscInt col_pf = i_ch + _n_channels * iz_ind;
2203 14400 : PetscScalar value_pf = -1.0 * pressure_factor;
2204 14400 : LibmeshPetscCall(
2205 : MatSetValues(_cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
2206 14400 : col_pf = j_ch + _n_channels * iz_ind;
2207 14400 : value_pf = pressure_factor;
2208 14400 : LibmeshPetscCall(
2209 : MatSetValues(_cmc_pressure_force_mat, 1, &row_pf, 1, &col_pf, &value_pf, ADD_VALUES));
2210 : }
2211 : }
2212 : }
2213 : /// Assembling system
2214 636 : LibmeshPetscCall(MatZeroEntries(_cmc_sys_Wij_mat));
2215 636 : LibmeshPetscCall(VecZeroEntries(_cmc_sys_Wij_rhs));
2216 636 : LibmeshPetscCall(MatAssemblyBegin(_cmc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
2217 636 : LibmeshPetscCall(MatAssemblyEnd(_cmc_time_derivative_mat, MAT_FINAL_ASSEMBLY));
2218 636 : LibmeshPetscCall(MatAssemblyBegin(_cmc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
2219 636 : LibmeshPetscCall(MatAssemblyEnd(_cmc_advective_derivative_mat, MAT_FINAL_ASSEMBLY));
2220 636 : LibmeshPetscCall(MatAssemblyBegin(_cmc_friction_force_mat, MAT_FINAL_ASSEMBLY));
2221 636 : LibmeshPetscCall(MatAssemblyEnd(_cmc_friction_force_mat, MAT_FINAL_ASSEMBLY));
2222 636 : LibmeshPetscCall(MatAssemblyBegin(_cmc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
2223 636 : LibmeshPetscCall(MatAssemblyEnd(_cmc_pressure_force_mat, MAT_FINAL_ASSEMBLY));
2224 636 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2225 636 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2226 : // Matrix
2227 : #if !PETSC_VERSION_LESS_THAN(3, 15, 0)
2228 636 : LibmeshPetscCall(
2229 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_time_derivative_mat, UNKNOWN_NONZERO_PATTERN));
2230 636 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2231 636 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2232 636 : LibmeshPetscCall(
2233 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_advective_derivative_mat, UNKNOWN_NONZERO_PATTERN));
2234 636 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2235 636 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2236 636 : LibmeshPetscCall(
2237 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_friction_force_mat, UNKNOWN_NONZERO_PATTERN));
2238 : #else
2239 : LibmeshPetscCall(
2240 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_time_derivative_mat, DIFFERENT_NONZERO_PATTERN));
2241 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2242 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2243 : LibmeshPetscCall(
2244 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_advective_derivative_mat, DIFFERENT_NONZERO_PATTERN));
2245 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2246 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2247 : LibmeshPetscCall(
2248 : MatAXPY(_cmc_sys_Wij_mat, 1.0, _cmc_friction_force_mat, DIFFERENT_NONZERO_PATTERN));
2249 : #endif
2250 636 : LibmeshPetscCall(MatAssemblyBegin(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2251 636 : LibmeshPetscCall(MatAssemblyEnd(_cmc_sys_Wij_mat, MAT_FINAL_ASSEMBLY));
2252 636 : _console << "Block: " << iblock << " - Cross flow system matrix assembled" << std::endl;
2253 636 : _console << "Block: " << iblock << " - Cross flow pressure force matrix assembled" << std::endl;
2254 : // RHS
2255 636 : LibmeshPetscCall(VecAXPY(_cmc_sys_Wij_rhs, 1.0, _cmc_time_derivative_rhs));
2256 636 : LibmeshPetscCall(VecAXPY(_cmc_sys_Wij_rhs, 1.0, _cmc_advective_derivative_rhs));
2257 636 : LibmeshPetscCall(VecAXPY(_cmc_sys_Wij_rhs, 1.0, _cmc_friction_force_rhs));
2258 :
2259 636 : if (_segregated_bool)
2260 : {
2261 : // Assembly the matrix system
2262 : Vec sol_holder_P;
2263 588 : LibmeshPetscCall(createPetscVector(sol_holder_P, _block_size * _n_gaps));
2264 : Vec sol_holder_W;
2265 588 : LibmeshPetscCall(createPetscVector(sol_holder_W, _block_size * _n_gaps));
2266 : Vec loc_holder_Wij;
2267 588 : LibmeshPetscCall(createPetscVector(loc_holder_Wij, _block_size * _n_gaps));
2268 588 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
2269 : _prodp, *_P_soln, iblock * _block_size, (iblock + 1) * _block_size - 1, _n_channels));
2270 588 : LibmeshPetscCall(populateVectorFromDense<libMesh::DenseMatrix<Real>>(
2271 : loc_holder_Wij, _Wij, first_node, last_node, _n_gaps));
2272 588 : LibmeshPetscCall(MatMult(_cmc_sys_Wij_mat, _Wij_vec, sol_holder_W));
2273 588 : LibmeshPetscCall(VecAXPY(sol_holder_W, -1.0, _cmc_sys_Wij_rhs));
2274 588 : LibmeshPetscCall(MatMult(_cmc_pressure_force_mat, _prodp, sol_holder_P));
2275 588 : LibmeshPetscCall(VecAXPY(sol_holder_P, -1.0, _cmc_pressure_force_rhs));
2276 588 : LibmeshPetscCall(VecAXPY(sol_holder_W, 1.0, sol_holder_P));
2277 : PetscScalar * xx;
2278 588 : LibmeshPetscCall(VecGetArray(sol_holder_W, &xx));
2279 3528 : for (unsigned int iz = first_node + 1; iz < last_node + 1; iz++)
2280 : {
2281 2940 : auto iz_ind = iz - first_node - 1;
2282 179340 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
2283 : {
2284 176400 : _Wij_residual_matrix(i_gap, iz - 1 - iblock * _block_size) = xx[iz_ind * _n_gaps + i_gap];
2285 : }
2286 : }
2287 588 : LibmeshPetscCall(VecDestroy(&sol_holder_P));
2288 588 : LibmeshPetscCall(VecDestroy(&sol_holder_W));
2289 588 : LibmeshPetscCall(VecDestroy(&loc_holder_Wij));
2290 : }
2291 : }
2292 8622 : }
2293 :
2294 : libMesh::DenseVector<Real>
2295 8586 : InterWrapper1PhaseProblem::residualFunction(int iblock, libMesh::DenseVector<Real> solution)
2296 : {
2297 8586 : unsigned int last_node = (iblock + 1) * _block_size;
2298 8586 : unsigned int first_node = iblock * _block_size;
2299 8586 : libMesh::DenseVector<Real> Wij_residual_vector(_n_gaps * _block_size, 0.0);
2300 : // Assign the solution to the cross-flow matrix
2301 : int i = 0;
2302 59268 : for (unsigned int iz = first_node + 1; iz < last_node + 1; iz++)
2303 : {
2304 3091602 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
2305 : {
2306 3040920 : _Wij(i_gap, iz) = solution(i);
2307 3040920 : i++;
2308 : }
2309 : }
2310 :
2311 : // Calculating sum of crossflows
2312 8586 : computeSumWij(iblock);
2313 : // Solving axial flux
2314 8586 : computeMdot(iblock);
2315 : // Calculation of turbulent Crossflow
2316 8586 : computeWijPrime(iblock);
2317 : // Solving for Pressure Drop
2318 8586 : computeDP(iblock);
2319 : // Solving for pressure
2320 8586 : computeP(iblock);
2321 : // Solving cross fluxes
2322 8586 : computeWij(iblock);
2323 :
2324 : // Turn the residual matrix into a residual vector
2325 59268 : for (unsigned int iz = 0; iz < _block_size; iz++)
2326 : {
2327 3091602 : for (unsigned int i_gap = 0; i_gap < _n_gaps; i_gap++)
2328 : {
2329 3040920 : int i = _n_gaps * iz + i_gap; // column wise transfer
2330 3040920 : Wij_residual_vector(i) = _Wij_residual_matrix(i_gap, iz);
2331 : }
2332 : }
2333 8586 : return Wij_residual_vector;
2334 : }
2335 :
2336 : PetscErrorCode
2337 732 : InterWrapper1PhaseProblem::petscSnesSolver(int iblock,
2338 : const libMesh::DenseVector<Real> & solution,
2339 : libMesh::DenseVector<Real> & root)
2340 : {
2341 : SNES snes;
2342 : KSP ksp;
2343 : PC pc;
2344 : Vec x, r;
2345 : PetscMPIInt size;
2346 : PetscScalar * xx;
2347 :
2348 : PetscFunctionBegin;
2349 732 : PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
2350 732 : if (size > 1)
2351 0 : SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, "Example is only for sequential runs");
2352 732 : LibmeshPetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
2353 732 : LibmeshPetscCall(VecCreate(PETSC_COMM_WORLD, &x));
2354 732 : LibmeshPetscCall(VecSetSizes(x, PETSC_DECIDE, _block_size * _n_gaps));
2355 732 : LibmeshPetscCall(VecSetFromOptions(x));
2356 732 : LibmeshPetscCall(VecDuplicate(x, &r));
2357 :
2358 : #if PETSC_VERSION_LESS_THAN(3, 13, 0)
2359 : LibmeshPetscCall(PetscOptionsSetValue(PETSC_NULL, "-snes_mf", PETSC_NULL));
2360 : #else
2361 732 : LibmeshPetscCall(SNESSetUseMatrixFree(snes, PETSC_FALSE, PETSC_TRUE));
2362 : #endif
2363 : problemInfo info;
2364 732 : info.iblock = iblock;
2365 732 : info.schp = this;
2366 732 : LibmeshPetscCall(SNESSetFunction(snes, r, formFunctionIW, &info));
2367 :
2368 732 : LibmeshPetscCall(SNESGetKSP(snes, &ksp));
2369 732 : LibmeshPetscCall(KSPGetPC(ksp, &pc));
2370 732 : LibmeshPetscCall(PCSetType(pc, PCNONE));
2371 732 : LibmeshPetscCall(KSPSetTolerances(ksp, _rtol, _atol, _dtol, _maxit));
2372 732 : LibmeshPetscCall(SNESSetFromOptions(snes));
2373 732 : LibmeshPetscCall(VecGetArray(x, &xx));
2374 72732 : for (unsigned int i = 0; i < _block_size * _n_gaps; i++)
2375 : {
2376 72000 : xx[i] = solution(i);
2377 : }
2378 732 : LibmeshPetscCall(VecRestoreArray(x, &xx));
2379 :
2380 732 : LibmeshPetscCall(SNESSolve(snes, NULL, x));
2381 732 : LibmeshPetscCall(VecGetArray(x, &xx));
2382 72732 : for (unsigned int i = 0; i < _block_size * _n_gaps; i++)
2383 72000 : root(i) = xx[i];
2384 :
2385 732 : LibmeshPetscCall(VecRestoreArray(x, &xx));
2386 732 : LibmeshPetscCall(VecDestroy(&x));
2387 732 : LibmeshPetscCall(VecDestroy(&r));
2388 732 : LibmeshPetscCall(SNESDestroy(&snes));
2389 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
2390 : }
2391 :
2392 : PetscErrorCode
2393 36 : InterWrapper1PhaseProblem::implicitPetscSolve(int iblock)
2394 : {
2395 : Vec b_nest, x_nest; /* approx solution, RHS, exact solution */
2396 : Mat A_nest; /* linear system matrix */
2397 : KSP ksp; /* linear solver context */
2398 : PC pc; /* preconditioner context */
2399 :
2400 : PetscFunctionBegin;
2401 36 : PetscInt Q = _monolithic_thermal_bool ? 4 : 3;
2402 36 : std::vector<Mat> mat_array(Q * Q);
2403 36 : std::vector<Vec> vec_array(Q);
2404 :
2405 : /// Initializing flags
2406 : bool _axial_mass_flow_tight_coupling = true;
2407 : bool _pressure_axial_momentum_tight_coupling = true;
2408 : bool _pressure_cross_momentum_tight_coupling = true;
2409 36 : unsigned int first_node = iblock * _block_size + 1;
2410 36 : unsigned int last_node = (iblock + 1) * _block_size;
2411 :
2412 : /// Assembling matrices
2413 : // Computing sum of crossflows with previous iteration
2414 36 : computeSumWij(iblock);
2415 : // Assembling axial flux matrix
2416 36 : computeMdot(iblock);
2417 : // Computing turbulent crossflow with previous step axial mass flows
2418 36 : computeWijPrime(iblock);
2419 : // Assembling for Pressure Drop matrix
2420 36 : computeDP(iblock);
2421 : // Assembling pressure matrix
2422 36 : computeP(iblock);
2423 : // Assembling cross fluxes matrix
2424 36 : computeWij(iblock);
2425 : // If monolithic solve - Assembling enthalpy matrix
2426 36 : if (_monolithic_thermal_bool)
2427 0 : computeh(iblock);
2428 :
2429 36 : _console << "Starting nested system." << std::endl;
2430 36 : _console << "Number of simultaneous variables: " << Q << std::endl;
2431 : // Mass conservation
2432 : PetscInt field_num = 0;
2433 36 : LibmeshPetscCall(
2434 : MatDuplicate(_mc_axial_convection_mat, MAT_COPY_VALUES, &mat_array[Q * field_num + 0]));
2435 36 : LibmeshPetscCall(MatAssemblyBegin(mat_array[Q * field_num + 0], MAT_FINAL_ASSEMBLY));
2436 36 : LibmeshPetscCall(MatAssemblyEnd(mat_array[Q * field_num + 0], MAT_FINAL_ASSEMBLY));
2437 36 : mat_array[Q * field_num + 1] = NULL;
2438 : if (_axial_mass_flow_tight_coupling)
2439 : {
2440 36 : LibmeshPetscCall(MatDuplicate(_mc_sumWij_mat, MAT_COPY_VALUES, &mat_array[Q * field_num + 2]));
2441 36 : LibmeshPetscCall(MatAssemblyBegin(mat_array[Q * field_num + 2], MAT_FINAL_ASSEMBLY));
2442 36 : LibmeshPetscCall(MatAssemblyEnd(mat_array[Q * field_num + 2], MAT_FINAL_ASSEMBLY));
2443 : }
2444 : else
2445 : {
2446 : mat_array[Q * field_num + 2] = NULL;
2447 : }
2448 36 : if (_monolithic_thermal_bool)
2449 : {
2450 0 : mat_array[Q * field_num + 3] = NULL;
2451 : }
2452 36 : LibmeshPetscCall(VecDuplicate(_mc_axial_convection_rhs, &vec_array[field_num]));
2453 36 : LibmeshPetscCall(VecCopy(_mc_axial_convection_rhs, vec_array[field_num]));
2454 : if (!_axial_mass_flow_tight_coupling)
2455 : {
2456 : Vec sumWij_loc;
2457 : LibmeshPetscCall(VecDuplicate(_mc_axial_convection_rhs, &sumWij_loc));
2458 : LibmeshPetscCall(VecSet(sumWij_loc, 0.0));
2459 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
2460 : {
2461 : auto iz_ind = iz - first_node;
2462 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
2463 : {
2464 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
2465 :
2466 : PetscScalar value_vec_2 = -1.0 * (*_SumWij_soln)(node_out);
2467 : PetscInt row_vec_2 = i_ch + _n_channels * iz_ind;
2468 : LibmeshPetscCall(VecSetValues(sumWij_loc, 1, &row_vec_2, &value_vec_2, ADD_VALUES));
2469 : }
2470 : }
2471 : LibmeshPetscCall(VecAXPY(vec_array[field_num], 1.0, sumWij_loc));
2472 : LibmeshPetscCall(VecDestroy(&sumWij_loc));
2473 : }
2474 :
2475 36 : _console << "Mass ok." << std::endl;
2476 : // Axial momentum conservation
2477 : field_num = 1;
2478 : if (_pressure_axial_momentum_tight_coupling)
2479 : {
2480 36 : LibmeshPetscCall(
2481 : MatDuplicate(_amc_sys_mdot_mat, MAT_COPY_VALUES, &mat_array[Q * field_num + 0]));
2482 36 : LibmeshPetscCall(MatAssemblyBegin(mat_array[Q * field_num + 0], MAT_FINAL_ASSEMBLY));
2483 36 : LibmeshPetscCall(MatAssemblyEnd(mat_array[Q * field_num + 0], MAT_FINAL_ASSEMBLY));
2484 : }
2485 : else
2486 : {
2487 : mat_array[Q * field_num + 0] = NULL;
2488 : }
2489 36 : LibmeshPetscCall(
2490 : MatDuplicate(_amc_pressure_force_mat, MAT_COPY_VALUES, &mat_array[Q * field_num + 1]));
2491 36 : LibmeshPetscCall(MatAssemblyBegin(mat_array[Q * field_num + 1], MAT_FINAL_ASSEMBLY));
2492 36 : LibmeshPetscCall(MatAssemblyEnd(mat_array[Q * field_num + 1], MAT_FINAL_ASSEMBLY));
2493 36 : mat_array[Q * field_num + 2] = NULL;
2494 36 : if (_monolithic_thermal_bool)
2495 : {
2496 0 : mat_array[Q * field_num + 3] = NULL;
2497 : }
2498 36 : LibmeshPetscCall(VecDuplicate(_amc_pressure_force_rhs, &vec_array[field_num]));
2499 36 : LibmeshPetscCall(VecCopy(_amc_pressure_force_rhs, vec_array[field_num]));
2500 : if (_pressure_axial_momentum_tight_coupling)
2501 : {
2502 36 : LibmeshPetscCall(VecAXPY(vec_array[field_num], 1.0, _amc_sys_mdot_rhs));
2503 : }
2504 : else
2505 : {
2506 : unsigned int last_node = (iblock + 1) * _block_size;
2507 : unsigned int first_node = iblock * _block_size + 1;
2508 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
2509 : _prod, *_mdot_soln, first_node, last_node, _n_channels));
2510 : Vec ls;
2511 : LibmeshPetscCall(VecDuplicate(_amc_sys_mdot_rhs, &ls));
2512 : LibmeshPetscCall(MatMult(_amc_sys_mdot_mat, _prod, ls));
2513 : LibmeshPetscCall(VecAXPY(ls, -1.0, _amc_sys_mdot_rhs));
2514 : LibmeshPetscCall(VecAXPY(vec_array[field_num], -1.0, ls));
2515 : LibmeshPetscCall(VecDestroy(&ls));
2516 : }
2517 :
2518 36 : _console << "Lin mom OK." << std::endl;
2519 :
2520 : // Cross momentum conservation
2521 : field_num = 2;
2522 36 : mat_array[Q * field_num + 0] = NULL;
2523 : if (_pressure_cross_momentum_tight_coupling)
2524 : {
2525 36 : LibmeshPetscCall(
2526 : MatDuplicate(_cmc_pressure_force_mat, MAT_COPY_VALUES, &mat_array[Q * field_num + 1]));
2527 36 : LibmeshPetscCall(MatAssemblyBegin(mat_array[Q * field_num + 1], MAT_FINAL_ASSEMBLY));
2528 36 : LibmeshPetscCall(MatAssemblyEnd(mat_array[Q * field_num + 1], MAT_FINAL_ASSEMBLY));
2529 : }
2530 : else
2531 : {
2532 : mat_array[Q * field_num + 1] = NULL;
2533 : }
2534 :
2535 36 : LibmeshPetscCall(MatDuplicate(_cmc_sys_Wij_mat, MAT_COPY_VALUES, &mat_array[Q * field_num + 2]));
2536 36 : LibmeshPetscCall(MatAssemblyBegin(mat_array[Q * field_num + 2], MAT_FINAL_ASSEMBLY));
2537 36 : LibmeshPetscCall(MatAssemblyEnd(mat_array[Q * field_num + 2], MAT_FINAL_ASSEMBLY));
2538 36 : if (_monolithic_thermal_bool)
2539 : {
2540 0 : mat_array[Q * field_num + 3] = NULL;
2541 : }
2542 :
2543 36 : LibmeshPetscCall(VecDuplicate(_cmc_sys_Wij_rhs, &vec_array[field_num]));
2544 36 : LibmeshPetscCall(VecCopy(_cmc_sys_Wij_rhs, vec_array[field_num]));
2545 : if (_pressure_cross_momentum_tight_coupling)
2546 : {
2547 36 : LibmeshPetscCall(VecAXPY(vec_array[field_num], 1.0, _cmc_pressure_force_rhs));
2548 : }
2549 : else
2550 : {
2551 : Vec sol_holder_P;
2552 : LibmeshPetscCall(createPetscVector(sol_holder_P, _block_size * _n_gaps));
2553 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
2554 : _prodp, *_P_soln, iblock * _block_size, (iblock + 1) * _block_size - 1, _n_channels));
2555 :
2556 : LibmeshPetscCall(MatMult(_cmc_pressure_force_mat, _prodp, sol_holder_P));
2557 : LibmeshPetscCall(VecAXPY(sol_holder_P, -1.0, _cmc_pressure_force_rhs));
2558 : LibmeshPetscCall(VecScale(sol_holder_P, 1.0));
2559 : LibmeshPetscCall(VecAXPY(vec_array[field_num], 1.0, sol_holder_P));
2560 : LibmeshPetscCall(VecDestroy(&sol_holder_P));
2561 : }
2562 :
2563 36 : _console << "Cross mom ok." << std::endl;
2564 :
2565 : // Energy conservation
2566 36 : if (_monolithic_thermal_bool)
2567 : {
2568 : field_num = 3;
2569 0 : mat_array[Q * field_num + 0] = NULL;
2570 0 : mat_array[Q * field_num + 1] = NULL;
2571 0 : mat_array[Q * field_num + 2] = NULL;
2572 0 : LibmeshPetscCall(MatDuplicate(_hc_sys_h_mat, MAT_COPY_VALUES, &mat_array[Q * field_num + 3]));
2573 0 : LibmeshPetscCall(MatAssemblyBegin(mat_array[Q * field_num + 3], MAT_FINAL_ASSEMBLY));
2574 0 : LibmeshPetscCall(MatAssemblyEnd(mat_array[Q * field_num + 3], MAT_FINAL_ASSEMBLY));
2575 0 : LibmeshPetscCall(VecDuplicate(_hc_sys_h_rhs, &vec_array[field_num]));
2576 0 : LibmeshPetscCall(VecCopy(_hc_sys_h_rhs, vec_array[field_num]));
2577 : }
2578 36 : _console << "Energy ok." << std::endl;
2579 :
2580 : // Relaxing linear system
2581 : // Weaker relaxation
2582 : if (true)
2583 : {
2584 : // Estimating cross-flow resistances to achieve realizable solves
2585 36 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
2586 : _prod, *_mdot_soln, first_node, last_node, _n_channels));
2587 : Vec mdot_estimate;
2588 36 : LibmeshPetscCall(createPetscVector(mdot_estimate, _block_size * _n_channels));
2589 : Vec pmat_diag;
2590 36 : LibmeshPetscCall(createPetscVector(pmat_diag, _block_size * _n_channels));
2591 : Vec p_estimate;
2592 36 : LibmeshPetscCall(createPetscVector(p_estimate, _block_size * _n_channels));
2593 : Vec unity_vec;
2594 36 : LibmeshPetscCall(createPetscVector(unity_vec, _block_size * _n_channels));
2595 36 : LibmeshPetscCall(VecSet(unity_vec, 1.0));
2596 : Vec sol_holder_P;
2597 36 : LibmeshPetscCall(createPetscVector(sol_holder_P, _block_size * _n_gaps));
2598 : Vec diag_Wij_loc;
2599 36 : LibmeshPetscCall(createPetscVector(diag_Wij_loc, _block_size * _n_gaps));
2600 : Vec Wij_estimate;
2601 36 : LibmeshPetscCall(createPetscVector(Wij_estimate, _block_size * _n_gaps));
2602 : Vec unity_vec_Wij;
2603 36 : LibmeshPetscCall(createPetscVector(unity_vec_Wij, _block_size * _n_gaps));
2604 36 : LibmeshPetscCall(VecSet(unity_vec_Wij, 1.0));
2605 : Vec _Wij_loc_vec;
2606 36 : LibmeshPetscCall(createPetscVector(_Wij_loc_vec, _block_size * _n_gaps));
2607 : Vec _Wij_old_loc_vec;
2608 36 : LibmeshPetscCall(createPetscVector(_Wij_old_loc_vec, _block_size * _n_gaps));
2609 36 : LibmeshPetscCall(MatMult(mat_array[Q], _prod, mdot_estimate));
2610 36 : LibmeshPetscCall(MatGetDiagonal(mat_array[Q + 1], pmat_diag));
2611 36 : LibmeshPetscCall(VecAXPY(pmat_diag, 1e-10, unity_vec));
2612 36 : LibmeshPetscCall(VecPointwiseDivide(p_estimate, mdot_estimate, pmat_diag));
2613 36 : LibmeshPetscCall(MatMult(mat_array[2 * Q + 1], p_estimate, sol_holder_P));
2614 36 : LibmeshPetscCall(VecAXPY(sol_holder_P, -1.0, _cmc_pressure_force_rhs));
2615 36 : LibmeshPetscCall(MatGetDiagonal(mat_array[2 * Q + 2], diag_Wij_loc));
2616 36 : LibmeshPetscCall(VecAXPY(diag_Wij_loc, 1e-10, unity_vec_Wij));
2617 36 : LibmeshPetscCall(VecPointwiseDivide(Wij_estimate, sol_holder_P, diag_Wij_loc));
2618 : Vec sumWij_loc;
2619 36 : LibmeshPetscCall(createPetscVector(sumWij_loc, _block_size * _n_channels));
2620 396 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
2621 : {
2622 360 : auto iz_ind = iz - first_node;
2623 13320 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
2624 : {
2625 12960 : PetscScalar sumWij = 0.0;
2626 : unsigned int counter = 0;
2627 56160 : for (auto i_gap : _subchannel_mesh.getChannelGaps(i_ch))
2628 : {
2629 43200 : auto chans = _subchannel_mesh.getGapChannels(i_gap);
2630 : unsigned int i_ch_loc = chans.first;
2631 43200 : PetscInt row_vec = i_ch_loc + _n_channels * iz_ind;
2632 : PetscScalar loc_Wij_value;
2633 43200 : LibmeshPetscCall(VecGetValues(sol_holder_P, 1, &row_vec, &loc_Wij_value));
2634 43200 : sumWij += _subchannel_mesh.getCrossflowSign(i_ch, counter) * loc_Wij_value;
2635 43200 : counter++;
2636 : }
2637 12960 : PetscInt row_vec = i_ch + _n_channels * iz_ind;
2638 12960 : LibmeshPetscCall(VecSetValues(sumWij_loc, 1, &row_vec, &sumWij, INSERT_VALUES));
2639 : }
2640 : }
2641 :
2642 : PetscScalar min_mdot;
2643 36 : LibmeshPetscCall(VecAbs(_prod));
2644 36 : LibmeshPetscCall(VecMin(_prod, NULL, &min_mdot));
2645 36 : _console << "Minimum estimated mdot: " << min_mdot << std::endl;
2646 :
2647 36 : LibmeshPetscCall(VecAbs(sumWij_loc));
2648 36 : LibmeshPetscCall(VecMax(sumWij_loc, NULL, &_max_sumWij));
2649 36 : _max_sumWij = std::max(1e-10, _max_sumWij);
2650 36 : _console << "Maximum estimated Wij: " << _max_sumWij << std::endl;
2651 :
2652 36 : LibmeshPetscCall(populateVectorFromDense<libMesh::DenseMatrix<Real>>(
2653 : _Wij_loc_vec, _Wij, first_node, last_node, _n_gaps));
2654 36 : LibmeshPetscCall(VecAbs(_Wij_loc_vec));
2655 36 : LibmeshPetscCall(populateVectorFromDense<libMesh::DenseMatrix<Real>>(
2656 : _Wij_old_loc_vec, _Wij_old, first_node, last_node, _n_gaps));
2657 36 : LibmeshPetscCall(VecAbs(_Wij_old_loc_vec));
2658 36 : LibmeshPetscCall(VecAXPY(_Wij_loc_vec, -1.0, _Wij_old_loc_vec));
2659 : PetscScalar relax_factor;
2660 36 : LibmeshPetscCall(VecAbs(_Wij_loc_vec));
2661 : #if !PETSC_VERSION_LESS_THAN(3, 16, 0)
2662 36 : LibmeshPetscCall(VecMean(_Wij_loc_vec, &relax_factor));
2663 : #else
2664 : LibmeshPetscCall(VecSum(_Wij_loc_vec, &relax_factor));
2665 : relax_factor /= _block_size * _n_gaps;
2666 : #endif
2667 36 : relax_factor = relax_factor / _max_sumWij + 0.5;
2668 36 : _console << "Relax base value: " << relax_factor << std::endl;
2669 :
2670 : PetscScalar resistance_relaxation = 0.9;
2671 36 : _added_K = _max_sumWij / min_mdot;
2672 36 : _console << "New cross resistance: " << _added_K << std::endl;
2673 36 : _added_K = (_added_K * resistance_relaxation + (1.0 - resistance_relaxation) * _added_K_old) *
2674 : relax_factor;
2675 36 : _console << "Relaxed cross resistance: " << _added_K << std::endl;
2676 36 : if (_added_K < 10 && _added_K >= 1.0)
2677 0 : _added_K = 1.0; //(1.0 - resistance_relaxation);
2678 36 : if (_added_K < 1.0 && _added_K >= 0.1)
2679 0 : _added_K = 0.5;
2680 36 : if (_added_K < 0.1 && _added_K >= 0.01)
2681 0 : _added_K = 1. / 3.;
2682 36 : if (_added_K < 1e-2 && _added_K >= 1e-3)
2683 0 : _added_K = 0.1;
2684 : if (_added_K < 1e-3)
2685 : _added_K = 1.0 * _added_K;
2686 36 : _console << "Actual added cross resistance: " << _added_K << std::endl;
2687 36 : LibmeshPetscCall(VecScale(unity_vec_Wij, _added_K));
2688 36 : _added_K_old = _added_K;
2689 :
2690 : // Adding cross resistances
2691 36 : LibmeshPetscCall(MatDiagonalSet(mat_array[2 * Q + 2], unity_vec_Wij, ADD_VALUES));
2692 36 : LibmeshPetscCall(VecDestroy(&mdot_estimate));
2693 36 : LibmeshPetscCall(VecDestroy(&pmat_diag));
2694 36 : LibmeshPetscCall(VecDestroy(&unity_vec));
2695 36 : LibmeshPetscCall(VecDestroy(&p_estimate));
2696 36 : LibmeshPetscCall(VecDestroy(&sol_holder_P));
2697 36 : LibmeshPetscCall(VecDestroy(&diag_Wij_loc));
2698 36 : LibmeshPetscCall(VecDestroy(&unity_vec_Wij));
2699 36 : LibmeshPetscCall(VecDestroy(&Wij_estimate));
2700 36 : LibmeshPetscCall(VecDestroy(&sumWij_loc));
2701 36 : LibmeshPetscCall(VecDestroy(&_Wij_loc_vec));
2702 36 : LibmeshPetscCall(VecDestroy(&_Wij_old_loc_vec));
2703 :
2704 : // Auto-computing relaxation factors
2705 : PetscScalar relaxation_factor_mdot, relaxation_factor_P, relaxation_factor_Wij;
2706 36 : relaxation_factor_mdot = 1.0;
2707 36 : relaxation_factor_P = 1.0; // std::exp(-5.0);
2708 36 : relaxation_factor_Wij = 0.1;
2709 :
2710 36 : _console << "Relax mdot: " << relaxation_factor_mdot << std::endl;
2711 36 : _console << "Relax P: " << relaxation_factor_P << std::endl;
2712 36 : _console << "Relax Wij: " << relaxation_factor_Wij << std::endl;
2713 :
2714 : PetscInt field_num = 0;
2715 : Vec diag_mdot;
2716 36 : LibmeshPetscCall(VecDuplicate(vec_array[field_num], &diag_mdot));
2717 36 : LibmeshPetscCall(MatGetDiagonal(mat_array[Q * field_num + field_num], diag_mdot));
2718 36 : LibmeshPetscCall(VecScale(diag_mdot, 1.0 / relaxation_factor_mdot));
2719 36 : LibmeshPetscCall(
2720 : MatDiagonalSet(mat_array[Q * field_num + field_num], diag_mdot, INSERT_VALUES));
2721 36 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
2722 : _prod, *_mdot_soln, first_node, last_node, _n_channels));
2723 36 : LibmeshPetscCall(VecScale(diag_mdot, (1.0 - relaxation_factor_mdot)));
2724 36 : LibmeshPetscCall(VecPointwiseMult(_prod, _prod, diag_mdot));
2725 36 : LibmeshPetscCall(VecAXPY(vec_array[field_num], 1.0, _prod));
2726 36 : LibmeshPetscCall(VecDestroy(&diag_mdot));
2727 :
2728 36 : _console << "mdot relaxed" << std::endl;
2729 :
2730 : field_num = 1;
2731 : Vec diag_P;
2732 36 : LibmeshPetscCall(VecDuplicate(vec_array[field_num], &diag_P));
2733 36 : LibmeshPetscCall(MatGetDiagonal(mat_array[Q * field_num + field_num], diag_P));
2734 36 : LibmeshPetscCall(VecScale(diag_P, 1.0 / relaxation_factor_P));
2735 36 : LibmeshPetscCall(MatDiagonalSet(mat_array[Q * field_num + field_num], diag_P, INSERT_VALUES));
2736 36 : _console << "Mat assembled" << std::endl;
2737 36 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
2738 : _prod, *_P_soln, first_node, last_node, _n_channels));
2739 36 : LibmeshPetscCall(VecScale(diag_P, (1.0 - relaxation_factor_P)));
2740 36 : LibmeshPetscCall(VecPointwiseMult(_prod, _prod, diag_P));
2741 36 : LibmeshPetscCall(VecAXPY(vec_array[field_num], 1.0, _prod));
2742 36 : LibmeshPetscCall(VecDestroy(&diag_P));
2743 :
2744 36 : _console << "P relaxed" << std::endl;
2745 :
2746 : field_num = 2;
2747 : Vec diag_Wij;
2748 36 : LibmeshPetscCall(VecDuplicate(vec_array[field_num], &diag_Wij));
2749 36 : LibmeshPetscCall(MatGetDiagonal(mat_array[Q * field_num + field_num], diag_Wij));
2750 36 : LibmeshPetscCall(VecScale(diag_Wij, 1.0 / relaxation_factor_Wij));
2751 36 : LibmeshPetscCall(MatDiagonalSet(mat_array[Q * field_num + field_num], diag_Wij, INSERT_VALUES));
2752 36 : LibmeshPetscCall(populateVectorFromDense<libMesh::DenseMatrix<Real>>(
2753 : _Wij_vec, _Wij, first_node, last_node, _n_gaps));
2754 36 : LibmeshPetscCall(VecScale(diag_Wij, (1.0 - relaxation_factor_Wij)));
2755 36 : LibmeshPetscCall(VecPointwiseMult(_Wij_vec, _Wij_vec, diag_Wij));
2756 36 : LibmeshPetscCall(VecAXPY(vec_array[field_num], 1.0, _Wij_vec));
2757 36 : LibmeshPetscCall(VecDestroy(&diag_Wij));
2758 :
2759 36 : _console << "Wij relaxed" << std::endl;
2760 : }
2761 36 : _console << "Linear solver relaxed." << std::endl;
2762 :
2763 : // Creating nested matrices
2764 36 : LibmeshPetscCall(MatCreateNest(PETSC_COMM_WORLD, Q, NULL, Q, NULL, mat_array.data(), &A_nest));
2765 36 : LibmeshPetscCall(VecCreateNest(PETSC_COMM_WORLD, Q, NULL, vec_array.data(), &b_nest));
2766 36 : _console << "Nested system created." << std::endl;
2767 :
2768 : /// Setting up linear solver
2769 : // Creating linear solver
2770 36 : LibmeshPetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
2771 36 : LibmeshPetscCall(KSPSetType(ksp, KSPFGMRES));
2772 : // Setting KSP operators
2773 36 : LibmeshPetscCall(KSPSetOperators(ksp, A_nest, A_nest));
2774 : // Set KSP and PC options
2775 36 : LibmeshPetscCall(KSPGetPC(ksp, &pc));
2776 36 : LibmeshPetscCall(PCSetType(pc, PCFIELDSPLIT));
2777 36 : LibmeshPetscCall(KSPSetTolerances(ksp, _rtol, _atol, _dtol, _maxit));
2778 : // Splitting fields
2779 36 : std::vector<IS> rows(Q);
2780 : // IS rows[Q];
2781 : PetscInt M = 0;
2782 36 : LibmeshPetscCall(MatNestGetISs(A_nest, rows.data(), NULL));
2783 144 : for (PetscInt j = 0; j < Q; ++j)
2784 : {
2785 : IS expand1;
2786 108 : LibmeshPetscCall(ISDuplicate(rows[M], &expand1));
2787 108 : M += 1;
2788 108 : LibmeshPetscCall(PCFieldSplitSetIS(pc, NULL, expand1));
2789 108 : LibmeshPetscCall(ISDestroy(&expand1));
2790 : }
2791 36 : _console << "Linear solver assembled." << std::endl;
2792 :
2793 : /// Solving
2794 36 : LibmeshPetscCall(VecDuplicate(b_nest, &x_nest));
2795 36 : LibmeshPetscCall(VecSet(x_nest, 0.0));
2796 36 : LibmeshPetscCall(KSPSolve(ksp, b_nest, x_nest));
2797 :
2798 : /// Destroying solver elements
2799 36 : LibmeshPetscCall(VecDestroy(&b_nest));
2800 36 : LibmeshPetscCall(MatDestroy(&A_nest));
2801 36 : LibmeshPetscCall(KSPDestroy(&ksp));
2802 360 : for (PetscInt i = 0; i < Q * Q; i++)
2803 : {
2804 324 : LibmeshPetscCall(MatDestroy(&mat_array[i]));
2805 : }
2806 144 : for (PetscInt i = 0; i < Q; i++)
2807 : {
2808 108 : LibmeshPetscCall(VecDestroy(&vec_array[i]));
2809 : }
2810 :
2811 : /// Recovering the solutions
2812 : Vec sol_mdot, sol_p, sol_Wij;
2813 36 : _console << "Vectors created." << std::endl;
2814 : PetscInt num_vecs;
2815 : Vec * loc_vecs;
2816 36 : LibmeshPetscCall(VecNestGetSubVecs(x_nest, &num_vecs, &loc_vecs));
2817 36 : _console << "Starting extraction." << std::endl;
2818 36 : LibmeshPetscCall(VecDuplicate(_mc_axial_convection_rhs, &sol_mdot));
2819 36 : LibmeshPetscCall(VecCopy(loc_vecs[0], sol_mdot));
2820 36 : LibmeshPetscCall(VecDuplicate(_amc_sys_mdot_rhs, &sol_p));
2821 36 : LibmeshPetscCall(VecCopy(loc_vecs[1], sol_p));
2822 36 : LibmeshPetscCall(VecDuplicate(_cmc_sys_Wij_rhs, &sol_Wij));
2823 36 : LibmeshPetscCall(VecCopy(loc_vecs[2], sol_Wij));
2824 36 : _console << "Getting individual field solutions from coupled solver." << std::endl;
2825 :
2826 : /// Assigning the solutions to arrays
2827 : PetscScalar * sol_p_array;
2828 36 : LibmeshPetscCall(VecGetArray(sol_p, &sol_p_array));
2829 : PetscScalar * sol_Wij_array;
2830 36 : LibmeshPetscCall(VecGetArray(sol_Wij, &sol_Wij_array));
2831 :
2832 : /// Populating Mass flow
2833 36 : LibmeshPetscCall(populateSolutionChan<SolutionHandle>(
2834 : sol_mdot, *_mdot_soln, first_node, last_node, _n_channels));
2835 :
2836 : /// Populating Pressure
2837 396 : for (unsigned int iz = last_node; iz > first_node - 1; iz--)
2838 : {
2839 360 : auto iz_ind = iz - first_node;
2840 13320 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
2841 : {
2842 12960 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
2843 12960 : PetscScalar value = sol_p_array[iz_ind * _n_channels + i_ch];
2844 12960 : _P_soln->set(node_in, value);
2845 : }
2846 : }
2847 :
2848 : /// Populating Crossflow
2849 36 : LibmeshPetscCall(populateDenseFromVector<libMesh::DenseMatrix<Real>>(
2850 : sol_Wij, _Wij, first_node, last_node - 1, _n_gaps));
2851 :
2852 : /// Populating Enthalpy
2853 36 : if (_monolithic_thermal_bool)
2854 : {
2855 : Vec sol_h;
2856 0 : LibmeshPetscCall(VecDuplicate(_hc_sys_h_rhs, &sol_h));
2857 0 : LibmeshPetscCall(VecCopy(loc_vecs[3], sol_h));
2858 : PetscScalar * sol_h_array;
2859 0 : LibmeshPetscCall(VecGetArray(sol_h, &sol_h_array));
2860 0 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
2861 : {
2862 0 : auto iz_ind = iz - first_node;
2863 0 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
2864 : {
2865 0 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
2866 0 : auto h_out = sol_h_array[iz_ind * _n_channels + i_ch];
2867 0 : if (h_out < 0)
2868 : {
2869 0 : mooseError(name(),
2870 : " : Calculation of negative Enthalpy h_out = : ",
2871 : h_out,
2872 : " Axial Level= : ",
2873 : iz);
2874 : }
2875 0 : _h_soln->set(node_out, h_out);
2876 : }
2877 : }
2878 0 : LibmeshPetscCall(VecDestroy(&sol_h));
2879 : }
2880 :
2881 : /// Populating sum_Wij
2882 36 : LibmeshPetscCall(MatMult(_mc_sumWij_mat, sol_Wij, _prod));
2883 36 : LibmeshPetscCall(populateSolutionChan<SolutionHandle>(
2884 : _prod, *_SumWij_soln, first_node, last_node, _n_channels));
2885 :
2886 : Vec sumWij_loc;
2887 36 : LibmeshPetscCall(createPetscVector(sumWij_loc, _block_size * _n_channels));
2888 36 : LibmeshPetscCall(populateVectorFromHandle<SolutionHandle>(
2889 : _prod, *_SumWij_soln, first_node, last_node, _n_channels));
2890 :
2891 36 : LibmeshPetscCall(VecAbs(_prod));
2892 36 : LibmeshPetscCall(VecMax(_prod, NULL, &_max_sumWij_new));
2893 36 : _console << "Maximum estimated Wij new: " << _max_sumWij_new << std::endl;
2894 36 : _correction_factor = _max_sumWij_new / _max_sumWij;
2895 36 : _console << "Correction factor: " << _correction_factor << std::endl;
2896 36 : _console << "Solutions assigned to MOOSE variables." << std::endl;
2897 :
2898 : /// Destroying arrays
2899 36 : LibmeshPetscCall(VecDestroy(&x_nest));
2900 36 : LibmeshPetscCall(VecDestroy(&sol_mdot));
2901 36 : LibmeshPetscCall(VecDestroy(&sol_p));
2902 36 : LibmeshPetscCall(VecDestroy(&sol_Wij));
2903 36 : LibmeshPetscCall(VecDestroy(&sumWij_loc));
2904 36 : _console << "Solutions destroyed." << std::endl;
2905 :
2906 36 : PetscFunctionReturn(LIBMESH_PETSC_SUCCESS);
2907 36 : }
2908 :
2909 : void
2910 54 : InterWrapper1PhaseProblem::initializeSolution()
2911 : {
2912 54 : unsigned int last_node = _n_cells;
2913 : unsigned int first_node = 1;
2914 594 : for (unsigned int iz = first_node; iz < last_node + 1; iz++)
2915 : {
2916 21780 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
2917 : {
2918 21240 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, iz);
2919 21240 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, iz - 1);
2920 21240 : _mdot_soln->set(node_out, (*_mdot_soln)(node_in));
2921 : }
2922 : }
2923 : _mdot_soln->close();
2924 54 : }
2925 :
2926 : void
2927 24 : InterWrapper1PhaseProblem::externalSolve()
2928 : {
2929 24 : _console << "Executing subchannel solver\n";
2930 24 : initializeSolution();
2931 24 : _console << "Solution initialized" << std::endl;
2932 24 : Real P_error = 1.0;
2933 24 : unsigned int P_it = 0;
2934 : unsigned int P_it_max;
2935 :
2936 24 : if (_segregated_bool)
2937 12 : P_it_max = 20 * _n_blocks;
2938 : else
2939 : P_it_max = 100;
2940 :
2941 24 : if ((_n_blocks == 1) && (_segregated_bool))
2942 : P_it_max = 5;
2943 :
2944 138 : while ((P_error > _P_tol && P_it < P_it_max))
2945 : {
2946 114 : P_it += 1;
2947 114 : if (P_it == P_it_max && _n_blocks != 1)
2948 : {
2949 0 : _console << "Reached maximum number of axial pressure iterations" << std::endl;
2950 0 : _converged = false;
2951 : }
2952 114 : _console << "Solving Outer Iteration : " << P_it << std::endl;
2953 114 : auto P_L2norm_old_axial = _P_soln->L2norm();
2954 822 : for (unsigned int iblock = 0; iblock < _n_blocks; iblock++)
2955 : {
2956 708 : int last_level = (iblock + 1) * _block_size;
2957 708 : int first_level = iblock * _block_size + 1;
2958 708 : Real T_block_error = 1.0;
2959 : auto T_it = 0;
2960 708 : _console << "Solving Block: " << iblock << " From first level: " << first_level
2961 708 : << " to last level: " << last_level << std::endl;
2962 1416 : while (T_block_error > _T_tol && T_it < _T_maxit)
2963 : {
2964 708 : T_it += 1;
2965 708 : if (T_it == _T_maxit)
2966 : {
2967 0 : _console << "Reached maximum number of temperature iterations for block: " << iblock
2968 0 : << std::endl;
2969 0 : _converged = false;
2970 : }
2971 708 : auto T_L2norm_old_block = _T_soln->L2norm();
2972 :
2973 708 : if (_segregated_bool)
2974 : {
2975 672 : computeWijFromSolve(iblock);
2976 :
2977 672 : if (_compute_power)
2978 : {
2979 0 : computeh(iblock);
2980 0 : _console << "Done with h solve" << std::endl;
2981 0 : computeT(iblock);
2982 0 : _console << "Done with T solve" << std::endl;
2983 : }
2984 : }
2985 : else
2986 : {
2987 36 : LibmeshPetscCall(implicitPetscSolve(iblock));
2988 36 : computeWijPrime(iblock);
2989 :
2990 36 : _console << "Done with main solve." << std::endl;
2991 36 : if (_monolithic_thermal_bool)
2992 : {
2993 : // Enthalpy is already solved from the monolithic solve
2994 0 : computeT(iblock);
2995 : }
2996 : else
2997 : {
2998 36 : if (_compute_power)
2999 : {
3000 0 : computeh(iblock);
3001 0 : computeT(iblock);
3002 : }
3003 36 : _console << "Done with thermal solve." << std::endl;
3004 : }
3005 : }
3006 :
3007 708 : if (_compute_density)
3008 696 : computeRho(iblock);
3009 :
3010 708 : if (_compute_viscosity)
3011 696 : computeMu(iblock);
3012 :
3013 708 : _console << "Done updating thermophysical properties." << std::endl;
3014 :
3015 : // We must do a global assembly to make sure data is parallel consistent before we do things
3016 : // like compute L2 norms
3017 708 : _aux->solution().close();
3018 :
3019 708 : auto T_L2norm_new = _T_soln->L2norm();
3020 708 : T_block_error =
3021 708 : std::abs((T_L2norm_new - T_L2norm_old_block) / (T_L2norm_old_block + 1E-14));
3022 708 : _console << "T_block_error: " << T_block_error << std::endl;
3023 : }
3024 : }
3025 114 : auto P_L2norm_new_axial = _P_soln->L2norm();
3026 114 : P_error =
3027 114 : std::abs((P_L2norm_new_axial - P_L2norm_old_axial) / (P_L2norm_old_axial + _P_out + 1E-14));
3028 114 : _console << "P_error :" << P_error << std::endl;
3029 : }
3030 : // update old crossflow matrix
3031 24 : _Wij_old = _Wij;
3032 :
3033 : auto power_in = 0.0;
3034 : auto power_out = 0.0;
3035 888 : for (unsigned int i_ch = 0; i_ch < _n_channels; i_ch++)
3036 : {
3037 864 : auto * node_in = _subchannel_mesh.getChannelNode(i_ch, 0);
3038 864 : auto * node_out = _subchannel_mesh.getChannelNode(i_ch, _n_cells);
3039 864 : power_in += (*_mdot_soln)(node_in) * (*_h_soln)(node_in);
3040 864 : power_out += (*_mdot_soln)(node_out) * (*_h_soln)(node_out);
3041 : }
3042 24 : _console << "Power added to coolant is: " << power_out - power_in << " Watt" << std::endl;
3043 24 : if (_pin_mesh_exist)
3044 : {
3045 0 : _console << "Commencing calculation of Pin surface temperature \n";
3046 0 : for (unsigned int i_pin = 0; i_pin < _n_assemblies; i_pin++)
3047 : {
3048 0 : for (unsigned int iz = 0; iz < _n_cells + 1; ++iz)
3049 : {
3050 0 : auto * pin_node = _subchannel_mesh.getPinNode(i_pin, iz);
3051 : Real sumTemp = 0.0;
3052 : // Calculate sum of pin surface temperatures that the channels around the pin see
3053 0 : for (auto i_ch : _subchannel_mesh.getPinChannels(i_pin))
3054 : {
3055 0 : auto * node = _subchannel_mesh.getChannelNode(i_ch, iz);
3056 :
3057 0 : auto mu = (*_mu_soln)(node);
3058 0 : auto S = (*_S_flow_soln)(node);
3059 0 : auto w_perim = (*_w_perim_soln)(node);
3060 0 : auto Dh_i = 4.0 * S / w_perim;
3061 0 : auto Re = (((*_mdot_soln)(node) / S) * Dh_i / mu);
3062 :
3063 0 : auto k = _fp->k_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
3064 0 : auto cp = _fp->cp_from_p_T((*_P_soln)(node) + _P_out, (*_T_soln)(node));
3065 0 : auto Pr = (*_mu_soln)(node)*cp / k;
3066 :
3067 0 : auto Nu = 0.023 * std::pow(Re, 0.8) * std::pow(Pr, 0.4);
3068 0 : auto hw = Nu * k / Dh_i;
3069 :
3070 0 : auto perimeter = 2.0 * (_subchannel_mesh.getSideX() + _subchannel_mesh.getSideY());
3071 0 : sumTemp += (*_q_prime_soln)(pin_node) / (perimeter * hw) + (*_T_soln)(node);
3072 : }
3073 0 : _Tpin_soln->set(pin_node, 0.25 * sumTemp);
3074 : }
3075 : }
3076 : }
3077 24 : _aux->solution().close();
3078 24 : _aux->update();
3079 24 : }
3080 :
3081 : void
3082 108 : InterWrapper1PhaseProblem::syncSolutions(Direction /*direction*/)
3083 : {
3084 108 : }
|