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 : }
|