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

Generated by: LCOV version 1.14