LCOV - code coverage report
Current view: top level - src/systems - NonlinearSystem.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 164 171 95.9 %
Date: 2026-05-29 20:35:17 Functions: 21 22 95.5 %
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             : // moose includes
      11             : #include "NonlinearSystem.h"
      12             : #include "FEProblem.h"
      13             : #include "DisplacedProblem.h"
      14             : #include "TimeIntegrator.h"
      15             : #include "FiniteDifferencePreconditioner.h"
      16             : #include "PetscSupport.h"
      17             : #include "ComputeResidualFunctor.h"
      18             : #include "ComputeFDResidualFunctor.h"
      19             : #include "MooseVariableScalar.h"
      20             : #include "MooseTypes.h"
      21             : #include "AuxiliarySystem.h"
      22             : #include "Console.h"
      23             : 
      24             : #include "libmesh/nonlinear_solver.h"
      25             : #include "libmesh/petsc_nonlinear_solver.h"
      26             : #include "libmesh/sparse_matrix.h"
      27             : #include "libmesh/petsc_matrix.h"
      28             : #include "libmesh/diagonal_matrix.h"
      29             : #include "libmesh/default_coupling.h"
      30             : #include "libmesh/petsc_solver_exception.h"
      31             : 
      32             : using namespace libMesh;
      33             : 
      34             : namespace Moose
      35             : {
      36             : void
      37      470651 : compute_jacobian(const NumericVector<Number> & soln,
      38             :                  SparseMatrix<Number> & jacobian,
      39             :                  NonlinearImplicitSystem & sys)
      40             : {
      41             :   FEProblemBase * p =
      42      470651 :       sys.get_equation_systems().parameters.get<FEProblemBase *>("_fe_problem_base");
      43      470651 :   p->computeJacobianSys(sys, soln, jacobian);
      44      470638 : }
      45             : 
      46             : void
      47         714 : compute_bounds(NumericVector<Number> & lower,
      48             :                NumericVector<Number> & upper,
      49             :                NonlinearImplicitSystem & sys)
      50             : {
      51             :   FEProblemBase * p =
      52         714 :       sys.get_equation_systems().parameters.get<FEProblemBase *>("_fe_problem_base");
      53         714 :   p->computeBounds(sys, lower, upper);
      54         714 : }
      55             : 
      56             : void
      57      294844 : compute_nullspace(std::vector<NumericVector<Number> *> & sp, NonlinearImplicitSystem & sys)
      58             : {
      59             :   FEProblemBase * p =
      60      294844 :       sys.get_equation_systems().parameters.get<FEProblemBase *>("_fe_problem_base");
      61      294844 :   p->computeNullSpace(sys, sp);
      62      294844 : }
      63             : 
      64             : void
      65      294844 : compute_transpose_nullspace(std::vector<NumericVector<Number> *> & sp,
      66             :                             NonlinearImplicitSystem & sys)
      67             : {
      68             :   FEProblemBase * p =
      69      294844 :       sys.get_equation_systems().parameters.get<FEProblemBase *>("_fe_problem_base");
      70      294844 :   p->computeTransposeNullSpace(sys, sp);
      71      294844 : }
      72             : 
      73             : void
      74      294844 : compute_nearnullspace(std::vector<NumericVector<Number> *> & sp, NonlinearImplicitSystem & sys)
      75             : {
      76             :   FEProblemBase * p =
      77      294844 :       sys.get_equation_systems().parameters.get<FEProblemBase *>("_fe_problem_base");
      78      294844 :   p->computeNearNullSpace(sys, sp);
      79      294844 : }
      80             : 
      81             : void
      82        2128 : compute_postcheck(const NumericVector<Number> & old_soln,
      83             :                   NumericVector<Number> & search_direction,
      84             :                   NumericVector<Number> & new_soln,
      85             :                   bool & changed_search_direction,
      86             :                   bool & changed_new_soln,
      87             :                   NonlinearImplicitSystem & sys)
      88             : {
      89             :   FEProblemBase * p =
      90        2128 :       sys.get_equation_systems().parameters.get<FEProblemBase *>("_fe_problem_base");
      91        2128 :   p->computePostCheck(
      92             :       sys, old_soln, search_direction, new_soln, changed_search_direction, changed_new_soln);
      93        2128 : }
      94             : } // namespace Moose
      95             : 
      96       60299 : NonlinearSystem::NonlinearSystem(FEProblemBase & fe_problem, const std::string & name)
      97             :   : NonlinearSystemBase(
      98       60299 :         fe_problem, fe_problem.es().add_system<NonlinearImplicitSystem>(name), name),
      99       60299 :     _nl_implicit_sys(fe_problem.es().get_system<NonlinearImplicitSystem>(name)),
     100       60299 :     _nl_residual_functor(_fe_problem),
     101       60299 :     _fd_residual_functor(_fe_problem),
     102       60299 :     _resid_and_jac_functor(_fe_problem),
     103      120598 :     _use_coloring_finite_difference(false)
     104             : {
     105       60299 :   nonlinearSolver()->residual_object = &_nl_residual_functor;
     106       60299 :   nonlinearSolver()->jacobian = Moose::compute_jacobian;
     107       60299 :   nonlinearSolver()->bounds = Moose::compute_bounds;
     108       60299 :   nonlinearSolver()->nullspace = Moose::compute_nullspace;
     109       60299 :   nonlinearSolver()->transpose_nullspace = Moose::compute_transpose_nullspace;
     110       60299 :   nonlinearSolver()->nearnullspace = Moose::compute_nearnullspace;
     111             : 
     112             :   PetscNonlinearSolver<Real> * petsc_solver =
     113       60299 :       static_cast<PetscNonlinearSolver<Real> *>(_nl_implicit_sys.nonlinear_solver.get());
     114       60299 :   if (petsc_solver)
     115             :   {
     116       60299 :     petsc_solver->set_residual_zero_out(false);
     117       60299 :     petsc_solver->set_jacobian_zero_out(false);
     118       60299 :     petsc_solver->use_default_monitor(false);
     119             :   }
     120       60299 : }
     121             : 
     122       57212 : NonlinearSystem::~NonlinearSystem() {}
     123             : 
     124             : void
     125      292764 : NonlinearSystem::potentiallySetupFiniteDifferencing()
     126             : {
     127      292764 :   if (_use_finite_differenced_preconditioner)
     128             :   {
     129         182 :     _nl_implicit_sys.nonlinear_solver->fd_residual_object = &_fd_residual_functor;
     130         182 :     setupFiniteDifferencedPreconditioner();
     131             :   }
     132             : 
     133             :   PetscNonlinearSolver<Real> & solver =
     134      292764 :       static_cast<PetscNonlinearSolver<Real> &>(*_nl_implicit_sys.nonlinear_solver);
     135      292764 :   solver.mffd_residual_object = &_fd_residual_functor;
     136             : 
     137      292764 :   solver.set_snesmf_reuse_base(_fe_problem.useSNESMFReuseBase());
     138      292764 : }
     139             : 
     140             : void
     141      287715 : NonlinearSystem::solve()
     142             : {
     143             :   // Only attach the postcheck function to the solver if we actually
     144             :   // have dampers or if the FEProblemBase needs to update the solution,
     145             :   // which is also done during the linesearch postcheck.  It doesn't
     146             :   // hurt to do this multiple times, it is just setting a pointer.
     147      575027 :   if (_fe_problem.hasDampers() || _fe_problem.shouldUpdateSolution() ||
     148      287312 :       _fe_problem.needsPreviousNewtonIteration())
     149         502 :     _nl_implicit_sys.nonlinear_solver->postcheck = Moose::compute_postcheck;
     150             : 
     151      287715 :   if (shouldEvaluatePreSMOResidual())
     152             :   {
     153         415 :     TIME_SECTION("nlPreSMOResidual", 3, "Computing Pre-SMO Residual");
     154             :     // Calculate the pre-SMO residual for use in the convergence criterion.
     155          83 :     _computing_pre_smo_residual = true;
     156          83 :     _fe_problem.computeResidualSys(_nl_implicit_sys, *_current_solution, *_nl_implicit_sys.rhs);
     157          83 :     _computing_pre_smo_residual = false;
     158          83 :     _nl_implicit_sys.rhs->close();
     159          83 :     _pre_smo_residual = _nl_implicit_sys.rhs->l2_norm();
     160          83 :     _console << " * Nonlinear |R| = "
     161         166 :              << Console::outputNorm(std::numeric_limits<Real>::max(), _pre_smo_residual)
     162          83 :              << " (Before preset BCs, predictors, correctors, and constraints)\n";
     163          83 :     _console << std::flush;
     164          83 :   }
     165             : 
     166      287715 :   const bool presolve_succeeded = preSolve();
     167      287715 :   if (!presolve_succeeded)
     168           0 :     return;
     169             : 
     170      287715 :   potentiallySetupFiniteDifferencing();
     171             : 
     172      287715 :   const bool time_integrator_solve = std::any_of(_time_integrators.begin(),
     173             :                                                  _time_integrators.end(),
     174      257955 :                                                  [](auto & ti) { return ti->overridesSolve(); });
     175             :   if (time_integrator_solve)
     176             :     mooseAssert(_time_integrators.size() == 1,
     177             :                 "If solve is overridden, then there must be only one time integrator");
     178             : 
     179      287715 :   if (time_integrator_solve)
     180       11099 :     _time_integrators.front()->solve();
     181             :   else
     182      276616 :     system().solve();
     183             : 
     184      545604 :   for (auto & ti : _time_integrators)
     185             :   {
     186      257941 :     if (!ti->overridesSolve())
     187      246842 :       ti->setNumIterationsLastSolve();
     188      257941 :     ti->postSolve();
     189             :   }
     190             : 
     191      287663 :   if (!_time_integrators.empty())
     192             :   {
     193      257601 :     _n_iters = _time_integrators.front()->getNumNonlinearIterations();
     194      257601 :     _n_linear_iters = _time_integrators.front()->getNumLinearIterations();
     195             :   }
     196             :   else
     197             :   {
     198       30062 :     _n_iters = _nl_implicit_sys.n_nonlinear_iterations();
     199       30062 :     _n_linear_iters = _nl_implicit_sys.nonlinear_solver->get_total_linear_iterations();
     200             :   }
     201             : 
     202             :   // store info about the solve
     203      287663 :   _final_residual = _nl_implicit_sys.final_nonlinear_residual();
     204             : 
     205             :   // determine whether solution invalid occurs in the converged solution
     206      287663 :   checkInvalidSolution();
     207             : 
     208      287660 :   if (_use_coloring_finite_difference)
     209          63 :     LibmeshPetscCall(MatFDColoringDestroy(&_fdcoloring));
     210             : }
     211             : 
     212             : void
     213         304 : NonlinearSystem::stopSolve(const ExecFlagType & exec_flag,
     214             :                            const std::set<TagID> & vector_tags_to_close)
     215             : {
     216             :   PetscNonlinearSolver<Real> & solver =
     217         304 :       static_cast<PetscNonlinearSolver<Real> &>(*sys().nonlinear_solver);
     218             : 
     219         304 :   if (exec_flag == EXEC_LINEAR || exec_flag == EXEC_POSTCHECK)
     220             :   {
     221         241 :     LibmeshPetscCall(SNESSetFunctionDomainError(solver.snes()));
     222             : 
     223             :     // Clean up by getting vectors into a valid state for a
     224             :     // (possible) subsequent solve.
     225         241 :     closeTaggedVectors(vector_tags_to_close);
     226             :   }
     227          63 :   else if (exec_flag == EXEC_NONLINEAR)
     228          63 :     LibmeshPetscCall(SNESSetJacobianDomainError(solver.snes()));
     229             :   else
     230           0 :     mooseError("Unsupported execute flag: ", Moose::stringify(exec_flag));
     231         304 : }
     232             : 
     233             : void
     234         182 : NonlinearSystem::setupFiniteDifferencedPreconditioner()
     235             : {
     236             :   std::shared_ptr<FiniteDifferencePreconditioner> fdp =
     237         182 :       std::dynamic_pointer_cast<FiniteDifferencePreconditioner>(_preconditioner);
     238         182 :   if (!fdp)
     239           0 :     mooseError("Did not setup finite difference preconditioner, and please add a preconditioning "
     240             :                "block with type = fdp");
     241             : 
     242         182 :   if (fdp->finiteDifferenceType() == "coloring")
     243             :   {
     244         171 :     _use_coloring_finite_difference = true;
     245         171 :     setupColoringFiniteDifferencedPreconditioner();
     246             :   }
     247             : 
     248          11 :   else if (fdp->finiteDifferenceType() == "standard")
     249             :   {
     250          11 :     _use_coloring_finite_difference = false;
     251          11 :     setupStandardFiniteDifferencedPreconditioner();
     252             :   }
     253             :   else
     254           0 :     mooseError("Unknown finite difference type");
     255         182 : }
     256             : 
     257             : void
     258          11 : NonlinearSystem::setupStandardFiniteDifferencedPreconditioner()
     259             : {
     260             :   // Make sure that libMesh isn't going to override our preconditioner
     261          11 :   _nl_implicit_sys.nonlinear_solver->jacobian = nullptr;
     262             : 
     263             :   PetscNonlinearSolver<Number> * petsc_nonlinear_solver =
     264          11 :       static_cast<PetscNonlinearSolver<Number> *>(_nl_implicit_sys.nonlinear_solver.get());
     265             : 
     266             :   PetscMatrix<Number> * petsc_mat =
     267          11 :       static_cast<PetscMatrix<Number> *>(&_nl_implicit_sys.get_system_matrix());
     268             : 
     269          11 :   LibmeshPetscCall(SNESSetJacobian(petsc_nonlinear_solver->snes(),
     270             :                                    petsc_mat->mat(),
     271             :                                    petsc_mat->mat(),
     272             :                                    SNESComputeJacobianDefault,
     273             :                                    nullptr));
     274          11 : }
     275             : 
     276             : void
     277         171 : NonlinearSystem::setupColoringFiniteDifferencedPreconditioner()
     278             : {
     279             :   // Make sure that libMesh isn't going to override our preconditioner
     280         171 :   _nl_implicit_sys.nonlinear_solver->jacobian = nullptr;
     281             : 
     282             :   PetscNonlinearSolver<Number> & petsc_nonlinear_solver =
     283         171 :       dynamic_cast<PetscNonlinearSolver<Number> &>(*_nl_implicit_sys.nonlinear_solver);
     284             : 
     285             :   // Pointer to underlying PetscMatrix type
     286             :   PetscMatrix<Number> * petsc_mat =
     287         171 :       dynamic_cast<PetscMatrix<Number> *>(&_nl_implicit_sys.get_system_matrix());
     288             : 
     289         171 :   Moose::compute_jacobian(*_nl_implicit_sys.current_local_solution, *petsc_mat, _nl_implicit_sys);
     290             : 
     291         171 :   if (!petsc_mat)
     292           0 :     mooseError("Could not convert to Petsc matrix.");
     293             : 
     294         171 :   petsc_mat->close();
     295             : 
     296             :   ISColoring iscoloring;
     297             : 
     298             :   // PETSc 3.5.x
     299             :   MatColoring matcoloring;
     300         171 :   LibmeshPetscCallA(_communicator.get(), MatColoringCreate(petsc_mat->mat(), &matcoloring));
     301         171 :   LibmeshPetscCallA(_communicator.get(), MatColoringSetType(matcoloring, MATCOLORINGLF));
     302         171 :   LibmeshPetscCallA(_communicator.get(), MatColoringSetFromOptions(matcoloring));
     303         171 :   LibmeshPetscCallA(_communicator.get(), MatColoringApply(matcoloring, &iscoloring));
     304         171 :   LibmeshPetscCallA(_communicator.get(), MatColoringDestroy(&matcoloring));
     305             : 
     306         171 :   LibmeshPetscCallA(_communicator.get(),
     307             :                     MatFDColoringCreate(petsc_mat->mat(), iscoloring, &_fdcoloring));
     308         171 :   LibmeshPetscCallA(_communicator.get(), MatFDColoringSetFromOptions(_fdcoloring));
     309             :   // clang-format off
     310             : #if PETSC_VERSION_LESS_THAN(3, 24, 0)
     311             :   LibmeshPetscCallA(_communicator.get(),
     312             :                     MatFDColoringSetFunction(_fdcoloring,
     313             :                                              (PetscErrorCode(*)(void))(void (*)(void))
     314             :                                              &libMesh::libmesh_petsc_snes_fd_residual,
     315             :                                              &petsc_nonlinear_solver));
     316             : #else
     317         171 :   LibmeshPetscCallA(_communicator.get(),
     318             :                     MatFDColoringSetFunction(_fdcoloring,
     319             :                                              (MatFDColoringFn*)
     320             :                                              &libMesh::libmesh_petsc_snes_fd_residual,
     321             :                                              &petsc_nonlinear_solver));
     322             : #endif
     323             :   // clang-format on
     324         171 :   LibmeshPetscCallA(_communicator.get(),
     325             :                     MatFDColoringSetUp(petsc_mat->mat(), iscoloring, _fdcoloring));
     326         171 :   LibmeshPetscCallA(_communicator.get(),
     327             :                     SNESSetJacobian(petsc_nonlinear_solver.snes(),
     328             :                                     petsc_mat->mat(),
     329             :                                     petsc_mat->mat(),
     330             :                                     SNESComputeJacobianDefaultColor,
     331             :                                     _fdcoloring));
     332             :   // PETSc >=3.3.0
     333         171 :   LibmeshPetscCallA(_communicator.get(), ISColoringDestroy(&iscoloring));
     334         171 : }
     335             : 
     336             : bool
     337      308801 : NonlinearSystem::converged()
     338             : {
     339      308801 :   if (_fe_problem.hasException() || _fe_problem.getFailNextNonlinearConvergenceCheck())
     340         172 :     return false;
     341             :   // When not computing the residual (for example at the beginning of a time step),
     342             :   // we may be in the process of counting invalid solution warnings, so the call to
     343             :   // acceptInvalidSolution() would fail due to lack of parallel synchronization
     344             :   // TODO: think of a better solution
     345      308629 :   if (_app.solutionInvalidity().hasSynced() && !_fe_problem.acceptInvalidSolution())
     346             :   {
     347         462 :     mooseWarning("The solution is not converged due to the solution being invalid.");
     348         459 :     return false;
     349             :   }
     350      308167 :   return _nl_implicit_sys.nonlinear_solver->converged;
     351             : }
     352             : 
     353             : void
     354         107 : NonlinearSystem::attachPreconditioner(Preconditioner<Number> * preconditioner)
     355             : {
     356         107 :   nonlinearSolver()->attach_preconditioner(preconditioner);
     357         107 : }
     358             : 
     359             : void
     360         474 : NonlinearSystem::computeScalingJacobian()
     361             : {
     362         474 :   _fe_problem.computeJacobianSys(_nl_implicit_sys, *_current_solution, *_scaling_matrix);
     363         474 : }
     364             : 
     365             : void
     366          36 : NonlinearSystem::computeScalingResidual()
     367             : {
     368          36 :   _fe_problem.computeResidualSys(_nl_implicit_sys, *_current_solution, RHS());
     369          36 : }
     370             : 
     371             : SNES
     372     1084358 : NonlinearSystem::getSNES()
     373             : {
     374             :   PetscNonlinearSolver<Number> * petsc_solver =
     375     1084358 :       dynamic_cast<PetscNonlinearSolver<Number> *>(nonlinearSolver());
     376             : 
     377     1084358 :   if (petsc_solver)
     378             :   {
     379     1084358 :     const char * snes_prefix = nullptr;
     380     1084358 :     std::string snes_prefix_str;
     381     1084358 :     if (system().prefix_with_name())
     382             :     {
     383       39244 :       snes_prefix_str = system().prefix();
     384       39244 :       snes_prefix = snes_prefix_str.c_str();
     385             :     }
     386     2168716 :     return petsc_solver->snes(snes_prefix);
     387     1084358 :   }
     388             :   else
     389           0 :     mooseError("It is not a petsc nonlinear solver");
     390             : }
     391             : 
     392             : void
     393         460 : NonlinearSystem::residualAndJacobianTogether()
     394             : {
     395         460 :   if (_fe_problem.solverParams(number())._type == Moose::ST_JFNK)
     396           0 :     mooseError(
     397             :         "Evaluting the residual and Jacobian together does not make sense for a JFNK solve type in "
     398             :         "which only function evaluations are required, e.g. there is no need to form a matrix");
     399             : 
     400         460 :   nonlinearSolver()->residual_object = nullptr;
     401         460 :   nonlinearSolver()->jacobian = nullptr;
     402         460 :   nonlinearSolver()->residual_and_jacobian_object = &_resid_and_jac_functor;
     403         460 : }

Generated by: LCOV version 1.14