https://mooseframework.inl.gov
Loading...
Searching...
No Matches
ReferenceResidualConvergence.C
Go to the documentation of this file.
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
13#include "FEProblemBase.h"
14#include "PetscSupport.h"
15#include "Executioner.h"
16#include "NonlinearSystemBase.h"
17#include "TaggingInterface.h"
18#include "AuxiliarySystem.h"
19#include "MooseVariableScalar.h"
20#include "NonlinearSystem.h"
21
22// PETSc includes
23#include <petscsnes.h>
24
26
29{
32
34 "Check the convergence of a problem with respect to a user-supplied reference solution."
35 " Replaces ReferenceResidualProblem, currently still used in conjunction with it.");
36
37 return params;
38}
39
41 : DefaultNonlinearConvergence(parameters),
43 _norm_type_enum(getParam<MooseEnum>("normalization_type")),
44 _accept_mult(getParam<Real>("acceptable_multiplier")),
45 _accept_iters(getParam<unsigned int>("acceptable_iterations")),
46 _nl_sys_num(_fe_problem.solverSysNum(getParam<SolverSystemName>("solver_sys"))),
47 _residual_vector(nullptr),
48 _reference_vector(nullptr),
49 _zero_ref_type(
50 getParam<MooseEnum>("zero_reference_residual_treatment").getEnum<ZeroReferenceType>()),
51 _unscale_the_residual(getParam<bool>("unscale_the_residual")),
52 _reference_vector_tag_id(Moose::INVALID_TAG_ID)
53{
54 if (_fe_problem.numNonlinearSystems() > 1 && !isParamSetByUser("solver_sys"))
55 paramError("solver_sys",
56 "Reference residual problem does not currently support multiple nonlinear systems "
57 "in a single Convergence object. Multiple Convergence objects can be used, one for "
58 "each nonlinear system, via the 'solver_sys' parameter.");
59
60 if (parameters.isParamValid("residual_vector"))
61 {
62 const auto residual_vector_tag_id =
63 _fe_problem.getVectorTagID(getParam<TagName>("residual_vector"));
66 }
67 else
69
70 if (parameters.isParamValid("reference_vector"))
71 {
72 _reference_vector_tag_id = _fe_problem.getVectorTagID(getParam<TagName>("reference_vector"));
75 }
76 else
78 "No `reference_vector` is provided, thus the Reference Residual convergence method will "
79 "revert to default tolerance checking. `reference_vector` will become a required parameter "
80 "on June 1st, 2027. If you are using `ReferenceResidualProblem`, either provide a "
81 "reference_vector or use a standard problem type (e.g., remove "
82 "Problem/type=ReferenceResidualProblem from your input file). If you are using "
83 "`ReferenceResidualConvergence`, either provide a reference_vector or utilize "
84 "`DefaultNonlinearConvergence` instead.");
85
86 if (_norm_type_enum == "LOCAL_L2")
87 {
89 _local_norm = true;
90 }
91 else if (_norm_type_enum == "GLOBAL_L2")
92 {
94 _local_norm = false;
95 }
96 else if (_norm_type_enum == "LOCAL_LINF")
97 {
99 _local_norm = true;
100 }
101 else if (_norm_type_enum == "GLOBAL_LINF")
102 {
104 _local_norm = false;
105 }
106 else
107 mooseAssert(false, "This point should not be reached.");
108
109 if (_local_norm && !parameters.isParamValid("reference_vector"))
110 paramError("reference_vector", "If local norm is used, a reference_vector must be provided.");
111}
112
118
119void
121{
123 // If no refernce_vector is provided, just revert to DefaultNonlinearConvergence behavior
125 return;
126
127 auto & nonlinear_sys = nonlinearSystem();
128 auto & s = nonlinear_sys.system();
129
130 // If the user provides reference_vector, that implies that they want the
131 // individual variables compared against their reference quantities in the
132 // tag vector. The code depends on having _soln_var_names populated,
133 // so fill that out if they didn't specify solution_variables.
134 for (const auto var_num : make_range(s.n_vars()))
135 _soln_var_names.push_back(s.variable_name(var_num));
136 const auto n_soln_vars = nonlinear_sys.nVariables();
137
138 const auto converge_on = getParam<std::vector<NonlinearVariableName>>("converge_on");
139 if (!converge_on.empty())
140 {
141 _converge_on_var.assign(n_soln_vars, false);
142 for (std::size_t i = 0; i < n_soln_vars; ++i)
143 for (const auto & c : converge_on)
145 {
146 _converge_on_var[i] = true;
147 break;
148 }
149 }
150 else
151 _converge_on_var.assign(n_soln_vars, true);
152
153 unsigned int num_variables_in_groups = 0;
154 for (const auto i : index_range(_group_variables))
155 {
156 num_variables_in_groups += _group_variables[i].size();
157 if (_group_variables[i].size() == 1)
158 paramError("group_variables",
159 "variable ",
160 _group_variables[i][0],
161 " is not grouped with other variables.");
162 }
163
164 // If no groups, size = n_soln_vars
165 unsigned int n_groups = n_soln_vars - num_variables_in_groups + _group_variables.size();
166 _group_ref_resid.resize(n_groups);
167 _group_resid.resize(n_groups);
168 _group_names.resize(n_groups);
169 _converge_on_group.assign(n_groups, true);
170 _scaling_factors.resize(n_soln_vars);
171
172 // Check to make sure variables aren't in multiple groups
174 {
175 std::set<std::string> check_duplicate;
176 for (const auto i : index_range(_group_variables))
177 for (const auto j : index_range(_group_variables[i]))
178 check_duplicate.insert(_group_variables[i][j]);
179
180 if (check_duplicate.size() != num_variables_in_groups)
181 paramError("group_variables", "A variable cannot be included in multiple groups.");
182 }
183
184 _soln_vars.clear();
185 for (const auto i : make_range(n_soln_vars))
186 {
187 bool found_match = false;
188 for (const auto var_num : make_range(s.n_vars()))
189 if (_soln_var_names[i] == s.variable_name(var_num))
190 {
191 _soln_vars.push_back(var_num);
192 found_match = true;
193 break;
194 }
195
196 if (!found_match)
197 mooseError("Could not find solution variable '",
199 "' in system '",
200 s.name(),
201 "'.");
202 }
203
204 unsigned int ungroup_index = 0;
206 ungroup_index = _group_variables.size();
207
208 // Determine which group each variable belongs to
209 _group_index.resize(n_soln_vars);
210 _is_var_grouped.assign(n_soln_vars, false);
211 for (const auto i : index_range(_soln_vars))
212 {
214 {
215 for (const auto j : index_range(_group_variables))
216 if (std::find(_group_variables[j].begin(),
217 _group_variables[j].end(),
218 s.variable_name(_soln_vars[i])) != _group_variables[j].end())
219 {
220 if (!_converge_on_var[i])
221 paramError("converge_on",
222 "You added variable '",
224 "' to a group but excluded it from the convergence check. This is not "
225 "permitted.");
226
227 _group_index[i] = j;
228 _is_var_grouped[i] = true;
229 break;
230 }
231
232 if (!_is_var_grouped[i])
233 {
234 _group_index[i] = ungroup_index;
235 ungroup_index++;
236 }
237 }
238 else
239 _group_index[i] = i;
240 }
241
242 // Check for variable groups containing both field and scalar variables
243 for (const auto i : index_range(_group_variables))
244 {
245 unsigned int num_scalar_vars = 0;
246 unsigned int num_field_vars = 0;
247 if (_group_variables[i].size() > 1)
248 {
249 for (const auto j : index_range(_group_variables[i]))
250 for (const auto var_num : make_range(s.n_vars()))
251 if (_group_variables[i][j] == s.variable_name(var_num))
252 {
253 if (nonlinear_sys.isScalarVariable(_soln_vars[var_num]))
254 ++num_scalar_vars;
255 else
256 ++num_field_vars;
257 break;
258 }
259 }
260 if (num_scalar_vars > 0 && num_field_vars > 0)
261 paramWarning("group_variables",
262 "standard variables and scalar variables are grouped together in group ",
263 i);
264 }
265
266 for (const auto i : index_range(_group_names))
267 {
268 // Accumulate names for a given group
269 std::vector<NonlinearVariableName> names;
270 for (const auto j : index_range(_group_index))
271 if (_group_index[j] == i)
272 {
273 names.push_back(_soln_var_names[j]);
275 }
276 if (names.size() == 0)
277 mooseError("Internal error, something is wrong with variable grouping");
278 else if (names.size() == 1)
279 _group_names[i] = names[0];
280 else
281 {
282 _group_names[i] = "(";
283 for (const auto j : index_range(names))
284 {
285 _group_names[i] += names[j];
286 if (j != names.size() - 1)
287 _group_names[i] += ", ";
288 }
289 _group_names[i] += ")";
290 }
291 }
292}
293
294void
296{
297 // If no reference_vector is provided, this method is completely skipped
298
299 auto & nonlinear_sys = nonlinearSystem();
300 auto & s = nonlinear_sys.system();
301
302 for (const auto i : index_range(_scaling_factors))
303 if (nonlinear_sys.isScalarVariable(_soln_vars[i]))
304 _scaling_factors[i] = nonlinear_sys.getScalarVariable(0, _soln_vars[i]).scalingFactor();
305 else
306 _scaling_factors[i] = nonlinear_sys.getVariable(/*tid*/ 0, _soln_vars[i]).scalingFactor();
307
308 std::fill(_group_resid.begin(), _group_resid.end(), 0.0);
309 std::fill(_group_ref_resid.begin(), _group_ref_resid.end(), 0.0);
310
311 for (const auto i : index_range(_soln_vars))
312 {
313 if (_converge_on_var[i])
314 {
315 const auto group = _group_index[i];
316
317 // Prepare residual
318 auto resid = Utility::pow<2>(s.calculate_norm(*_residual_vector, _soln_vars[i], _norm_type));
320 {
321 mooseAssert(_scaling_factors[i], "Scaling factor must not be zero");
322 resid /= Utility::pow<2>(_scaling_factors[i]);
323 }
324 _group_resid[group] += resid;
325
326 // Prepare reference residual. If local norm, this is actually the ratio of the residual
327 // dividied by the reference at all DOF
328 Real ref_resid;
329 if (_local_norm)
330 {
331 mooseAssert((*_residual_vector).size() == (*_reference_vector).size(),
332 "Sizes of nonlinear RHS and reference vector should be the same.");
333 mooseAssert((*_reference_vector).size(), "Reference vector must be provided.");
334 auto ref = _reference_vector->clone();
335 // Add a tiny number to the reference to prevent a divide by zero.
336 ref->add(std::numeric_limits<Number>::min());
337 auto div = (*_residual_vector).clone();
338 *div /= *ref;
339 ref_resid = Utility::pow<2>(s.calculate_norm(*div, _soln_vars[i], _norm_type));
340 }
341 else
342 {
343 ref_resid =
344 Utility::pow<2>(s.calculate_norm(*_reference_vector, _soln_vars[i], _norm_type));
346 ref_resid /= Utility::pow<2>(_scaling_factors[i]);
347 }
348 _group_ref_resid[group] += ref_resid;
349 }
350 }
351
352 for (const auto i : index_range(_group_resid))
353 {
354 _group_resid[i] = std::sqrt(_group_resid[i]);
355 _group_ref_resid[i] = std::sqrt(_group_ref_resid[i]);
356 }
357}
358
359void
361{
362 // If no refernce_vector is provided, just revert to DefaultNonlinearConvergence behavior
364 return;
365
367
368 std::ostringstream out;
369 out << _name << ": " << _norm_type_enum << " Reference Residual check\n";
370
371 if (_group_names.size() > 0)
372 {
373 // Set residual and references so that they always have a spacing of 8
374 out << std::setprecision(2) << std::scientific;
375 unsigned int var_space = 0;
376 for (const auto i : index_range(_group_names))
377 if (_group_names[i].size() > var_space)
378 var_space = _group_names[i].size();
379
380 for (const auto i : index_range(_group_names))
381 {
382 if (_converge_on_group[i])
383 {
384 // Print residual
385 out << " " << std::setw(var_space + 8) << std::right << _group_names[i] + "-> res: "
386 << (_group_resid[i] < _abs_tol ? COLOR_YELLOW : COLOR_DEFAULT) << std::setw(8)
387 << _group_resid[i] << COLOR_DEFAULT;
388
389 // Print res/ref ratio
390 if (_local_norm)
391 out << " local res/ref: "
392 << (_group_resid[i] / _group_ref_resid[i] < _rel_tol ? COLOR_GREEN : COLOR_DEFAULT)
393 << std::setw(8) << _group_ref_resid[i] << COLOR_DEFAULT << "\n";
394 else
395 {
396 // Print reference first if not local norm
397 out << " ref: " << std::setw(8) << _group_ref_resid[i] << " res/ref: ";
398
399 if (!_group_ref_resid[i])
400 out << _group_resid[i] << "\n";
401 else
402 out << (_group_resid[i] / _group_ref_resid[i] < _rel_tol ? COLOR_GREEN : COLOR_DEFAULT)
403 << std::setw(8) << _group_resid[i] / _group_ref_resid[i] << COLOR_DEFAULT << "\n";
404 }
405 }
406 }
407 _console << out.str() << std::flush;
408 }
409}
410
411bool
413 const Real /*fnorm*/,
414 const Real abs_tol,
415 const Real rel_tol,
416 const Real /*initial_residual_before_preset_bcs*/)
417{
418 // Convergence is checked via:
419 // 1) Ratio of group residual to reference is less than relative tolerance
420 // 2) if group residual is less than absolute tolerance
421 // 3) if group reference residual is zero and:
422 // 3.1) Convergence type is ZERO_TOLERANCE and group residual is zero (rare, but possible, and
423 // historically implemented that way)
424 // 3.2) Convergence type is RELATIVE_TOLERANCE and group residual
425 // is less than relative tolerance. (i.e., using the relative tolerance to check group
426 // convergence in an absolute way)
427
428 bool convergedRelative = true;
429 for (const auto i : index_range(_group_resid))
430 convergedRelative &=
431 ((!_local_norm && _group_resid[i] < _group_ref_resid[i] * rel_tol) ||
432 (_local_norm && _group_ref_resid[i] < rel_tol) || _group_resid[i] < abs_tol ||
433 (!_group_ref_resid[i] && !_local_norm &&
436 _group_resid[i] <= rel_tol))));
437 return convergedRelative;
438}
439
440bool
442 const Real fnorm,
443 const Real ref_norm,
444 const Real rel_tol,
445 const Real abs_tol,
446 std::ostringstream & oss)
447{
448 // If no refernce_vector is provided, just revert to DefaultNonlinearConvergence behavior
451 n_iter, fnorm, ref_norm, rel_tol, abs_tol, oss);
452
453 if (checkConvergenceIndividVars(fnorm, abs_tol, rel_tol, ref_norm))
454 {
455 oss << "Converged normally";
456 return true;
457 }
458 else if (n_iter >= _accept_iters &&
460 fnorm, abs_tol * _accept_mult, rel_tol * _accept_mult, ref_norm))
461 {
462 oss << " Converged due a larger acceptable tolerance due to `acceptible_multiplier` after "
463 "`acceptible_iterations`.";
464 _console << " Converged due to ACCEPTABLE tolerances" << std::endl;
465 return true;
466 }
467
468 return false;
469}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
void mooseDeprecated(Args &&... args)
Emit a deprecated code/feature message with the given stringified, concatenated args.
Definition MooseError.h:363
registerMooseObject("MooseApp", ReferenceResidualConvergence)
void ErrorVector unsigned int
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
virtual void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job.
Definition Convergence.h:45
Default nonlinear convergence criteria for FEProblem.
PetscReal _rel_tol
Nonlinear relative tolerance.
virtual bool checkResidualConvergence(const unsigned int n_iter, const Real fnorm, const Real ref_norm, const Real rel_tol, const Real abs_tol, std::ostringstream &oss)
Check the absolute and relative convergence of the nonlinear solution.
PetscReal _abs_tol
Nonlinear absolute tolerance.
virtual std::size_t numNonlinearSystems() const override
NonlinearSystemBase & getNonlinearSystemBase(const unsigned int sys_num)
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump.
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another,...
const InputParameters & parameters() const
Get the parameters of the object.
Definition MooseBase.h:131
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition MooseBase.h:457
bool isParamSetByUser(const std::string &name) const
Test if the supplied parameter is set by a user, as opposed to not set or set to default.
Definition MooseBase.h:205
const std::string & _name
The name of this class.
Definition MooseBase.h:381
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
Nonlinear system to be solved.
virtual NumericVector< Number > & RHS()=0
Uses a reference residual to define relative convergence criteria.
virtual void nonlinearConvergenceSetup() override
Performs setup necessary for each call to checkConvergence.
bool checkConvergenceIndividVars(const Real fnorm, const Real abs_tol, const Real rel_tol, const Real initial_residual_before_preset_bcs)
Check the convergence by comparing the norm of each variable's residual separately against its refere...
ZeroReferenceType
Container for convergence treatment when the reference residual is zero.
ReferenceResidualConvergence(const InputParameters &parameters)
virtual bool checkResidualConvergence(const unsigned int n_iter, const Real fnorm, const Real ref_norm, const Real rel_tol, const Real abs_tol, std::ostringstream &oss) override
Check the absolute and relative convergence of the nonlinear solution.
enum ReferenceResidualConvergence::ZeroReferenceType _zero_ref_type
std::vector< bool > _is_var_grouped
Vector of bools to signify if variable is in a group.
const NumericVector< Number > * _reference_vector
The vector storing the reference residual values.
std::vector< unsigned int > _soln_vars
const bool _unscale_the_residual
Bool to unscale the residual before convergence checks and screen output.
const MooseEnum _norm_type_enum
Enum holding the normalization type.
virtual void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job.
void updateReferenceResidual()
Computes the reference residuals for each group.
const unsigned int _nl_sys_num
Nonlinear system to which this convergence object applies.
virtual NonlinearSystemBase & nonlinearSystem() override
Nonlinear system whose convergence state should be checked.
libMesh::FEMNormType _norm_type
Container for normalization type.
std::vector< NonlinearVariableName > _group_names
std::vector< unsigned int > _group_index
Group number index for each variable.
std::vector< Real > _scaling_factors
Local storage for the scaling factors applied to each of the variables to apply to _ref_resid_vars.
std::vector< bool > _converge_on_var
Flag for each solution variable or group being in 'converge_on'.
const NumericVector< Number > * _residual_vector
The optional vector storing the reference residual values.
TagID _reference_vector_tag_id
The reference vector tag id.
bool _local_norm
Flag to optionally perform normalization of residual by reference residual before or after L2 norm is...
std::vector< NonlinearVariableName > _soln_var_names
Interface class shared between ReferenceResidualProblem and ReferenceResidualConvergence.
static InputParameters validParams()
bool _use_group_variables
True if any variables are grouped.
std::vector< std::vector< NonlinearVariableName > > _group_variables
Name of variables that are grouped together to check convergence.
void paramWarning(const std::string &param, Args... args) const
virtual TagID getVectorTagID(const TagName &tag_name) const
Get a TagID from a TagName.
Definition SubProblem.C:204
virtual NumericVector< Number > & getVector(const std::string &name)
Get a raw NumericVector by name.
Definition SystemBase.C:932
virtual std::unique_ptr< NumericVector< T > > clone() const=0
bool globCompare(const std::string &candidate, const std::string &pattern, std::size_t c, std::size_t p)
Definition MooseUtils.C:943
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...