LCOV - code coverage report
Current view: top level - src/executioners - LinearAssemblySegregatedSolve.C (source / functions) Hit Total Coverage
Test: idaholab/moose navier_stokes: 9fc4b0 Lines: 290 324 89.5 %
Date: 2025-08-14 10:14:56 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "LinearAssemblySegregatedSolve.h"
      11             : #include "FEProblem.h"
      12             : #include "SegregatedSolverUtils.h"
      13             : #include "LinearSystem.h"
      14             : 
      15             : using namespace libMesh;
      16             : 
      17             : InputParameters
      18        1618 : LinearAssemblySegregatedSolve::validParams()
      19             : {
      20        1618 :   InputParameters params = SIMPLESolveBase::validParams();
      21             : 
      22        3236 :   params.addParam<std::vector<SolverSystemName>>(
      23             :       "active_scalar_systems", {}, "The solver system for each active scalar advection equation.");
      24             : 
      25             :   /*
      26             :    * Parameters to control the solution of each scalar advection system
      27             :    */
      28        1618 :   params.addParam<std::vector<Real>>("active_scalar_equation_relaxation",
      29        1618 :                                      std::vector<Real>(),
      30             :                                      "The relaxation which should be used for the active scalar "
      31             :                                      "equations. (=1 for no relaxation, "
      32             :                                      "diagonal dominance will still be enforced)");
      33             : 
      34        3236 :   params.addParam<MultiMooseEnum>("active_scalar_petsc_options",
      35        3236 :                                   Moose::PetscSupport::getCommonPetscFlags(),
      36             :                                   "Singleton PETSc options for the active scalar equation(s)");
      37        3236 :   params.addParam<MultiMooseEnum>(
      38             :       "active_scalar_petsc_options_iname",
      39        3236 :       Moose::PetscSupport::getCommonPetscKeys(),
      40             :       "Names of PETSc name/value pairs for the active scalar equation(s)");
      41        3236 :   params.addParam<std::vector<std::string>>(
      42             :       "active_scalar_petsc_options_value",
      43             :       "Values of PETSc name/value pairs (must correspond with \"petsc_options_iname\" for the "
      44             :       "active scalar equation(s)");
      45        1618 :   params.addParam<std::vector<Real>>(
      46             :       "active_scalar_absolute_tolerance",
      47        1618 :       std::vector<Real>(),
      48             :       "The absolute tolerance(s) on the normalized residual(s) of the active scalar equation(s).");
      49        4854 :   params.addRangeCheckedParam<Real>("active_scalar_l_tol",
      50        3236 :                                     1e-5,
      51             :                                     "0.0<=active_scalar_l_tol & active_scalar_l_tol<1.0",
      52             :                                     "The relative tolerance on the normalized residual in the "
      53             :                                     "linear solver of the active scalar equation(s).");
      54        4854 :   params.addRangeCheckedParam<Real>("active_scalar_l_abs_tol",
      55        3236 :                                     1e-10,
      56             :                                     "0.0<active_scalar_l_abs_tol",
      57             :                                     "The absolute tolerance on the normalized residual in the "
      58             :                                     "linear solver of the active scalar equation(s).");
      59        3236 :   params.addParam<unsigned int>(
      60             :       "active_scalar_l_max_its",
      61        3236 :       10000,
      62             :       "The maximum allowed iterations in the linear solver of the turbulence equation.");
      63             : 
      64        3236 :   params.addParamNamesToGroup(
      65             :       "active_scalar_systems active_scalar_equation_relaxation active_scalar_petsc_options "
      66             :       "active_scalar_petsc_options_iname "
      67             :       "active_scalar_petsc_options_value active_scalar_petsc_options_value "
      68             :       "active_scalar_absolute_tolerance "
      69             :       "active_scalar_l_tol active_scalar_l_abs_tol active_scalar_l_max_its",
      70             :       "Active Scalars Equations");
      71             : 
      72        1618 :   return params;
      73           0 : }
      74             : 
      75         809 : LinearAssemblySegregatedSolve::LinearAssemblySegregatedSolve(Executioner & ex)
      76             :   : SIMPLESolveBase(ex),
      77        1618 :     _pressure_sys_number(_problem.linearSysNum(getParam<SolverSystemName>("pressure_system"))),
      78         809 :     _pressure_system(_problem.getLinearSystem(_pressure_sys_number)),
      79        1618 :     _energy_sys_number(_has_energy_system
      80        1329 :                            ? _problem.linearSysNum(getParam<SolverSystemName>("energy_system"))
      81             :                            : libMesh::invalid_uint),
      82         809 :     _energy_system(_has_energy_system ? &_problem.getLinearSystem(_energy_sys_number) : nullptr),
      83         809 :     _solid_energy_sys_number(
      84         809 :         _has_solid_energy_system
      85         845 :             ? _problem.linearSysNum(getParam<SolverSystemName>("solid_energy_system"))
      86             :             : libMesh::invalid_uint),
      87         809 :     _solid_energy_system(
      88         809 :         _has_solid_energy_system ? &_problem.getLinearSystem(_solid_energy_sys_number) : nullptr),
      89        1618 :     _active_scalar_system_names(getParam<std::vector<SolverSystemName>>("active_scalar_systems")),
      90         809 :     _has_active_scalar_systems(!_active_scalar_system_names.empty()),
      91        1618 :     _active_scalar_equation_relaxation(
      92             :         getParam<std::vector<Real>>("active_scalar_equation_relaxation")),
      93        1618 :     _active_scalar_l_abs_tol(getParam<Real>("active_scalar_l_abs_tol")),
      94        1618 :     _active_scalar_absolute_tolerance(
      95        1618 :         getParam<std::vector<Real>>("active_scalar_absolute_tolerance"))
      96             : {
      97             :   // We fetch the systems and their numbers for the momentum equations.
      98        2388 :   for (auto system_i : index_range(_momentum_system_names))
      99             :   {
     100        1579 :     _momentum_system_numbers.push_back(_problem.linearSysNum(_momentum_system_names[system_i]));
     101        1579 :     _momentum_systems.push_back(&_problem.getLinearSystem(_momentum_system_numbers[system_i]));
     102        1579 :     _systems_to_solve.push_back(_momentum_systems.back());
     103             :   }
     104             : 
     105         809 :   _systems_to_solve.push_back(&_pressure_system);
     106             : 
     107         809 :   if (_has_energy_system)
     108         260 :     _systems_to_solve.push_back(_energy_system);
     109             : 
     110         809 :   if (_has_solid_energy_system)
     111          18 :     _systems_to_solve.push_back(_solid_energy_system);
     112             :   // and for the turbulence surrogate equations
     113         809 :   if (_has_turbulence_systems)
     114         132 :     for (auto system_i : index_range(_turbulence_system_names))
     115             :     {
     116          88 :       _turbulence_system_numbers.push_back(
     117          88 :           _problem.linearSysNum(_turbulence_system_names[system_i]));
     118          88 :       _turbulence_systems.push_back(
     119          88 :           &_problem.getLinearSystem(_turbulence_system_numbers[system_i]));
     120             :     }
     121             : 
     122             :   // and for the passive scalar equations
     123         809 :   if (_has_passive_scalar_systems)
     124         138 :     for (auto system_i : index_range(_passive_scalar_system_names))
     125             :     {
     126          92 :       _passive_scalar_system_numbers.push_back(
     127          92 :           _problem.linearSysNum(_passive_scalar_system_names[system_i]));
     128          92 :       _passive_scalar_systems.push_back(
     129          92 :           &_problem.getLinearSystem(_passive_scalar_system_numbers[system_i]));
     130          92 :       _systems_to_solve.push_back(_passive_scalar_systems.back());
     131             :     }
     132             : 
     133             :   // and for the active scalar equations
     134         809 :   if (_has_active_scalar_systems)
     135         118 :     for (auto system_i : index_range(_active_scalar_system_names))
     136             :     {
     137          59 :       _active_scalar_system_numbers.push_back(
     138          59 :           _problem.linearSysNum(_active_scalar_system_names[system_i]));
     139          59 :       _active_scalar_systems.push_back(
     140          59 :           &_problem.getLinearSystem(_active_scalar_system_numbers[system_i]));
     141          59 :       _systems_to_solve.push_back(_active_scalar_systems.back());
     142             : 
     143             :       const auto & active_scalar_petsc_options =
     144          59 :           getParam<MultiMooseEnum>("active_scalar_petsc_options");
     145             :       const auto & active_scalar_petsc_pair_options = getParam<MooseEnumItem, std::string>(
     146         118 :           "active_scalar_petsc_options_iname", "active_scalar_petsc_options_value");
     147          59 :       Moose::PetscSupport::addPetscFlagsToPetscOptions(
     148             :           active_scalar_petsc_options, "-", *this, _active_scalar_petsc_options);
     149         118 :       Moose::PetscSupport::addPetscPairsToPetscOptions(active_scalar_petsc_pair_options,
     150          59 :                                                        _problem.mesh().dimension(),
     151             :                                                        "-",
     152             :                                                        *this,
     153             :                                                        _active_scalar_petsc_options);
     154             : 
     155          59 :       _active_scalar_linear_control.real_valued_data["rel_tol"] =
     156         118 :           getParam<Real>("active_scalar_l_tol");
     157          59 :       _active_scalar_linear_control.real_valued_data["abs_tol"] =
     158         118 :           getParam<Real>("active_scalar_l_abs_tol");
     159          59 :       _active_scalar_linear_control.int_valued_data["max_its"] =
     160         118 :           getParam<unsigned int>("active_scalar_l_max_its");
     161          59 :     }
     162             : 
     163         809 :   if (_active_scalar_equation_relaxation.size() != _active_scalar_system_names.size())
     164           0 :     paramError("active_scalar_equation_relaxation",
     165             :                "Should be the same size as the number of systems");
     166             : 
     167             :   // We disable the prefix here for the time being, the segregated solvers use a different approach
     168             :   // for setting the petsc parameters
     169        3626 :   for (auto & system : _systems_to_solve)
     170        2817 :     system->system().prefix_with_name(false);
     171         809 : }
     172             : 
     173             : void
     174         807 : LinearAssemblySegregatedSolve::linkRhieChowUserObject()
     175             : {
     176         807 :   _rc_uo =
     177         807 :       const_cast<RhieChowMassFlux *>(&getUserObject<RhieChowMassFlux>("rhie_chow_user_object"));
     178         807 :   _rc_uo->linkMomentumPressureSystems(
     179         807 :       _momentum_systems, _pressure_system, _momentum_system_numbers);
     180             : 
     181             :   // Initialize the face velocities in the RC object
     182         807 :   if (!_app.isRecovering())
     183         739 :     _rc_uo->initFaceMassFlux();
     184         807 :   _rc_uo->initCouplingField();
     185         807 : }
     186             : 
     187             : std::vector<std::pair<unsigned int, Real>>
     188      162117 : LinearAssemblySegregatedSolve::solveMomentumPredictor()
     189             : {
     190             :   // Temporary storage for the (flux-normalized) residuals from
     191             :   // different momentum components
     192             :   std::vector<std::pair<unsigned int, Real>> its_normalized_residuals;
     193             : 
     194             :   LinearImplicitSystem & momentum_system_0 =
     195      162117 :       libMesh::cast_ref<LinearImplicitSystem &>(_momentum_systems[0]->system());
     196             : 
     197             :   PetscLinearSolver<Real> & momentum_solver =
     198      162117 :       libMesh::cast_ref<PetscLinearSolver<Real> &>(*momentum_system_0.get_linear_solver());
     199             : 
     200             :   // Solve the momentum equations.
     201             :   // TO DO: These equations are VERY similar. If we can store the differences (things coming from
     202             :   // BCs for example) separately, it is enough to construct one matrix.
     203      423538 :   for (const auto system_i : index_range(_momentum_systems))
     204             :   {
     205      261421 :     _problem.setCurrentLinearSystem(_momentum_system_numbers[system_i]);
     206             : 
     207             :     // We will need the right hand side and the solution of the next component
     208             :     LinearImplicitSystem & momentum_system =
     209      261421 :         libMesh::cast_ref<LinearImplicitSystem &>(_momentum_systems[system_i]->system());
     210             : 
     211             :     NumericVector<Number> & solution = *(momentum_system.solution);
     212      261421 :     NumericVector<Number> & rhs = *(momentum_system.rhs);
     213      261421 :     SparseMatrix<Number> & mmat = *(momentum_system.matrix);
     214             : 
     215      261421 :     auto diff_diagonal = solution.zero_clone();
     216             : 
     217             :     // We assemble the matrix and the right hand side
     218      261421 :     _problem.computeLinearSystemSys(momentum_system, mmat, rhs, /*compute_grads*/ true);
     219             : 
     220             :     // Still need to relax the right hand side with the same vector
     221      261421 :     NS::FV::relaxMatrix(mmat, _momentum_equation_relaxation, *diff_diagonal);
     222      261421 :     NS::FV::relaxRightHandSide(rhs, solution, *diff_diagonal);
     223             : 
     224             :     // The normalization factor depends on the right hand side so we need to recompute it for this
     225             :     // component
     226      261421 :     Real norm_factor = NS::FV::computeNormalizationFactor(solution, mmat, rhs);
     227             : 
     228             :     // Very important, for deciding the convergence, we need the unpreconditioned
     229             :     // norms in the linear solve
     230      261421 :     LibmeshPetscCall(KSPSetNormType(momentum_solver.ksp(), KSP_NORM_UNPRECONDITIONED));
     231             :     // Solve this component. We don't update the ghosted solution yet, that will come at the end
     232             :     // of the corrector step. Also setting the linear tolerances and maximum iteration counts.
     233      261421 :     _momentum_linear_control.real_valued_data["abs_tol"] = _momentum_l_abs_tol * norm_factor;
     234      261421 :     momentum_solver.set_solver_configuration(_momentum_linear_control);
     235             : 
     236             :     // We solve the equation
     237      261421 :     auto its_resid_pair = momentum_solver.solve(mmat, mmat, solution, rhs);
     238      261421 :     momentum_system.update();
     239             : 
     240             :     // We will reuse the preconditioner for every momentum system
     241      261421 :     if (system_i == 0)
     242      162117 :       momentum_solver.reuse_preconditioner(true);
     243             : 
     244             :     // Save the normalized residual
     245             :     its_normalized_residuals.push_back(
     246      261421 :         std::make_pair(its_resid_pair.first, momentum_solver.get_initial_residual() / norm_factor));
     247             : 
     248      261421 :     if (_print_fields)
     249             :     {
     250           0 :       _console << " solution after solve " << std::endl;
     251           0 :       solution.print();
     252           0 :       _console << " matrix when we solve " << std::endl;
     253           0 :       mmat.print();
     254           0 :       _console << " rhs when we solve " << std::endl;
     255           0 :       rhs.print();
     256           0 :       _console << " velocity solution component " << system_i << std::endl;
     257           0 :       solution.print();
     258           0 :       _console << "Norm factor " << norm_factor << std::endl;
     259           0 :       _console << Moose::stringify(momentum_solver.get_initial_residual()) << std::endl;
     260             :     }
     261             : 
     262             :     // Printing residuals
     263      261421 :     _console << " Momentum equation:"
     264             :              << (_momentum_systems.size() > 1
     265     1115291 :                      ? std::string(" Component ") + std::to_string(system_i + 1) + std::string(" ")
     266      261421 :                      : std::string(" "))
     267      261421 :              << COLOR_GREEN << its_normalized_residuals[system_i].second << COLOR_DEFAULT
     268      261421 :              << " Linear its: " << its_normalized_residuals[system_i].first << std::endl;
     269      261421 :   }
     270             : 
     271      423538 :   for (const auto system_i : index_range(_momentum_systems))
     272             :   {
     273             :     LinearImplicitSystem & momentum_system =
     274      261421 :         libMesh::cast_ref<LinearImplicitSystem &>(_momentum_systems[system_i]->system());
     275      261421 :     _momentum_systems[system_i]->setSolution(*(momentum_system.current_local_solution));
     276      261421 :     _momentum_systems[system_i]->copyPreviousNonlinearSolutions();
     277             :   }
     278             : 
     279             :   // We reset this to ensure the preconditioner is recomputed new time we go to the momentum
     280             :   // predictor
     281      162117 :   momentum_solver.reuse_preconditioner(false);
     282             : 
     283      162117 :   return its_normalized_residuals;
     284           0 : }
     285             : 
     286             : std::pair<unsigned int, Real>
     287      183817 : LinearAssemblySegregatedSolve::solvePressureCorrector()
     288             : {
     289      183817 :   _problem.setCurrentLinearSystem(_pressure_sys_number);
     290             : 
     291             :   // We will need some members from the linear system
     292             :   LinearImplicitSystem & pressure_system =
     293      183817 :       libMesh::cast_ref<LinearImplicitSystem &>(_pressure_system.system());
     294             : 
     295             :   // We will need the solution, the right hand side and the matrix
     296             :   NumericVector<Number> & current_local_solution = *(pressure_system.current_local_solution);
     297             :   NumericVector<Number> & solution = *(pressure_system.solution);
     298      183817 :   SparseMatrix<Number> & mmat = *(pressure_system.matrix);
     299      183817 :   NumericVector<Number> & rhs = *(pressure_system.rhs);
     300             : 
     301             :   // Fetch the linear solver from the system
     302             :   PetscLinearSolver<Real> & pressure_solver =
     303      183817 :       libMesh::cast_ref<PetscLinearSolver<Real> &>(*pressure_system.get_linear_solver());
     304             : 
     305      183817 :   _problem.computeLinearSystemSys(pressure_system, mmat, rhs, false);
     306             : 
     307      183817 :   if (_print_fields)
     308             :   {
     309           0 :     _console << "Pressure matrix" << std::endl;
     310           0 :     mmat.print();
     311             :   }
     312             : 
     313             :   // We compute the normalization factors based on the fluxes
     314      183817 :   Real norm_factor = NS::FV::computeNormalizationFactor(solution, mmat, rhs);
     315             : 
     316             :   // We need the non-preconditioned norm to be consistent with the norm factor
     317      183817 :   LibmeshPetscCall(KSPSetNormType(pressure_solver.ksp(), KSP_NORM_UNPRECONDITIONED));
     318             : 
     319             :   // Setting the linear tolerances and maximum iteration counts
     320      183817 :   _pressure_linear_control.real_valued_data["abs_tol"] = _pressure_l_abs_tol * norm_factor;
     321      183817 :   pressure_solver.set_solver_configuration(_pressure_linear_control);
     322             : 
     323      183817 :   if (_pin_pressure)
     324       51119 :     NS::FV::constrainSystem(mmat, rhs, _pressure_pin_value, _pressure_pin_dof);
     325      183817 :   pressure_system.update();
     326             : 
     327      183817 :   auto its_res_pair = pressure_solver.solve(mmat, mmat, solution, rhs);
     328      183817 :   pressure_system.update();
     329             : 
     330      183817 :   if (_print_fields)
     331             :   {
     332           0 :     _console << " rhs when we solve pressure " << std::endl;
     333           0 :     rhs.print();
     334           0 :     _console << " Pressure " << std::endl;
     335           0 :     solution.print();
     336           0 :     _console << "Norm factor " << norm_factor << std::endl;
     337             :   }
     338             : 
     339      183817 :   _pressure_system.setSolution(current_local_solution);
     340             : 
     341             :   const auto residuals =
     342      183817 :       std::make_pair(its_res_pair.first, pressure_solver.get_initial_residual() / norm_factor);
     343             : 
     344      183817 :   _console << " Pressure equation: " << COLOR_GREEN << residuals.second << COLOR_DEFAULT
     345      183817 :            << " Linear its: " << residuals.first << std::endl;
     346             : 
     347      183817 :   return residuals;
     348             : }
     349             : 
     350             : std::pair<unsigned int, Real>
     351        5058 : LinearAssemblySegregatedSolve::solveSolidEnergy()
     352             : {
     353        5058 :   _problem.setCurrentLinearSystem(_solid_energy_sys_number);
     354             : 
     355             :   // We will need some members from the linear system
     356             :   LinearImplicitSystem & system =
     357        5058 :       libMesh::cast_ref<LinearImplicitSystem &>(_solid_energy_system->system());
     358             : 
     359             :   // We will need the solution, the right hand side and the matrix
     360             :   NumericVector<Number> & current_local_solution = *(system.current_local_solution);
     361             :   NumericVector<Number> & solution = *(system.solution);
     362        5058 :   SparseMatrix<Number> & mmat = *(system.matrix);
     363        5058 :   NumericVector<Number> & rhs = *(system.rhs);
     364             : 
     365             :   // Fetch the linear solver from the system
     366             :   PetscLinearSolver<Real> & solver =
     367        5058 :       libMesh::cast_ref<PetscLinearSolver<Real> &>(*system.get_linear_solver());
     368             : 
     369        5058 :   _problem.computeLinearSystemSys(system, mmat, rhs, false);
     370             : 
     371        5058 :   if (_print_fields)
     372             :   {
     373           0 :     _console << "Solid energy matrix" << std::endl;
     374           0 :     mmat.print();
     375             :   }
     376             : 
     377             :   // We compute the normalization factors based on the fluxes
     378        5058 :   Real norm_factor = NS::FV::computeNormalizationFactor(solution, mmat, rhs);
     379             : 
     380             :   // We need the non-preconditioned norm to be consistent with the norm factor
     381        5058 :   LibmeshPetscCall(KSPSetNormType(solver.ksp(), KSP_NORM_UNPRECONDITIONED));
     382             : 
     383             :   // Setting the linear tolerances and maximum iteration counts
     384        5058 :   _solid_energy_linear_control.real_valued_data["abs_tol"] = _solid_energy_l_abs_tol * norm_factor;
     385        5058 :   solver.set_solver_configuration(_solid_energy_linear_control);
     386             : 
     387        5058 :   auto its_res_pair = solver.solve(mmat, mmat, solution, rhs);
     388        5058 :   system.update();
     389             : 
     390        5058 :   if (_print_fields)
     391             :   {
     392           0 :     _console << " rhs when we solve solid energy " << std::endl;
     393           0 :     rhs.print();
     394           0 :     _console << " Solid energy " << std::endl;
     395           0 :     solution.print();
     396           0 :     _console << "Norm factor " << norm_factor << std::endl;
     397             :   }
     398             : 
     399        5058 :   _solid_energy_system->setSolution(current_local_solution);
     400             : 
     401             :   const auto residuals =
     402        5058 :       std::make_pair(its_res_pair.first, solver.get_initial_residual() / norm_factor);
     403             : 
     404        5058 :   _console << " Solid energy equation: " << COLOR_GREEN << residuals.second << COLOR_DEFAULT
     405        5058 :            << " Linear its: " << residuals.first << std::endl;
     406             : 
     407        5058 :   return residuals;
     408             : }
     409             : 
     410             : std::pair<unsigned int, Real>
     411      183817 : LinearAssemblySegregatedSolve::correctVelocity(const bool subtract_updated_pressure,
     412             :                                                const bool recompute_face_mass_flux,
     413             :                                                const SolverParams & solver_params)
     414             : {
     415             :   // Compute the coupling fields between the momentum and pressure equations.
     416             :   // The first argument makes sure the pressure gradient is staged at the first
     417             :   // iteration
     418      183817 :   _rc_uo->computeHbyA(subtract_updated_pressure, _print_fields);
     419             : 
     420             :   // We set the preconditioner/controllable parameters for the pressure equations through
     421             :   // petsc options. Linear tolerances will be overridden within the solver.
     422      183817 :   Moose::PetscSupport::petscSetOptions(_pressure_petsc_options, solver_params);
     423             : 
     424             :   // Solve the pressure corrector
     425      183817 :   const auto residuals = solvePressureCorrector();
     426             : 
     427             :   // Compute the face velocity which is used in the advection terms. In certain
     428             :   // segregated solver algorithms (like PISO) this is only done on the last iteration.
     429      183817 :   if (recompute_face_mass_flux)
     430      162117 :     _rc_uo->computeFaceMassFlux();
     431             : 
     432      183817 :   auto & pressure_current_solution = *(_pressure_system.system().current_local_solution.get());
     433      183817 :   auto & pressure_old_solution = *(_pressure_system.solutionPreviousNewton());
     434             : 
     435             :   // Relax the pressure update for the next momentum predictor
     436      183817 :   NS::FV::relaxSolutionUpdate(
     437      183817 :       pressure_current_solution, pressure_old_solution, _pressure_variable_relaxation);
     438             : 
     439             :   // Overwrite old solution
     440      183817 :   pressure_old_solution = pressure_current_solution;
     441      183817 :   _pressure_system.setSolution(pressure_current_solution);
     442             : 
     443             :   // We recompute the updated pressure gradient
     444      183817 :   _pressure_system.computeGradients();
     445             : 
     446             :   // Reconstruct the cell velocity as well to accelerate convergence
     447      183817 :   _rc_uo->computeCellVelocity();
     448             : 
     449      183817 :   return residuals;
     450             : }
     451             : 
     452             : std::pair<unsigned int, Real>
     453      105108 : LinearAssemblySegregatedSolve::solveAdvectedSystem(const unsigned int system_num,
     454             :                                                    LinearSystem & system,
     455             :                                                    const Real relaxation_factor,
     456             :                                                    SolverConfiguration & solver_config,
     457             :                                                    const Real absolute_tol,
     458             :                                                    const Real field_relaxation,
     459             :                                                    const Real min_value_limiter)
     460             : {
     461      105108 :   _problem.setCurrentLinearSystem(system_num);
     462             : 
     463             :   // We will need some members from the implicit linear system
     464      105108 :   LinearImplicitSystem & li_system = libMesh::cast_ref<LinearImplicitSystem &>(system.system());
     465             : 
     466             :   // We will need the solution, the right hand side and the matrix
     467             :   NumericVector<Number> & current_local_solution = *(li_system.current_local_solution);
     468             :   NumericVector<Number> & solution = *(li_system.solution);
     469      105108 :   SparseMatrix<Number> & mmat = *(li_system.matrix);
     470      105108 :   NumericVector<Number> & rhs = *(li_system.rhs);
     471             : 
     472             :   // We need a vector that stores the (diagonal_relaxed-original_diagonal) vector
     473      105108 :   auto diff_diagonal = solution.zero_clone();
     474             : 
     475             :   // Fetch the linear solver from the system
     476             :   PetscLinearSolver<Real> & linear_solver =
     477      105108 :       libMesh::cast_ref<PetscLinearSolver<Real> &>(*li_system.get_linear_solver());
     478             : 
     479      105108 :   _problem.computeLinearSystemSys(li_system, mmat, rhs, true);
     480             : 
     481             :   // Go and relax the system matrix and the right hand side
     482      105108 :   NS::FV::relaxMatrix(mmat, relaxation_factor, *diff_diagonal);
     483      105108 :   NS::FV::relaxRightHandSide(rhs, solution, *diff_diagonal);
     484             : 
     485      105108 :   if (_print_fields)
     486             :   {
     487           0 :     _console << system.name() << " system matrix" << std::endl;
     488           0 :     mmat.print();
     489             :   }
     490             : 
     491             :   // We compute the normalization factors based on the fluxes
     492      105108 :   Real norm_factor = NS::FV::computeNormalizationFactor(solution, mmat, rhs);
     493             : 
     494             :   // We need the non-preconditioned norm to be consistent with the norm factor
     495      105108 :   LibmeshPetscCall(KSPSetNormType(linear_solver.ksp(), KSP_NORM_UNPRECONDITIONED));
     496             : 
     497             :   // Setting the linear tolerances and maximum iteration counts
     498      105108 :   solver_config.real_valued_data["abs_tol"] = absolute_tol * norm_factor;
     499      105108 :   linear_solver.set_solver_configuration(solver_config);
     500             : 
     501             :   // Solve the system and update current local solution
     502      105108 :   auto its_res_pair = linear_solver.solve(mmat, mmat, solution, rhs);
     503      105108 :   li_system.update();
     504             : 
     505      105108 :   if (_print_fields)
     506             :   {
     507           0 :     _console << " rhs when we solve " << system.name() << std::endl;
     508           0 :     rhs.print();
     509           0 :     _console << system.name() << " solution " << std::endl;
     510           0 :     solution.print();
     511           0 :     _console << " Norm factor " << norm_factor << std::endl;
     512             :   }
     513             : 
     514             :   // Limiting scalar solution
     515      105108 :   if (min_value_limiter != std::numeric_limits<Real>::min())
     516       37298 :     NS::FV::limitSolutionUpdate(current_local_solution, min_value_limiter);
     517             : 
     518             :   // Relax the field update for the next momentum predictor
     519      105108 :   if (field_relaxation != 1.0)
     520             :   {
     521       12082 :     auto & old_local_solution = *(system.solutionPreviousNewton());
     522       12082 :     NS::FV::relaxSolutionUpdate(current_local_solution, old_local_solution, field_relaxation);
     523             : 
     524             :     // Update old solution, only needed if relaxing the field
     525       12082 :     old_local_solution = current_local_solution;
     526             :   }
     527             : 
     528      105108 :   system.setSolution(current_local_solution);
     529             : 
     530             :   const auto residuals =
     531      105108 :       std::make_pair(its_res_pair.first, linear_solver.get_initial_residual() / norm_factor);
     532             : 
     533      105108 :   _console << " Advected system: " << system.name() << " " << COLOR_GREEN << residuals.second
     534      105108 :            << COLOR_DEFAULT << " Linear its: " << residuals.first << std::endl;
     535             : 
     536      105108 :   return residuals;
     537      105108 : }
     538             : 
     539             : bool
     540        1634 : LinearAssemblySegregatedSolve::solve()
     541             : {
     542             :   // Do not solve if problem is set not to
     543        1634 :   if (!_problem.shouldSolve())
     544             :     return true;
     545             : 
     546             :   // Dummy solver parameter file which is needed for switching petsc options
     547        1634 :   SolverParams solver_params;
     548        1634 :   solver_params._type = Moose::SolveType::ST_LINEAR;
     549        1634 :   solver_params._line_search = Moose::LineSearchType::LS_NONE;
     550             : 
     551             :   // Initialize the SIMPLE iteration counter
     552        1634 :   unsigned int simple_iteration_counter = 0;
     553             : 
     554             :   // Assign residuals to general residual vector
     555        1634 :   const unsigned int no_systems = _momentum_systems.size() + 1 + _has_energy_system +
     556        1634 :                                   _has_solid_energy_system + _active_scalar_systems.size() +
     557        1634 :                                   _turbulence_systems.size();
     558             : 
     559        1634 :   std::vector<std::pair<unsigned int, Real>> ns_residuals(no_systems, std::make_pair(0, 1.0));
     560        1634 :   std::vector<Real> ns_abs_tols(_momentum_systems.size(), _momentum_absolute_tolerance);
     561        1634 :   ns_abs_tols.push_back(_pressure_absolute_tolerance);
     562             : 
     563             :   // Push back energy tolerances
     564        1634 :   if (_has_energy_system)
     565         840 :     ns_abs_tols.push_back(_energy_absolute_tolerance);
     566        1634 :   if (_has_solid_energy_system)
     567          18 :     ns_abs_tols.push_back(_solid_energy_absolute_tolerance);
     568        1634 :   if (_has_active_scalar_systems)
     569         338 :     for (const auto scalar_tol : _active_scalar_absolute_tolerance)
     570         169 :       ns_abs_tols.push_back(scalar_tol);
     571             : 
     572             :   // Push back turbulence tolerances
     573        1634 :   if (_has_turbulence_systems)
     574         378 :     for (const auto turbulence_tol : _turbulence_absolute_tolerance)
     575         252 :       ns_abs_tols.push_back(turbulence_tol);
     576             : 
     577             :   bool converged = false;
     578             :   // Loop until converged or hit the maximum allowed iteration number
     579      163751 :   while (simple_iteration_counter < _num_iterations && !converged)
     580             :   {
     581      162117 :     simple_iteration_counter++;
     582             : 
     583             :     // We set the preconditioner/controllable parameters through petsc options. Linear
     584             :     // tolerances will be overridden within the solver. In case of a segregated momentum
     585             :     // solver, we assume that every velocity component uses the same preconditioner
     586      162117 :     Moose::PetscSupport::petscSetOptions(_momentum_petsc_options, solver_params);
     587             : 
     588             :     // Initialize pressure gradients, after this we just reuse the last ones from each
     589             :     // iteration
     590      162117 :     if (simple_iteration_counter == 1)
     591        1634 :       _pressure_system.computeGradients();
     592             : 
     593      162117 :     _console << "Iteration " << simple_iteration_counter << " Initial residual norms:" << std::endl;
     594             : 
     595             :     // Solve the momentum predictor step
     596      162117 :     auto momentum_residual = solveMomentumPredictor();
     597      423538 :     for (const auto system_i : index_range(momentum_residual))
     598             :       ns_residuals[system_i] = momentum_residual[system_i];
     599             : 
     600             :     // Now we correct the velocity, this function depends on the method, it differs for
     601             :     // SIMPLE/PIMPLE, this returns the pressure errors
     602      162117 :     ns_residuals[momentum_residual.size()] = correctVelocity(true, true, solver_params);
     603             : 
     604             :     // If we have an energy equation, solve it here.We assume the material properties in the
     605             :     // Navier-Stokes equations depend on temperature, therefore we can not solve for temperature
     606             :     // outside of the velocity-pressure loop
     607      162117 :     if (_has_energy_system)
     608             :     {
     609             :       // We set the preconditioner/controllable parameters through petsc options. Linear
     610             :       // tolerances will be overridden within the solver.
     611       26223 :       Moose::PetscSupport::petscSetOptions(_energy_petsc_options, solver_params);
     612       26223 :       ns_residuals[momentum_residual.size() + _has_energy_system] =
     613       26223 :           solveAdvectedSystem(_energy_sys_number,
     614       26223 :                               *_energy_system,
     615       26223 :                               _energy_equation_relaxation,
     616             :                               _energy_linear_control,
     617       26223 :                               _energy_l_abs_tol);
     618             :     }
     619      162117 :     if (_has_solid_energy_system)
     620             :     {
     621             :       // We set the preconditioner/controllable parameters through petsc options. Linear
     622             :       // tolerances will be overridden within the solver.
     623        5058 :       Moose::PetscSupport::petscSetOptions(_solid_energy_petsc_options, solver_params);
     624        5058 :       ns_residuals[momentum_residual.size() + _has_solid_energy_system + _has_energy_system] =
     625        5058 :           solveSolidEnergy();
     626             :     }
     627             : 
     628             :     // If we have active scalar equations, solve them here in case they depend on temperature
     629             :     // or they affect the fluid properties such that they must be solved concurrently with pressure
     630             :     // and velocity
     631      162117 :     if (_has_active_scalar_systems)
     632             :     {
     633       24023 :       _problem.execute(EXEC_NONLINEAR);
     634             : 
     635             :       // We set the preconditioner/controllable parameters through petsc options. Linear
     636             :       // tolerances will be overridden within the solver.
     637       24023 :       Moose::PetscSupport::petscSetOptions(_active_scalar_petsc_options, solver_params);
     638       48046 :       for (const auto i : index_range(_active_scalar_system_names))
     639       24023 :         ns_residuals[momentum_residual.size() + 1 + _has_energy_system + _has_solid_energy_system +
     640       24023 :                      i] = solveAdvectedSystem(_active_scalar_system_numbers[i],
     641             :                                               *_active_scalar_systems[i],
     642             :                                               _active_scalar_equation_relaxation[i],
     643             :                                               _active_scalar_linear_control,
     644       24023 :                                               _active_scalar_l_abs_tol);
     645             :     }
     646             : 
     647             :     // If we have turbulence equations, solve them here.
     648             :     // The turbulent viscosity depends on the value of the turbulence surrogate variables
     649      162117 :     if (_has_turbulence_systems)
     650             :     {
     651             :       // We set the preconditioner/controllable parameters through petsc options. Linear
     652             :       // tolerances will be overridden within the solver.
     653       18649 :       Moose::PetscSupport::petscSetOptions(_turbulence_petsc_options, solver_params);
     654       55947 :       for (const auto i : index_range(_turbulence_system_names))
     655             :       {
     656       37298 :         ns_residuals[momentum_residual.size() + 1 + _has_energy_system + _has_solid_energy_system +
     657       37298 :                      _active_scalar_system_names.size() + i] =
     658       37298 :             solveAdvectedSystem(_turbulence_system_numbers[i],
     659             :                                 *_turbulence_systems[i],
     660             :                                 _turbulence_equation_relaxation[i],
     661             :                                 _turbulence_linear_control,
     662       37298 :                                 _turbulence_l_abs_tol,
     663             :                                 _turbulence_field_relaxation[i],
     664             :                                 _turbulence_field_min_limit[i]);
     665             :       }
     666             :     }
     667             : 
     668      162117 :     _problem.execute(EXEC_NONLINEAR);
     669             : 
     670      162117 :     converged = NS::FV::converged(ns_residuals, ns_abs_tols);
     671      162117 :   }
     672             : 
     673             :   // If we have passive scalar equations, solve them here. We assume the material properties in the
     674             :   // Navier-Stokes equations do not depend on passive scalars, as they are passive, therefore we
     675             :   // solve outside of the velocity-pressure loop
     676        1634 :   if (_has_passive_scalar_systems && (converged || _continue_on_max_its))
     677             :   {
     678             :     // The reason why we need more than one iteration is due to the matrix relaxation
     679             :     // which can be used to stabilize the equations
     680             :     bool passive_scalar_converged = false;
     681          98 :     unsigned int ps_iteration_counter = 0;
     682             : 
     683          98 :     _console << "Passive scalar iteration " << ps_iteration_counter
     684          98 :              << " Initial residual norms:" << std::endl;
     685             : 
     686        8880 :     while (ps_iteration_counter < _num_iterations && !passive_scalar_converged)
     687             :     {
     688        8782 :       ps_iteration_counter++;
     689             :       std::vector<std::pair<unsigned int, Real>> scalar_residuals(
     690        8782 :           _passive_scalar_system_names.size(), std::make_pair(0, 1.0));
     691             :       std::vector<Real> scalar_abs_tols;
     692       26346 :       for (const auto scalar_tol : _passive_scalar_absolute_tolerance)
     693       17564 :         scalar_abs_tols.push_back(scalar_tol);
     694             : 
     695             :       // We set the preconditioner/controllable parameters through petsc options. Linear
     696             :       // tolerances will be overridden within the solver.
     697        8782 :       Moose::PetscSupport::petscSetOptions(_passive_scalar_petsc_options, solver_params);
     698       26346 :       for (const auto i : index_range(_passive_scalar_system_names))
     699       17564 :         scalar_residuals[i] = solveAdvectedSystem(_passive_scalar_system_numbers[i],
     700             :                                                   *_passive_scalar_systems[i],
     701             :                                                   _passive_scalar_equation_relaxation[i],
     702             :                                                   _passive_scalar_linear_control,
     703       17564 :                                                   _passive_scalar_l_abs_tol);
     704             : 
     705        8782 :       passive_scalar_converged = NS::FV::converged(scalar_residuals, scalar_abs_tols);
     706        8782 :     }
     707             : 
     708             :     // Both flow and scalars must converge
     709          98 :     converged = passive_scalar_converged && converged;
     710             :   }
     711             : 
     712        1634 :   converged = _continue_on_max_its ? true : converged;
     713             : 
     714             :   return converged;
     715        1634 : }

Generated by: LCOV version 1.14