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 : #include "ConvergenceIterationTypes.h"
16 :
17 : #include "libmesh/equation_systems.h"
18 :
19 : // PETSc includes
20 : #include <petsc.h>
21 : #include <petscmat.h>
22 : #include <petscsnes.h>
23 :
24 : registerMooseObject("MooseApp", DefaultNonlinearConvergence);
25 :
26 : InputParameters
27 133226 : DefaultNonlinearConvergence::validParams()
28 : {
29 133226 : InputParameters params = DefaultConvergenceBase::validParams();
30 133226 : params += FEProblemSolve::feProblemDefaultConvergenceParams();
31 :
32 399678 : params.addPrivateParam<bool>("added_as_default", false);
33 :
34 133226 : params.addClassDescription("Default convergence criteria for FEProblem.");
35 :
36 133226 : return params;
37 0 : }
38 :
39 66364 : DefaultNonlinearConvergence::DefaultNonlinearConvergence(const InputParameters & parameters)
40 : : DefaultConvergenceBase(parameters),
41 199092 : _fe_problem(*getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")),
42 132728 : _nl_abs_div_tol(getSharedExecutionerParam<Real>("nl_abs_div_tol")),
43 132728 : _nl_rel_div_tol(getSharedExecutionerParam<Real>("nl_div_tol")),
44 66364 : _div_threshold(std::numeric_limits<Real>::max()),
45 132728 : _nl_forced_its(getSharedExecutionerParam<unsigned int>("nl_forced_its")),
46 132728 : _nl_max_pingpong(getSharedExecutionerParam<unsigned int>("n_max_nonlinear_pingpong")),
47 66364 : _nl_current_pingpong(0)
48 : {
49 66364 : EquationSystems & es = _fe_problem.es();
50 :
51 66364 : es.parameters.set<unsigned int>("nonlinear solver maximum iterations") =
52 199092 : getSharedExecutionerParam<unsigned int>("nl_max_its");
53 66364 : es.parameters.set<unsigned int>("nonlinear solver maximum function evaluations") =
54 199092 : getSharedExecutionerParam<unsigned int>("nl_max_funcs");
55 66364 : es.parameters.set<Real>("nonlinear solver absolute residual tolerance") =
56 199092 : getSharedExecutionerParam<Real>("nl_abs_tol");
57 66364 : es.parameters.set<Real>("nonlinear solver relative residual tolerance") =
58 199092 : getSharedExecutionerParam<Real>("nl_rel_tol");
59 66364 : es.parameters.set<Real>("nonlinear solver divergence tolerance") =
60 199092 : getSharedExecutionerParam<Real>("nl_div_tol");
61 66364 : es.parameters.set<Real>("nonlinear solver absolute step tolerance") =
62 199092 : getSharedExecutionerParam<Real>("nl_abs_step_tol");
63 66364 : es.parameters.set<Real>("nonlinear solver relative step tolerance") =
64 199092 : getSharedExecutionerParam<Real>("nl_rel_step_tol");
65 66364 : }
66 :
67 : void
68 56646 : DefaultNonlinearConvergence::checkIterationType(IterationType it_type) const
69 : {
70 56646 : DefaultConvergenceBase::checkIterationType(it_type);
71 :
72 56646 : if (it_type != ConvergenceIterationTypes::NONLINEAR)
73 3 : mooseError("DefaultNonlinearConvergence can only be used with nonlinear solves.");
74 56643 : }
75 :
76 : NonlinearSystemBase &
77 732388 : DefaultNonlinearConvergence::nonlinearSystem()
78 : {
79 732388 : return _fe_problem.currentNonlinearSystem();
80 : }
81 :
82 : bool
83 731480 : DefaultNonlinearConvergence::checkResidualConvergence(const unsigned int n_iter,
84 : const Real fnorm,
85 : const Real ref_norm,
86 : const Real rel_tol,
87 : const Real abs_tol,
88 : std::ostringstream & oss)
89 : {
90 731480 : if (fnorm < abs_tol)
91 : {
92 144618 : oss << "Converged due to absolute residual " << fnorm << " < absolute tolerance (" << abs_tol
93 144618 : << ")\n";
94 144618 : return true;
95 : }
96 586862 : else if (n_iter && fnorm <= ref_norm * rel_tol)
97 : {
98 139535 : oss << "Converged due to relative/normalized residual norm " << fnorm / ref_norm
99 139535 : << " < relative tolerance (" << rel_tol << ")\n";
100 139535 : return true;
101 : }
102 : else
103 447327 : return false;
104 : }
105 :
106 : Convergence::MooseConvergenceStatus
107 750577 : DefaultNonlinearConvergence::checkConvergence(unsigned int n_iter)
108 : {
109 750577 : TIME_SECTION(_perfid_check_convergence);
110 :
111 750577 : NonlinearSystemBase & system = nonlinearSystem();
112 750577 : MooseConvergenceStatus status = MooseConvergenceStatus::ITERATING;
113 :
114 750577 : SNES snes = system.getSNES();
115 :
116 : // ||u||
117 : PetscReal xnorm;
118 750577 : LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetSolutionNorm(snes, &xnorm));
119 :
120 : // ||r||
121 : PetscReal fnorm;
122 750577 : LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetFunctionNorm(snes, &fnorm));
123 :
124 : // ||du||
125 : PetscReal snorm;
126 750577 : LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetUpdateNorm(snes, &snorm));
127 :
128 : // Get current number of function evaluations done by SNES
129 : PetscInt nfuncs;
130 750577 : LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetNumberFunctionEvals(snes, &nfuncs));
131 :
132 : // Get tolerances from SNES
133 : PetscReal rel_step_tol;
134 : PetscInt max_its, max_funcs;
135 750577 : LibmeshPetscCallA(
136 : _fe_problem.comm().get(),
137 : SNESGetTolerances(snes, &_abs_tol, &_rel_tol, &rel_step_tol, &max_its, &max_funcs));
138 :
139 : #if !PETSC_VERSION_LESS_THAN(3, 8, 4)
140 750577 : PetscBool force_iteration = PETSC_FALSE;
141 750577 : LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetForceIteration(snes, &force_iteration));
142 :
143 : // if PETSc says to force iteration, then force at least one iteration
144 750577 : if (force_iteration && !(_nl_forced_its))
145 752 : _nl_forced_its = 1;
146 :
147 : // if specified here to force iteration, but PETSc doesn't know, tell it
148 750577 : if (!force_iteration && (_nl_forced_its))
149 : {
150 164 : LibmeshPetscCallA(_fe_problem.comm().get(), SNESSetForceIteration(snes, PETSC_TRUE));
151 : }
152 : #endif
153 :
154 : // See if SNESSetFunctionDomainError() has been called.
155 : // Note: SNESSetFunctionDomainError() and SNESGetFunctionDomainError() were added
156 : // in different releases of PETSc. The latter was removed in PETSc 3.25.0.
157 : #if PETSC_VERSION_LESS_THAN(3, 25, 0)
158 : PetscBool domainerror;
159 : LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetFunctionDomainError(snes, &domainerror));
160 : if (domainerror)
161 : status = MooseConvergenceStatus::DIVERGED;
162 : #else
163 : SNESConvergedReason reason;
164 750577 : LibmeshPetscCallA(_fe_problem.comm().get(), SNESGetConvergedReason(snes, &reason));
165 750577 : if (reason == SNES_DIVERGED_FUNCTION_DOMAIN)
166 0 : status = MooseConvergenceStatus::DIVERGED;
167 : #endif
168 :
169 : // Needed by ResidualReferenceConvergence
170 750577 : nonlinearConvergenceSetup();
171 :
172 : Real fnorm_old;
173 : // This is the first residual before any iterations have been done, but after
174 : // solution-modifying objects (if any) have been imposed on the solution vector.
175 : // We save it, and use it to detect convergence if system.usePreSMOResidual() == false.
176 750577 : if (n_iter == 0)
177 : {
178 286471 : system.setInitialResidual(fnorm);
179 286471 : fnorm_old = fnorm;
180 286471 : _nl_current_pingpong = 0;
181 : }
182 : else
183 464106 : fnorm_old = system._last_nl_rnorm;
184 :
185 : // Check for nonlinear residual ping-pong.
186 : // Ping-pong will always start from a residual increase
187 750577 : if ((_nl_current_pingpong % 2 == 1 && !(fnorm > fnorm_old)) ||
188 745930 : (_nl_current_pingpong % 2 == 0 && fnorm > fnorm_old))
189 9608 : _nl_current_pingpong += 1;
190 : else
191 740969 : _nl_current_pingpong = 0;
192 :
193 750577 : const auto ref_residual = system.referenceResidual();
194 750577 : std::ostringstream oss;
195 750577 : if (n_iter < _nl_forced_its)
196 1554 : oss << "Number of forced iterations not yet reached: " << n_iter << " < " << _nl_forced_its
197 1554 : << '\n';
198 749023 : else if (fnorm != fnorm)
199 : {
200 0 : oss << "Failed to converge, residual norm is NaN\n";
201 0 : status = MooseConvergenceStatus::DIVERGED;
202 : }
203 749023 : else if (checkResidualConvergence(n_iter, fnorm, ref_residual, _rel_tol, _abs_tol, oss))
204 285886 : status = MooseConvergenceStatus::CONVERGED;
205 463137 : else if (nfuncs >= max_funcs)
206 : {
207 0 : oss << "Exceeded maximum number of residual evaluations: " << nfuncs << " > " << max_funcs
208 0 : << '\n';
209 0 : status = MooseConvergenceStatus::DIVERGED;
210 : }
211 463137 : else if ((n_iter >= _nl_forced_its) && n_iter && fnorm > system._last_nl_rnorm &&
212 5155 : fnorm >= _div_threshold)
213 : {
214 0 : oss << "Nonlinear solve was blowing up!\n";
215 0 : status = MooseConvergenceStatus::DIVERGED;
216 : }
217 463137 : else if (snorm < rel_step_tol * xnorm)
218 : {
219 0 : oss << "Converged due to small update length: " << snorm << " < " << rel_step_tol << " * "
220 0 : << xnorm << '\n';
221 0 : status = MooseConvergenceStatus::CONVERGED;
222 : }
223 463137 : else if (_nl_rel_div_tol > 0 && fnorm > ref_residual * _nl_rel_div_tol)
224 : {
225 2 : oss << "Diverged due to relative residual " << ref_residual << " > divergence tolerance "
226 2 : << _nl_rel_div_tol << " * relative residual " << ref_residual << '\n';
227 2 : status = MooseConvergenceStatus::DIVERGED;
228 : }
229 463135 : else if (_nl_abs_div_tol > 0 && fnorm > _nl_abs_div_tol)
230 : {
231 9 : oss << "Diverged due to residual " << fnorm << " > absolute divergence tolerance "
232 9 : << _nl_abs_div_tol << '\n';
233 9 : status = MooseConvergenceStatus::DIVERGED;
234 : }
235 463126 : else if (_nl_current_pingpong > _nl_max_pingpong)
236 : {
237 9 : oss << "Diverged due to maximum nonlinear residual pingpong achieved" << '\n';
238 9 : status = MooseConvergenceStatus::DIVERGED;
239 : }
240 :
241 750577 : system._last_nl_rnorm = fnorm;
242 750577 : system._current_nl_its = static_cast<unsigned int>(n_iter);
243 :
244 750577 : std::string msg;
245 750577 : msg = oss.str();
246 750577 : if (_app.multiAppLevel() > 0)
247 383722 : MooseUtils::indentMessage(_app.name(), msg);
248 750577 : if (msg.length() > 0)
249 : #if !PETSC_VERSION_LESS_THAN(3, 17, 0)
250 287460 : LibmeshPetscCallA(_fe_problem.comm().get(), PetscInfo(snes, "%s", msg.c_str()));
251 : #else
252 : LibmeshPetscCallA(_fe_problem.comm().get(), PetscInfo(snes, msg.c_str()));
253 : #endif
254 :
255 750577 : verboseOutput(oss);
256 :
257 750577 : return status;
258 750577 : }
|