LCOV - code coverage report
Current view: top level - src/executioners - SIMPLESolveNonlinearAssembly.C (source / functions) Hit Total Coverage
Test: idaholab/moose navier_stokes: 9fc4b0 Lines: 295 334 88.3 %
Date: 2025-08-14 10:14:56 Functions: 10 10 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 "SIMPLESolveNonlinearAssembly.h"
      11             : #include "FEProblem.h"
      12             : #include "SegregatedSolverUtils.h"
      13             : #include "NonlinearSystemBase.h"
      14             : 
      15             : #include "libmesh/nonlinear_implicit_system.h"
      16             : 
      17             : using namespace libMesh;
      18             : 
      19             : InputParameters
      20        1484 : SIMPLESolveNonlinearAssembly::validParams()
      21             : {
      22        1484 :   InputParameters params = SIMPLESolveBase::validParams();
      23             : 
      24        2968 :   params.addParam<TagName>("pressure_gradient_tag",
      25             :                            "pressure_momentum_kernels",
      26             :                            "The name of the tags associated with the kernels in the momentum "
      27             :                            "equations which are not related to the pressure gradient.");
      28             : 
      29        1484 :   return params;
      30           0 : }
      31             : 
      32         742 : SIMPLESolveNonlinearAssembly::SIMPLESolveNonlinearAssembly(Executioner & ex)
      33             :   : SIMPLESolveBase(ex),
      34        1484 :     _pressure_sys_number(_problem.nlSysNum(getParam<SolverSystemName>("pressure_system"))),
      35         742 :     _pressure_system(_problem.getNonlinearSystemBase(_pressure_sys_number)),
      36        1484 :     _has_turbulence_systems(!getParam<std::vector<SolverSystemName>>("turbulence_systems").empty()),
      37        1484 :     _energy_sys_number(_has_energy_system
      38        1106 :                            ? _problem.nlSysNum(getParam<SolverSystemName>("energy_system"))
      39             :                            : libMesh::invalid_uint),
      40         742 :     _energy_system(_has_energy_system ? &_problem.getNonlinearSystemBase(_energy_sys_number)
      41             :                                       : nullptr),
      42         742 :     _solid_energy_sys_number(
      43         742 :         _has_solid_energy_system
      44         760 :             ? _problem.nlSysNum(getParam<SolverSystemName>("solid_energy_system"))
      45             :             : libMesh::invalid_uint),
      46         742 :     _solid_energy_system(_has_solid_energy_system
      47         742 :                              ? &_problem.getNonlinearSystemBase(_solid_energy_sys_number)
      48             :                              : nullptr),
      49        1484 :     _turbulence_system_names(getParam<std::vector<SolverSystemName>>("turbulence_systems")),
      50        1484 :     _turbulence_equation_relaxation(getParam<std::vector<Real>>("turbulence_equation_relaxation")),
      51        1484 :     _turbulence_field_min_limit(getParam<std::vector<Real>>("turbulence_field_min_limit")),
      52        1484 :     _turbulence_l_abs_tol(getParam<Real>("turbulence_l_abs_tol")),
      53        1484 :     _turbulence_absolute_tolerance(getParam<std::vector<Real>>("turbulence_absolute_tolerance")),
      54         742 :     _pressure_tag_name(getParam<TagName>("pressure_gradient_tag")),
      55        2226 :     _pressure_tag_id(_problem.addVectorTag(_pressure_tag_name))
      56             : {
      57             :   // We disable this considering that this object passes petsc options a little differently
      58         742 :   _pressure_system.system().prefix_with_name(false);
      59             : 
      60             :   // We fetch the system numbers for the momentum components plus add vectors
      61             :   // for removing the contribution from the pressure gradient terms.
      62        2283 :   for (auto system_i : index_range(_momentum_system_names))
      63             :   {
      64        1541 :     _momentum_system_numbers.push_back(_problem.nlSysNum(_momentum_system_names[system_i]));
      65        1541 :     _momentum_systems.push_back(
      66        1541 :         &_problem.getNonlinearSystemBase(_momentum_system_numbers[system_i]));
      67        1541 :     _momentum_systems[system_i]->addVector(_pressure_tag_id, false, ParallelType::PARALLEL);
      68             : 
      69             :     // We disable this considering that this object passes petsc options a little differently
      70        1541 :     _momentum_systems[system_i]->system().prefix_with_name(false);
      71             :   }
      72             : 
      73         742 :   if (_has_passive_scalar_systems)
      74         156 :     for (auto system_i : index_range(_passive_scalar_system_names))
      75             :     {
      76         101 :       _passive_scalar_system_numbers.push_back(
      77         101 :           _problem.nlSysNum(_passive_scalar_system_names[system_i]));
      78         101 :       _passive_scalar_systems.push_back(
      79         101 :           &_problem.getNonlinearSystemBase(_passive_scalar_system_numbers[system_i]));
      80             : 
      81             :       // We disable this considering that this object passes petsc options a little differently
      82         101 :       _passive_scalar_systems[system_i]->system().prefix_with_name(false);
      83             :     }
      84             : 
      85         742 :   if (_has_turbulence_systems)
      86             :   {
      87         882 :     for (auto system_i : index_range(_turbulence_system_names))
      88             :     {
      89         588 :       _turbulence_system_numbers.push_back(_problem.nlSysNum(_turbulence_system_names[system_i]));
      90         588 :       _turbulence_systems.push_back(
      91         588 :           &_problem.getNonlinearSystemBase(_turbulence_system_numbers[system_i]));
      92             : 
      93             :       // We disable this considering that this object passes petsc options a little differently
      94         588 :       _turbulence_systems[system_i]->system().prefix_with_name(false);
      95             :     }
      96             : 
      97             :     // We check for input errors with regards to the turbulence equations. At the same time, we
      98             :     // set up the corresponding system numbers
      99         294 :     if (_turbulence_system_names.size() != _turbulence_equation_relaxation.size())
     100           0 :       paramError("turbulence_equation_relaxation",
     101             :                  "The number of equation relaxation parameters does not match the number of "
     102             :                  "turbulence scalar equations!");
     103         294 :     if (_turbulence_system_names.size() != _turbulence_absolute_tolerance.size())
     104           0 :       paramError("turbulence_absolute_tolerance",
     105             :                  "The number of absolute tolerances does not match the number of "
     106             :                  "turbulence equations!");
     107         294 :     if (_turbulence_field_min_limit.empty())
     108             :       // If no minimum bounds are given, initialize to default value 1e-8
     109         294 :       _turbulence_field_min_limit.resize(_turbulence_system_names.size(), 1e-8);
     110           0 :     else if (_turbulence_system_names.size() != _turbulence_field_min_limit.size())
     111           0 :       paramError("turbulence_field_min_limit",
     112             :                  "The number of lower bounds for turbulent quantities does not match the "
     113             :                  "number of turbulence equations!");
     114             :   }
     115             : 
     116        2226 :   if (isParamValid("solid_energy_system") && !_has_energy_system)
     117           0 :     paramError(
     118             :         "solid_energy_system",
     119             :         "We cannot solve a solid energy system without solving for the fluid energy as well!");
     120             : 
     121         742 :   if (_has_turbulence_systems)
     122             :   {
     123         294 :     const auto & turbulence_petsc_options = getParam<MultiMooseEnum>("turbulence_petsc_options");
     124             :     const auto & turbulence_petsc_pair_options = getParam<MooseEnumItem, std::string>(
     125         588 :         "turbulence_petsc_options_iname", "turbulence_petsc_options_value");
     126         294 :     Moose::PetscSupport::addPetscFlagsToPetscOptions(
     127             :         turbulence_petsc_options, "-", *this, _turbulence_petsc_options);
     128         588 :     Moose::PetscSupport::addPetscPairsToPetscOptions(turbulence_petsc_pair_options,
     129         294 :                                                      _problem.mesh().dimension(),
     130             :                                                      "-",
     131             :                                                      *this,
     132             :                                                      _turbulence_petsc_options);
     133             : 
     134         588 :     _turbulence_linear_control.real_valued_data["rel_tol"] = getParam<Real>("turbulence_l_tol");
     135         588 :     _turbulence_linear_control.real_valued_data["abs_tol"] = getParam<Real>("turbulence_l_abs_tol");
     136         294 :     _turbulence_linear_control.int_valued_data["max_its"] =
     137         588 :         getParam<unsigned int>("turbulence_l_max_its");
     138         294 :   }
     139             :   else
     140         896 :     checkDependentParameterError("turbulence_system",
     141             :                                  {"turbulence_petsc_options",
     142             :                                   "turbulence_petsc_options_iname",
     143             :                                   "turbulence_petsc_options_value",
     144             :                                   "turbulence_l_tol",
     145             :                                   "turbulence_l_abs_tol",
     146             :                                   "turbulence_l_max_its",
     147             :                                   "turbulence_equation_relaxation",
     148             :                                   "turbulence_absolute_tolerance"},
     149             :                                  false);
     150         742 : }
     151             : 
     152             : void
     153         742 : SIMPLESolveNonlinearAssembly::linkRhieChowUserObject()
     154             : {
     155             :   // Fetch the segregated rhie-chow object and transfer some information about the momentum
     156             :   // system(s)
     157         742 :   _rc_uo = const_cast<INSFVRhieChowInterpolatorSegregated *>(
     158         742 :       &getUserObject<INSFVRhieChowInterpolatorSegregated>("rhie_chow_user_object"));
     159         742 :   _rc_uo->linkMomentumSystem(_momentum_systems, _momentum_system_numbers, _pressure_tag_id);
     160             : 
     161             :   // Initialize the face velocities in the RC object
     162         742 :   _rc_uo->initFaceVelocities();
     163         742 : }
     164             : 
     165             : std::vector<std::pair<unsigned int, Real>>
     166       43563 : SIMPLESolveNonlinearAssembly::solveMomentumPredictor()
     167             : {
     168             :   // Temporary storage for the (flux-normalized) residuals form
     169             :   // different momentum components
     170             :   std::vector<std::pair<unsigned int, Real>> its_normalized_residuals;
     171             : 
     172             :   // We can create this here with the assumption that every momentum component has the same number
     173             :   // of dofs
     174       43563 :   auto zero_solution = _momentum_systems[0]->system().current_local_solution->zero_clone();
     175             : 
     176             :   // Solve the momentum equations.
     177             :   // TO DO: These equations are VERY similar. If we can store the differences (things coming from
     178             :   // BCs for example) separately, it is enough to construct one matrix.
     179      135771 :   for (const auto system_i : index_range(_momentum_systems))
     180             :   {
     181       92208 :     _problem.setCurrentNonlinearSystem(_momentum_system_numbers[system_i]);
     182             : 
     183             :     // We will need the right hand side and the solution of the next component
     184             :     NonlinearImplicitSystem & momentum_system =
     185       92208 :         libMesh::cast_ref<NonlinearImplicitSystem &>(_momentum_systems[system_i]->system());
     186             : 
     187             :     PetscLinearSolver<Real> & momentum_solver =
     188       92208 :         libMesh::cast_ref<PetscLinearSolver<Real> &>(*momentum_system.get_linear_solver());
     189             : 
     190             :     NumericVector<Number> & solution = *(momentum_system.solution);
     191       92208 :     NumericVector<Number> & rhs = *(momentum_system.rhs);
     192       92208 :     SparseMatrix<Number> & mmat = *(momentum_system.matrix);
     193             : 
     194       92208 :     auto diff_diagonal = solution.zero_clone();
     195             : 
     196             :     // We plug zero in this to get the system matrix and the right hand side of the linear problem
     197       92208 :     _problem.computeResidualAndJacobian(*zero_solution, rhs, mmat);
     198             :     // Sadly, this returns -b so we multiply with -1
     199       92208 :     rhs.scale(-1.0);
     200             : 
     201             :     // Still need to relax the right hand side with the same vector
     202       92208 :     NS::FV::relaxMatrix(mmat, _momentum_equation_relaxation, *diff_diagonal);
     203       92208 :     NS::FV::relaxRightHandSide(rhs, solution, *diff_diagonal);
     204             : 
     205             :     // The normalization factor depends on the right hand side so we need to recompute it for this
     206             :     // component
     207       92208 :     Real norm_factor = NS::FV::computeNormalizationFactor(solution, mmat, rhs);
     208             : 
     209             :     // Very important, for deciding the convergence, we need the unpreconditioned
     210             :     // norms in the linear solve
     211       92208 :     LibmeshPetscCall(KSPSetNormType(momentum_solver.ksp(), KSP_NORM_UNPRECONDITIONED));
     212             :     // Solve this component. We don't update the ghosted solution yet, that will come at the end
     213             :     // of the corrector step. Also setting the linear tolerances and maximum iteration counts.
     214       92208 :     _momentum_linear_control.real_valued_data["abs_tol"] = _momentum_l_abs_tol * norm_factor;
     215       92208 :     momentum_solver.set_solver_configuration(_momentum_linear_control);
     216             : 
     217             :     // We solve the equation
     218       92208 :     auto its_resid_pair = momentum_solver.solve(mmat, mmat, solution, rhs);
     219       92208 :     momentum_system.update();
     220             : 
     221             :     // Save the normalized residual
     222             :     its_normalized_residuals.push_back(
     223       92208 :         std::make_pair(its_resid_pair.first, momentum_solver.get_initial_residual() / norm_factor));
     224             : 
     225       92208 :     if (_print_fields)
     226             :     {
     227           0 :       _console << " matrix when we solve " << std::endl;
     228           0 :       mmat.print();
     229           0 :       _console << " rhs when we solve " << std::endl;
     230           0 :       rhs.print();
     231           0 :       _console << " velocity solution component " << system_i << std::endl;
     232           0 :       solution.print();
     233           0 :       _console << "Norm factor " << norm_factor << std::endl;
     234           0 :       _console << Moose::stringify(momentum_solver.get_initial_residual()) << std::endl;
     235             :     }
     236             : 
     237       92208 :     _momentum_systems[system_i]->setSolution(*(momentum_system.current_local_solution));
     238       92208 :     _momentum_systems[system_i]->copyPreviousNonlinearSolutions();
     239       92208 :   }
     240             : 
     241       43563 :   return its_normalized_residuals;
     242       43563 : }
     243             : 
     244             : std::pair<unsigned int, Real>
     245       43563 : SIMPLESolveNonlinearAssembly::solvePressureCorrector()
     246             : {
     247       43563 :   _problem.setCurrentNonlinearSystem(_pressure_sys_number);
     248             : 
     249             :   // We will need some members from the implicit nonlinear system
     250             :   NonlinearImplicitSystem & pressure_system =
     251       43563 :       libMesh::cast_ref<NonlinearImplicitSystem &>(_pressure_system.system());
     252             : 
     253             :   // We will need the solution, the right hand side and the matrix
     254             :   NumericVector<Number> & current_local_solution = *(pressure_system.current_local_solution);
     255             :   NumericVector<Number> & solution = *(pressure_system.solution);
     256       43563 :   SparseMatrix<Number> & mmat = *(pressure_system.matrix);
     257       43563 :   NumericVector<Number> & rhs = *(pressure_system.rhs);
     258             : 
     259             :   // Fetch the linear solver from the system
     260             :   PetscLinearSolver<Real> & pressure_solver =
     261       43563 :       libMesh::cast_ref<PetscLinearSolver<Real> &>(*pressure_system.get_linear_solver());
     262             : 
     263             :   // We need a zero vector to be able to emulate the Ax=b system by evaluating the
     264             :   // residual and jacobian. Unfortunately, this will leave us with the -b on the right hand side
     265             :   // so we correct it by multiplying it with (-1)
     266       43563 :   auto zero_solution = current_local_solution.zero_clone();
     267       43563 :   _problem.computeResidualAndJacobian(*zero_solution, rhs, mmat);
     268       43563 :   rhs.scale(-1.0);
     269             : 
     270       43563 :   if (_print_fields)
     271             :   {
     272           0 :     _console << "Pressure matrix" << std::endl;
     273           0 :     mmat.print();
     274             :   }
     275             : 
     276             :   // We compute the normalization factors based on the fluxes
     277       43563 :   Real norm_factor = NS::FV::computeNormalizationFactor(solution, mmat, rhs);
     278             : 
     279             :   // We need the non-preconditioned norm to be consistent with the norm factor
     280       43563 :   LibmeshPetscCall(KSPSetNormType(pressure_solver.ksp(), KSP_NORM_UNPRECONDITIONED));
     281             : 
     282             :   // Setting the linear tolerances and maximum iteration counts
     283       43563 :   _pressure_linear_control.real_valued_data["abs_tol"] = _pressure_l_abs_tol * norm_factor;
     284       43563 :   pressure_solver.set_solver_configuration(_pressure_linear_control);
     285             : 
     286       43563 :   if (_pin_pressure)
     287       16646 :     NS::FV::constrainSystem(mmat, rhs, _pressure_pin_value, _pressure_pin_dof);
     288             : 
     289       43563 :   auto its_res_pair = pressure_solver.solve(mmat, mmat, solution, rhs);
     290       43563 :   pressure_system.update();
     291             : 
     292       43563 :   if (_print_fields)
     293             :   {
     294           0 :     _console << " rhs when we solve pressure " << std::endl;
     295           0 :     rhs.print();
     296           0 :     _console << " Pressure " << std::endl;
     297           0 :     solution.print();
     298           0 :     _console << "Norm factor " << norm_factor << std::endl;
     299             :   }
     300             : 
     301       43563 :   _pressure_system.setSolution(current_local_solution);
     302             : 
     303       43563 :   return std::make_pair(its_res_pair.first, pressure_solver.get_initial_residual() / norm_factor);
     304       43563 : }
     305             : 
     306             : std::pair<unsigned int, Real>
     307       35667 : SIMPLESolveNonlinearAssembly::solveAdvectedSystem(const unsigned int system_num,
     308             :                                                   NonlinearSystemBase & system,
     309             :                                                   const Real relaxation_factor,
     310             :                                                   libMesh::SolverConfiguration & solver_config,
     311             :                                                   const Real absolute_tol)
     312             : {
     313       35667 :   _problem.setCurrentNonlinearSystem(system_num);
     314             : 
     315             :   // We will need some members from the implicit nonlinear system
     316             :   NonlinearImplicitSystem & ni_system =
     317       35667 :       libMesh::cast_ref<NonlinearImplicitSystem &>(system.system());
     318             : 
     319             :   // We will need the solution, the right hand side and the matrix
     320             :   NumericVector<Number> & current_local_solution = *(ni_system.current_local_solution);
     321             :   NumericVector<Number> & solution = *(ni_system.solution);
     322       35667 :   SparseMatrix<Number> & mmat = *(ni_system.matrix);
     323       35667 :   NumericVector<Number> & rhs = *(ni_system.rhs);
     324             : 
     325             :   // We need a vector that stores the (diagonal_relaxed-original_diagonal) vector
     326       35667 :   auto diff_diagonal = solution.zero_clone();
     327             : 
     328             :   // Fetch the linear solver from the system
     329             :   PetscLinearSolver<Real> & linear_solver =
     330       35667 :       libMesh::cast_ref<PetscLinearSolver<Real> &>(*ni_system.get_linear_solver());
     331             : 
     332             :   // We need a zero vector to be able to emulate the Ax=b system by evaluating the
     333             :   // residual and jacobian. Unfortunately, this will leave us with the -b on the right hand side
     334             :   // so we correct it by multiplying it with (-1)
     335       35667 :   auto zero_solution = current_local_solution.zero_clone();
     336       35667 :   _problem.computeResidualAndJacobian(*zero_solution, rhs, mmat);
     337       35667 :   rhs.scale(-1.0);
     338             : 
     339             :   // Go and relax the system matrix and the right hand side
     340       35667 :   NS::FV::relaxMatrix(mmat, relaxation_factor, *diff_diagonal);
     341       35667 :   NS::FV::relaxRightHandSide(rhs, solution, *diff_diagonal);
     342             : 
     343       35667 :   if (_print_fields)
     344             :   {
     345           0 :     _console << system.name() << " system matrix" << std::endl;
     346           0 :     mmat.print();
     347           0 :     _console << system.name() << " RHS vector" << std::endl;
     348           0 :     rhs.print();
     349             :   }
     350             : 
     351             :   // We compute the normalization factors based on the fluxes
     352       35667 :   Real norm_factor = NS::FV::computeNormalizationFactor(solution, mmat, rhs);
     353             : 
     354             :   // We need the non-preconditioned norm to be consistent with the norm factor
     355       35667 :   LibmeshPetscCall(KSPSetNormType(linear_solver.ksp(), KSP_NORM_UNPRECONDITIONED));
     356             : 
     357             :   // Setting the linear tolerances and maximum iteration counts
     358       35667 :   solver_config.real_valued_data["abs_tol"] = absolute_tol * norm_factor;
     359       35667 :   linear_solver.set_solver_configuration(solver_config);
     360             : 
     361             :   // Solve the system and update current local solution
     362       35667 :   auto its_res_pair = linear_solver.solve(mmat, mmat, solution, rhs);
     363       35667 :   ni_system.update();
     364             : 
     365       35667 :   if (_print_fields)
     366             :   {
     367           0 :     _console << " rhs when we solve " << system.name() << std::endl;
     368           0 :     rhs.print();
     369           0 :     _console << system.name() << " solution " << std::endl;
     370           0 :     solution.print();
     371           0 :     _console << " Norm factor " << norm_factor << std::endl;
     372             :   }
     373             : 
     374       35667 :   system.setSolution(current_local_solution);
     375             : 
     376       71334 :   return std::make_pair(its_res_pair.first, linear_solver.get_initial_residual() / norm_factor);
     377       35667 : }
     378             : 
     379             : std::pair<unsigned int, Real>
     380         666 : SIMPLESolveNonlinearAssembly::solveSolidEnergySystem()
     381             : {
     382         666 :   _problem.setCurrentNonlinearSystem(_solid_energy_sys_number);
     383             : 
     384             :   // We will need some members from the implicit nonlinear system
     385             :   NonlinearImplicitSystem & se_system =
     386         666 :       libMesh::cast_ref<NonlinearImplicitSystem &>(_solid_energy_system->system());
     387             : 
     388             :   // We will need the solution, the right hand side and the matrix
     389             :   NumericVector<Number> & current_local_solution = *(se_system.current_local_solution);
     390             :   NumericVector<Number> & solution = *(se_system.solution);
     391         666 :   SparseMatrix<Number> & mat = *(se_system.matrix);
     392         666 :   NumericVector<Number> & rhs = *(se_system.rhs);
     393             : 
     394             :   // Fetch the linear solver from the system
     395             :   PetscLinearSolver<Real> & se_solver =
     396         666 :       libMesh::cast_ref<PetscLinearSolver<Real> &>(*se_system.get_linear_solver());
     397             : 
     398             :   // We need a zero vector to be able to emulate the Ax=b system by evaluating the
     399             :   // residual and jacobian. Unfortunately, this will leave us with the -b on the righ hand side
     400             :   // so we correct it by multiplying it with (-1)
     401         666 :   auto zero_solution = current_local_solution.zero_clone();
     402         666 :   _problem.computeResidualAndJacobian(*zero_solution, rhs, mat);
     403         666 :   rhs.scale(-1.0);
     404             : 
     405         666 :   if (_print_fields)
     406             :   {
     407           0 :     _console << "Solid energy matrix" << std::endl;
     408           0 :     mat.print();
     409             :   }
     410             : 
     411             :   // We compute the normalization factors based on the fluxes
     412         666 :   Real norm_factor = NS::FV::computeNormalizationFactor(solution, mat, rhs);
     413             : 
     414             :   // We need the non-preconditioned norm to be consistent with the norm factor
     415         666 :   LibmeshPetscCall(KSPSetNormType(se_solver.ksp(), KSP_NORM_UNPRECONDITIONED));
     416             : 
     417             :   // Setting the linear tolerances and maximum iteration counts
     418         666 :   _solid_energy_linear_control.real_valued_data["abs_tol"] = _solid_energy_l_abs_tol * norm_factor;
     419         666 :   se_solver.set_solver_configuration(_solid_energy_linear_control);
     420             : 
     421         666 :   auto its_res_pair = se_solver.solve(mat, mat, solution, rhs);
     422         666 :   se_system.update();
     423             : 
     424         666 :   if (_print_fields)
     425             :   {
     426           0 :     _console << " Solid energy rhs " << std::endl;
     427           0 :     rhs.print();
     428           0 :     _console << " Solid temperature " << std::endl;
     429           0 :     solution.print();
     430           0 :     _console << "Norm factor " << norm_factor << std::endl;
     431             :   }
     432             : 
     433         666 :   _solid_energy_system->setSolution(current_local_solution);
     434             : 
     435         666 :   return std::make_pair(its_res_pair.first, se_solver.get_initial_residual() / norm_factor);
     436         666 : }
     437             : 
     438             : bool
     439         647 : SIMPLESolveNonlinearAssembly::solve()
     440             : {
     441             :   // Dummy solver parameter file which is needed for switching petsc options
     442         647 :   SolverParams solver_params;
     443         647 :   solver_params._type = Moose::SolveType::ST_LINEAR;
     444         647 :   solver_params._line_search = Moose::LineSearchType::LS_NONE;
     445             : 
     446             :   // Initialize the quantities which matter in terms of the iteration
     447         647 :   unsigned int iteration_counter = 0;
     448             : 
     449             :   // Assign residuals to general residual vector
     450             :   unsigned int no_systems =
     451         647 :       _momentum_systems.size() + 1 + _has_energy_system + _has_solid_energy_system;
     452         647 :   if (_has_turbulence_systems)
     453         219 :     no_systems += _turbulence_systems.size();
     454         647 :   std::vector<std::pair<unsigned int, Real>> ns_its_residuals(no_systems, std::make_pair(0, 1.0));
     455         647 :   std::vector<Real> ns_abs_tols(_momentum_systems.size(), _momentum_absolute_tolerance);
     456         647 :   ns_abs_tols.push_back(_pressure_absolute_tolerance);
     457         647 :   if (_has_energy_system)
     458             :   {
     459         152 :     ns_abs_tols.push_back(_energy_absolute_tolerance);
     460         152 :     if (_has_solid_energy_system)
     461           9 :       ns_abs_tols.push_back(_solid_energy_absolute_tolerance);
     462             :   }
     463         647 :   if (_has_turbulence_systems)
     464         657 :     for (auto system_i : index_range(_turbulence_absolute_tolerance))
     465         438 :       ns_abs_tols.push_back(_turbulence_absolute_tolerance[system_i]);
     466             : 
     467             :   bool converged = false;
     468             :   // Loop until converged or hit the maximum allowed iteration number
     469       44210 :   while (iteration_counter < _num_iterations && !converged)
     470             :   {
     471       43563 :     iteration_counter++;
     472             :     // Resdiual index
     473             :     size_t residual_index = 0;
     474             : 
     475             :     // Execute all objects tagged as nonlinear
     476             :     // This will execute everything in the problem at nonlinear, including the aux kernels.
     477             :     // This way we compute the aux kernels before the momentum equations are solved.
     478       43563 :     _problem.execute(EXEC_NONLINEAR);
     479             : 
     480             :     // We clear the caches in the momentum and pressure variables
     481      135771 :     for (auto system_i : index_range(_momentum_systems))
     482       92208 :       _momentum_systems[system_i]->residualSetup();
     483       43563 :     _pressure_system.residualSetup();
     484             : 
     485             :     // If we solve for energy, we clear the caches there too
     486       43563 :     if (_has_energy_system)
     487             :     {
     488        9463 :       _energy_system->residualSetup();
     489        9463 :       if (_has_solid_energy_system)
     490         666 :         _solid_energy_system->residualSetup();
     491             :     }
     492             : 
     493             :     // If we solve for turbulence, we clear the caches there too
     494       43563 :     if (_has_turbulence_systems)
     495       27495 :       for (auto system_i : index_range(_turbulence_systems))
     496       18330 :         _turbulence_systems[system_i]->residualSetup();
     497             : 
     498             :     // We set the preconditioner/controllable parameters through petsc options. Linear
     499             :     // tolerances will be overridden within the solver. In case of a segregated momentum
     500             :     // solver, we assume that every velocity component uses the same preconditioner
     501       43563 :     Moose::PetscSupport::petscSetOptions(_momentum_petsc_options, solver_params);
     502             : 
     503             :     // Solve the momentum predictor step
     504       43563 :     auto momentum_residual = solveMomentumPredictor();
     505      135771 :     for (const auto system_i : index_range(momentum_residual))
     506             :       ns_its_residuals[system_i] = momentum_residual[system_i];
     507             : 
     508             :     // Compute the coupling fields between the momentum and pressure equations
     509       43563 :     _rc_uo->computeHbyA(_print_fields);
     510             : 
     511             :     // We set the preconditioner/controllable parameters for the pressure equations through
     512             :     // petsc options. Linear tolerances will be overridden within the solver.
     513       43563 :     Moose::PetscSupport::petscSetOptions(_pressure_petsc_options, solver_params);
     514             : 
     515             :     // Solve the pressure corrector
     516       43563 :     ns_its_residuals[momentum_residual.size()] = solvePressureCorrector();
     517             :     // We need this to make sure we evaluate cell gradients for the nonorthogonal correction in
     518             :     // the face velocity update
     519       43563 :     _pressure_system.residualSetup();
     520             : 
     521             :     // Compute the face velocity which is used in the advection terms
     522       43563 :     _rc_uo->computeFaceVelocity();
     523             : 
     524       43563 :     auto & pressure_current_solution = *(_pressure_system.system().current_local_solution.get());
     525       43563 :     auto & pressure_old_solution = *(_pressure_system.solutionPreviousNewton());
     526             :     // Relax the pressure update for the next momentum predictor
     527       43563 :     NS::FV::relaxSolutionUpdate(
     528       43563 :         pressure_current_solution, pressure_old_solution, _pressure_variable_relaxation);
     529             : 
     530             :     // Overwrite old solution
     531       43563 :     pressure_old_solution = pressure_current_solution;
     532       43563 :     _pressure_system.setSolution(pressure_current_solution);
     533             : 
     534             :     // We clear out the caches so that the gradients can be computed with the relaxed solution
     535       43563 :     _pressure_system.residualSetup();
     536             : 
     537             :     // Reconstruct the cell velocity as well to accelerate convergence
     538       43563 :     _rc_uo->computeCellVelocity();
     539             : 
     540             :     // Update residual index
     541             :     residual_index = momentum_residual.size();
     542             : 
     543             :     // If we have an energy equation, solve it here. We assume the material properties in the
     544             :     // Navier-Stokes equations depend on temperature, therefore we can not solve for temperature
     545             :     // outside of the velocity-pressure loop
     546       43563 :     if (_has_energy_system)
     547             :     {
     548             :       // We set the preconditioner/controllable parameters through petsc options. Linear
     549             :       // tolerances will be overridden within the solver.
     550        9463 :       Moose::PetscSupport::petscSetOptions(_energy_petsc_options, solver_params);
     551        9463 :       residual_index += 1;
     552       18926 :       ns_its_residuals[residual_index] = solveAdvectedSystem(_energy_sys_number,
     553        9463 :                                                              *_energy_system,
     554        9463 :                                                              _energy_equation_relaxation,
     555             :                                                              _energy_linear_control,
     556        9463 :                                                              _energy_l_abs_tol);
     557             : 
     558        9463 :       if (_has_solid_energy_system)
     559             :       {
     560             :         // We set the preconditioner/controllable parameters through petsc options. Linear
     561             :         // tolerances will be overridden within the solver.
     562         666 :         Moose::PetscSupport::petscSetOptions(_energy_petsc_options, solver_params);
     563         666 :         residual_index += 1;
     564         666 :         ns_its_residuals[residual_index] = solveSolidEnergySystem();
     565             :       }
     566             :     }
     567             : 
     568             :     // If we have an turbulence equations, we solve it here. We solve it inside the
     569             :     // momentum-pressure loop because it affects the turbulent viscosity
     570       43563 :     if (_has_turbulence_systems)
     571             :     {
     572        9165 :       Moose::PetscSupport::petscSetOptions(_turbulence_petsc_options, solver_params);
     573             : 
     574       27495 :       for (auto system_i : index_range(_turbulence_systems))
     575             :       {
     576       18330 :         residual_index += 1;
     577             :         ns_its_residuals[residual_index] =
     578       36660 :             solveAdvectedSystem(_turbulence_system_numbers[system_i],
     579             :                                 *_turbulence_systems[system_i],
     580             :                                 _turbulence_equation_relaxation[system_i],
     581             :                                 _turbulence_linear_control,
     582       18330 :                                 _turbulence_l_abs_tol);
     583             : 
     584             :         auto & current_solution =
     585       18330 :             *(_turbulence_systems[system_i]->system().current_local_solution.get());
     586       18330 :         NS::FV::limitSolutionUpdate(current_solution, _turbulence_field_min_limit[system_i]);
     587             : 
     588             :         // Relax the turbulence update for the next momentum predictor
     589       18330 :         auto & old_solution = *(_turbulence_systems[system_i]->solutionPreviousNewton());
     590             : 
     591             :         // Relax the pressure update for the next momentum predictor
     592       18330 :         NS::FV::relaxSolutionUpdate(
     593             :             current_solution, old_solution, _turbulence_equation_relaxation[system_i]);
     594             : 
     595             :         // Overwrite old solution
     596       18330 :         old_solution = current_solution;
     597       18330 :         _turbulence_systems[system_i]->setSolution(current_solution);
     598             : 
     599             :         // We clear out the caches so that the gradients can be computed with the relaxed solution
     600       18330 :         _turbulence_systems[system_i]->residualSetup();
     601             :       }
     602             :     }
     603             : 
     604             :     // Printing residuals
     605             :     residual_index = 0;
     606       43563 :     _console << "Iteration " << iteration_counter << " Initial residual norms:" << std::endl;
     607      135771 :     for (auto system_i : index_range(_momentum_systems))
     608       92208 :       _console << " Momentum equation:"
     609             :                << (_momentum_systems.size() > 1
     610      461040 :                        ? std::string(" Component ") + std::to_string(system_i + 1) +
     611      276624 :                              std::string(" ")
     612       92208 :                        : std::string(" "))
     613       92208 :                << COLOR_GREEN << ns_its_residuals[system_i].second << COLOR_DEFAULT << std::endl;
     614       43563 :     _console << " Pressure equation: " << COLOR_GREEN
     615       43563 :              << ns_its_residuals[momentum_residual.size()].second << COLOR_DEFAULT << std::endl;
     616             :     residual_index = momentum_residual.size();
     617             : 
     618       43563 :     if (_has_energy_system)
     619             :     {
     620        9463 :       residual_index += 1;
     621        9463 :       _console << " Energy equation: " << COLOR_GREEN << ns_its_residuals[residual_index].second
     622        9463 :                << COLOR_DEFAULT << std::endl;
     623        9463 :       if (_has_solid_energy_system)
     624             :       {
     625         666 :         residual_index += 1;
     626         666 :         _console << " Solid energy equation: " << COLOR_GREEN
     627         666 :                  << ns_its_residuals[residual_index].second << COLOR_DEFAULT << std::endl;
     628             :       }
     629             :     }
     630             : 
     631       43563 :     if (_has_turbulence_systems)
     632             :     {
     633        9165 :       _console << "Turbulence Iteration " << std::endl;
     634       27495 :       for (auto system_i : index_range(_turbulence_systems))
     635             :       {
     636       18330 :         residual_index += 1;
     637       18330 :         _console << _turbulence_systems[system_i]->name() << " " << COLOR_GREEN
     638       18330 :                  << ns_its_residuals[residual_index].second << COLOR_DEFAULT << std::endl;
     639             :       }
     640             :     }
     641             : 
     642       43563 :     converged = NS::FV::converged(ns_its_residuals, ns_abs_tols);
     643       43563 :   }
     644             : 
     645         647 :   converged = _continue_on_max_its ? true : converged;
     646             : 
     647             :   // Now we solve for the passive scalar equations, they should not influence the solution of the
     648             :   // system above. The reason why we need more than one iteration is due to the matrix relaxation
     649             :   // which can be used to stabilize the equations
     650         647 :   if (_has_passive_scalar_systems)
     651             :   {
     652          50 :     _console << " Passive Scalar Iteration " << iteration_counter << std::endl;
     653             : 
     654             :     // We set the options used by Petsc (preconditioners etc). We assume that every passive
     655             :     // scalar equation uses the same options for now.
     656          50 :     Moose::PetscSupport::petscSetOptions(_passive_scalar_petsc_options, solver_params);
     657             : 
     658          50 :     iteration_counter = 0;
     659             :     std::vector<std::pair<unsigned int, Real>> passive_scalar_residuals(
     660          50 :         _passive_scalar_systems.size(), std::make_pair(0, 1.0));
     661             : 
     662             :     bool passive_scalar_converged =
     663          50 :         NS::FV::converged(passive_scalar_residuals, _passive_scalar_absolute_tolerance);
     664        4185 :     while (iteration_counter < _num_iterations && !passive_scalar_converged)
     665             :     {
     666             :       // We clear the caches in the passive scalar variables
     667       12009 :       for (auto system_i : index_range(_passive_scalar_systems))
     668        7874 :         _passive_scalar_systems[system_i]->residualSetup();
     669             : 
     670        4135 :       iteration_counter++;
     671             : 
     672             :       // Solve the passive scalar equations
     673       12009 :       for (auto system_i : index_range(_passive_scalar_systems))
     674             :         passive_scalar_residuals[system_i] =
     675        7874 :             solveAdvectedSystem(_passive_scalar_system_numbers[system_i],
     676             :                                 *_passive_scalar_systems[system_i],
     677             :                                 _passive_scalar_equation_relaxation[system_i],
     678             :                                 _passive_scalar_linear_control,
     679        7874 :                                 _passive_scalar_l_abs_tol);
     680             : 
     681        4135 :       _console << "Iteration " << iteration_counter << " Initial residual norms:" << std::endl;
     682       12009 :       for (auto system_i : index_range(_passive_scalar_systems))
     683        7874 :         _console << _passive_scalar_systems[system_i]->name() << " " << COLOR_GREEN
     684        7874 :                  << passive_scalar_residuals[system_i].second << COLOR_DEFAULT << std::endl;
     685             : 
     686             :       passive_scalar_converged =
     687        4135 :           NS::FV::converged(passive_scalar_residuals, _passive_scalar_absolute_tolerance);
     688             :     }
     689             : 
     690          50 :     converged = _continue_on_max_its ? true : passive_scalar_converged;
     691          50 :   }
     692             : 
     693         647 :   return converged;
     694         647 : }
     695             : 
     696             : void
     697         742 : SIMPLESolveNonlinearAssembly::checkIntegrity()
     698             : {
     699             :   // check to make sure that we don't have any time kernels in this simulation (Steady State)
     700        2283 :   for (const auto system : _momentum_systems)
     701        1541 :     checkTimeKernels(*system);
     702             : 
     703         742 :   checkTimeKernels(_pressure_system);
     704             : 
     705         742 :   if (_has_energy_system)
     706             :   {
     707         182 :     checkTimeKernels(*_energy_system);
     708         182 :     if (_has_solid_energy_system)
     709           9 :       checkTimeKernels(*_solid_energy_system);
     710             :   }
     711             : 
     712         742 :   if (_has_passive_scalar_systems)
     713         156 :     for (const auto system : _passive_scalar_systems)
     714         101 :       checkTimeKernels(*system);
     715             : 
     716         742 :   if (_has_turbulence_systems)
     717         882 :     for (const auto system : _turbulence_systems)
     718         588 :       checkTimeKernels(*system);
     719         742 : }
     720             : 
     721             : void
     722        3163 : SIMPLESolveNonlinearAssembly::checkTimeKernels(NonlinearSystemBase & system)
     723             : {
     724             :   // check to make sure that we don't have any time kernels in this simulation (Steady State)
     725        3163 :   if (system.containsTimeKernel())
     726           0 :     mooseError("You have specified time kernels in your steady state simulation in system",
     727           0 :                system.name());
     728        3163 : }

Generated by: LCOV version 1.14