LCOV - code coverage report
Current view: top level - src/convergence - DefaultNonlinearConvergence.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 99 111 89.2 %
Date: 2025-07-17 01:28:37 Functions: 4 4 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             : // MOOSE includes
      11             : #include "DefaultNonlinearConvergence.h"
      12             : #include "FEProblemBase.h"
      13             : #include "PetscSupport.h"
      14             : #include "NonlinearSystemBase.h"
      15             : 
      16             : #include "libmesh/equation_systems.h"
      17             : 
      18             : // PETSc includes
      19             : #include <petsc.h>
      20             : #include <petscmat.h>
      21             : #include <petscsnes.h>
      22             : 
      23             : registerMooseObject("MooseApp", DefaultNonlinearConvergence);
      24             : 
      25             : InputParameters
      26      148237 : DefaultNonlinearConvergence::validParams()
      27             : {
      28      148237 :   InputParameters params = DefaultConvergenceBase::validParams();
      29      148237 :   params += FEProblemSolve::feProblemDefaultConvergenceParams();
      30             : 
      31      148237 :   params.addPrivateParam<bool>("added_as_default", false);
      32             : 
      33      148237 :   params.addClassDescription("Default convergence criteria for FEProblem.");
      34             : 
      35      148237 :   return params;
      36           0 : }
      37             : 
      38       62398 : DefaultNonlinearConvergence::DefaultNonlinearConvergence(const InputParameters & parameters)
      39             :   : DefaultConvergenceBase(parameters),
      40       62398 :     _fe_problem(*getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")),
      41       62398 :     _nl_abs_div_tol(getSharedExecutionerParam<Real>("nl_abs_div_tol")),
      42       62398 :     _nl_rel_div_tol(getSharedExecutionerParam<Real>("nl_div_tol")),
      43       62398 :     _div_threshold(std::numeric_limits<Real>::max()),
      44       62398 :     _nl_forced_its(getSharedExecutionerParam<unsigned int>("nl_forced_its")),
      45       62398 :     _nl_max_pingpong(getSharedExecutionerParam<unsigned int>("n_max_nonlinear_pingpong")),
      46       62398 :     _nl_current_pingpong(0)
      47             : {
      48       62398 :   EquationSystems & es = _fe_problem.es();
      49             : 
      50       62398 :   es.parameters.set<unsigned int>("nonlinear solver maximum iterations") =
      51      124796 :       getSharedExecutionerParam<unsigned int>("nl_max_its");
      52       62398 :   es.parameters.set<unsigned int>("nonlinear solver maximum function evaluations") =
      53      124796 :       getSharedExecutionerParam<unsigned int>("nl_max_funcs");
      54       62398 :   es.parameters.set<Real>("nonlinear solver absolute residual tolerance") =
      55      124796 :       getSharedExecutionerParam<Real>("nl_abs_tol");
      56       62398 :   es.parameters.set<Real>("nonlinear solver relative residual tolerance") =
      57      124796 :       getSharedExecutionerParam<Real>("nl_rel_tol");
      58       62398 :   es.parameters.set<Real>("nonlinear solver divergence tolerance") =
      59      124796 :       getSharedExecutionerParam<Real>("nl_div_tol");
      60       62398 :   es.parameters.set<Real>("nonlinear solver absolute step tolerance") =
      61      124796 :       getSharedExecutionerParam<Real>("nl_abs_step_tol");
      62       62398 :   es.parameters.set<Real>("nonlinear solver relative step tolerance") =
      63      124796 :       getSharedExecutionerParam<Real>("nl_rel_step_tol");
      64       62398 : }
      65             : 
      66             : bool
      67      334088 : DefaultNonlinearConvergence::checkRelativeConvergence(const unsigned int /*it*/,
      68             :                                                       const Real fnorm,
      69             :                                                       const Real ref_norm,
      70             :                                                       const Real rel_tol,
      71             :                                                       const Real /*abs_tol*/,
      72             :                                                       std::ostringstream & oss)
      73             : {
      74      334088 :   if (fnorm <= ref_norm * rel_tol)
      75             :   {
      76      137672 :     oss << "Converged due to relative/normalized residual norm " << fnorm / ref_norm
      77      137672 :         << " < relative tolerance (" << rel_tol << ")\n";
      78      137672 :     return true;
      79             :   }
      80             :   else
      81      196416 :     return false;
      82             : }
      83             : 
      84             : Convergence::MooseConvergenceStatus
      85      746723 : DefaultNonlinearConvergence::checkConvergence(unsigned int iter)
      86             : {
      87      746723 :   TIME_SECTION(_perfid_check_convergence);
      88             : 
      89      746723 :   NonlinearSystemBase & system = _fe_problem.currentNonlinearSystem();
      90      746723 :   MooseConvergenceStatus status = MooseConvergenceStatus::ITERATING;
      91             : 
      92             :   // Needed by ResidualReferenceConvergence
      93      746723 :   nonlinearConvergenceSetup();
      94             : 
      95      746723 :   SNES snes = system.getSNES();
      96             : 
      97             :   // ||u||
      98             :   PetscReal xnorm;
      99      746723 :   LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetSolutionNorm(snes, &xnorm));
     100             : 
     101             :   // ||r||
     102             :   PetscReal fnorm;
     103      746723 :   LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetFunctionNorm(snes, &fnorm));
     104             : 
     105             :   // ||du||
     106             :   PetscReal snorm;
     107      746723 :   LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetUpdateNorm(snes, &snorm));
     108             : 
     109             :   // Get current number of function evaluations done by SNES
     110             :   PetscInt nfuncs;
     111      746723 :   LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetNumberFunctionEvals(snes, &nfuncs));
     112             : 
     113             :   // Get tolerances from SNES
     114             :   PetscReal abs_tol, rel_tol, rel_step_tol;
     115             :   PetscInt max_its, max_funcs;
     116      746723 :   LibmeshPetscCallA(
     117             :       _fe_problem.comm().get(),
     118             :       SNESGetTolerances(snes, &abs_tol, &rel_tol, &rel_step_tol, &max_its, &max_funcs));
     119             : 
     120             : #if !PETSC_VERSION_LESS_THAN(3, 8, 4)
     121      746723 :   PetscBool force_iteration = PETSC_FALSE;
     122      746723 :   LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetForceIteration(snes, &force_iteration));
     123             : 
     124             :   // if PETSc says to force iteration, then force at least one iteration
     125      746723 :   if (force_iteration && !(_nl_forced_its))
     126         671 :     _nl_forced_its = 1;
     127             : 
     128             :   // if specified here to force iteration, but PETSc doesn't know, tell it
     129      746723 :   if (!force_iteration && (_nl_forced_its))
     130             :   {
     131         112 :     LibmeshPetscCallA(_fe_problem.comm().get(), SNESSetForceIteration(snes, PETSC_TRUE));
     132             :   }
     133             : #endif
     134             : 
     135             :   // See if SNESSetFunctionDomainError() has been called.  Note:
     136             :   // SNESSetFunctionDomainError() and SNESGetFunctionDomainError()
     137             :   // were added in different releases of PETSc.
     138             :   PetscBool domainerror;
     139      746723 :   LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetFunctionDomainError(snes, &domainerror));
     140      746723 :   if (domainerror)
     141           0 :     status = MooseConvergenceStatus::DIVERGED;
     142             : 
     143             :   Real fnorm_old;
     144             :   // This is the first residual before any iterations have been done, but after
     145             :   // solution-modifying objects (if any) have been imposed on the solution vector.
     146             :   // We save it, and use it to detect convergence if system.usePreSMOResidual() == false.
     147      746723 :   if (iter == 0)
     148             :   {
     149      282707 :     system.setInitialResidual(fnorm);
     150      282707 :     fnorm_old = fnorm;
     151      282707 :     _nl_current_pingpong = 0;
     152             :   }
     153             :   else
     154      464016 :     fnorm_old = system._last_nl_rnorm;
     155             : 
     156             :   // Check for nonlinear residual ping-pong.
     157             :   // Ping-pong will always start from a residual increase
     158      746723 :   if ((_nl_current_pingpong % 2 == 1 && !(fnorm > fnorm_old)) ||
     159      742088 :       (_nl_current_pingpong % 2 == 0 && fnorm > fnorm_old))
     160        9896 :     _nl_current_pingpong += 1;
     161             :   else
     162      736827 :     _nl_current_pingpong = 0;
     163             : 
     164      746723 :   std::ostringstream oss;
     165      746723 :   if (fnorm != fnorm)
     166             :   {
     167           0 :     oss << "Failed to converge, residual norm is NaN\n";
     168           0 :     status = MooseConvergenceStatus::DIVERGED;
     169             :   }
     170      746723 :   else if ((iter >= _nl_forced_its) && fnorm < abs_tol)
     171             :   {
     172      140769 :     oss << "Converged due to residual norm " << fnorm << " < " << abs_tol << '\n';
     173      140769 :     status = MooseConvergenceStatus::CONVERGED;
     174             :   }
     175      605954 :   else if (nfuncs >= max_funcs)
     176             :   {
     177           0 :     oss << "Exceeded maximum number of residual evaluations: " << nfuncs << " > " << max_funcs
     178           0 :         << '\n';
     179           0 :     status = MooseConvergenceStatus::DIVERGED;
     180             :   }
     181      605954 :   else if ((iter >= _nl_forced_its) && iter && fnorm > system._last_nl_rnorm &&
     182        5727 :            fnorm >= _div_threshold)
     183             :   {
     184           0 :     oss << "Nonlinear solve was blowing up!\n";
     185           0 :     status = MooseConvergenceStatus::DIVERGED;
     186             :   }
     187      746723 :   if ((iter >= _nl_forced_its) && iter && status == MooseConvergenceStatus::ITERATING)
     188             :   {
     189      354017 :     const auto ref_residual = system.referenceResidual();
     190      354017 :     if (checkRelativeConvergence(iter, fnorm, ref_residual, rel_tol, abs_tol, oss))
     191      139462 :       status = MooseConvergenceStatus::CONVERGED;
     192      214555 :     else if (snorm < rel_step_tol * xnorm)
     193             :     {
     194           0 :       oss << "Converged due to small update length: " << snorm << " < " << rel_step_tol << " * "
     195           0 :           << xnorm << '\n';
     196           0 :       status = MooseConvergenceStatus::CONVERGED;
     197             :     }
     198      214555 :     else if (_nl_rel_div_tol > 0 && fnorm > ref_residual * _nl_rel_div_tol)
     199             :     {
     200           6 :       oss << "Diverged due to relative residual " << ref_residual << " > divergence tolerance "
     201           6 :           << _nl_rel_div_tol << " * relative residual " << ref_residual << '\n';
     202           6 :       status = MooseConvergenceStatus::DIVERGED;
     203             :     }
     204      214549 :     else if (_nl_abs_div_tol > 0 && fnorm > _nl_abs_div_tol)
     205             :     {
     206          10 :       oss << "Diverged due to residual " << fnorm << " > absolute divergence tolerance "
     207          10 :           << _nl_abs_div_tol << '\n';
     208          10 :       status = MooseConvergenceStatus::DIVERGED;
     209             :     }
     210      214539 :     else if (_nl_current_pingpong > _nl_max_pingpong)
     211             :     {
     212           6 :       oss << "Diverged due to maximum nonlinear residual pingpong achieved" << '\n';
     213           6 :       status = MooseConvergenceStatus::DIVERGED;
     214             :     }
     215             :   }
     216             : 
     217      746723 :   system._last_nl_rnorm = fnorm;
     218      746723 :   system._current_nl_its = static_cast<unsigned int>(iter);
     219             : 
     220      746723 :   std::string msg;
     221      746723 :   msg = oss.str();
     222      746723 :   if (_app.multiAppLevel() > 0)
     223      189124 :     MooseUtils::indentMessage(_app.name(), msg);
     224      746723 :   if (msg.length() > 0)
     225             : #if !PETSC_VERSION_LESS_THAN(3, 17, 0)
     226      280253 :     LibmeshPetscCallA(_fe_problem.comm().get(), PetscInfo(snes, "%s", msg.c_str()));
     227             : #else
     228             :     LibmeshPetscCallA(_fe_problem.comm().get(), PetscInfo(snes, msg.c_str()));
     229             : #endif
     230             : 
     231      746723 :   verboseOutput(oss);
     232             : 
     233      746723 :   return status;
     234      746723 : }

Generated by: LCOV version 1.14