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