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