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 "Executioner.h"
15#include "NonlinearSystemBase.h"
16#include "TaggingInterface.h"
17#include "AuxiliarySystem.h"
18#include "MooseVariableScalar.h"
19#include "NonlinearSystem.h"
20
22
25{
28
30 "Check the convergence of a problem with respect to a user-supplied reference solution."
31 " Replaces ReferenceResidualProblem, currently still used in conjunction with it.");
32
33 return params;
34}
35
37 : DefaultNonlinearConvergence(parameters),
39 _norm_type_enum(getParam<MooseEnum>("normalization_type")),
40 _accept_mult(getParam<Real>("acceptable_multiplier")),
41 _accept_iters(getParam<unsigned int>("acceptable_iterations")),
42 _nl_sys_num(_fe_problem.solverSysNum(getParam<SolverSystemName>("solver_sys"))),
43 _residual_vector(nullptr),
44 _reference_vector(nullptr),
45 _zero_ref_type(
46 getParam<MooseEnum>("zero_reference_residual_treatment").getEnum<ZeroReferenceType>()),
47 _unscale_the_residual(getParam<bool>("unscale_the_residual")),
48 _reference_vector_tag_id(Moose::INVALID_TAG_ID)
49{
50 if (_fe_problem.numNonlinearSystems() > 1 && !isParamSetByUser("solver_sys"))
51 paramError("solver_sys",
52 "Reference residual problem does not currently support multiple nonlinear systems "
53 "in a single Convergence object. Multiple Convergence objects can be used, one for "
54 "each nonlinear system, via the 'solver_sys' parameter.");
55
56 if (parameters.isParamValid("residual_vector"))
57 {
58 const auto residual_vector_tag_id =
59 _fe_problem.getVectorTagID(getParam<TagName>("residual_vector"));
62 }
63 else
65
66 if (parameters.isParamValid("reference_vector"))
67 {
68 _reference_vector_tag_id = _fe_problem.getVectorTagID(getParam<TagName>("reference_vector"));
71 }
72 else
74 "No `reference_vector` is provided, thus the Reference Residual convergence method will "
75 "revert to default tolerance checking. `reference_vector` will become a required parameter "
76 "on June 1st, 2027. If you are using `ReferenceResidualProblem`, either provide a "
77 "reference_vector or use a standard problem type (e.g., remove "
78 "Problem/type=ReferenceResidualProblem from your input file). If you are using "
79 "`ReferenceResidualConvergence`, either provide a reference_vector or utilize "
80 "`DefaultNonlinearConvergence` instead.");
81
82 if (_norm_type_enum == "LOCAL_L2")
83 {
85 _local_norm = true;
86 }
87 else if (_norm_type_enum == "GLOBAL_L2")
88 {
90 _local_norm = false;
91 }
92 else if (_norm_type_enum == "LOCAL_LINF")
93 {
95 _local_norm = true;
96 }
97 else if (_norm_type_enum == "GLOBAL_LINF")
98 {
100 _local_norm = false;
101 }
102 else
103 mooseAssert(false, "This point should not be reached.");
104
105 if (_local_norm && !parameters.isParamValid("reference_vector"))
106 paramError("reference_vector", "If local norm is used, a reference_vector must be provided.");
107}
108
114
115void
117{
119 // If no refernce_vector is provided, just revert to DefaultNonlinearConvergence behavior
121 return;
122
123 auto & nonlinear_sys = nonlinearSystem();
124 auto & s = nonlinear_sys.system();
125
126 // If the user provides reference_vector, that implies that they want the
127 // individual variables compared against their reference quantities in the
128 // tag vector. The code depends on having _soln_var_names populated,
129 // so fill that out if they didn't specify solution_variables.
130 for (const auto var_num : make_range(s.n_vars()))
131 _soln_var_names.push_back(s.variable_name(var_num));
132 const auto n_soln_vars = nonlinear_sys.nVariables();
133
134 const auto converge_on = getParam<std::vector<NonlinearVariableName>>("converge_on");
135 if (!converge_on.empty())
136 {
137 _converge_on_var.assign(n_soln_vars, false);
138 for (std::size_t i = 0; i < n_soln_vars; ++i)
139 for (const auto & c : converge_on)
141 {
142 _converge_on_var[i] = true;
143 break;
144 }
145 }
146 else
147 _converge_on_var.assign(n_soln_vars, true);
148
149 unsigned int num_variables_in_groups = 0;
150 for (const auto i : index_range(_group_variables))
151 {
152 num_variables_in_groups += _group_variables[i].size();
153 if (_group_variables[i].size() == 1)
154 paramError("group_variables",
155 "variable ",
156 _group_variables[i][0],
157 " is not grouped with other variables.");
158 }
159
160 // If no groups, size = n_soln_vars
161 unsigned int n_groups = n_soln_vars - num_variables_in_groups + _group_variables.size();
162 _group_ref_resid.resize(n_groups);
163 _group_resid.resize(n_groups);
164 _group_names.resize(n_groups);
165 _converge_on_group.assign(n_groups, true);
166 _scaling_factors.resize(n_soln_vars);
167
168 // Check to make sure variables aren't in multiple groups
170 {
171 std::set<std::string> check_duplicate;
172 for (const auto i : index_range(_group_variables))
173 for (const auto j : index_range(_group_variables[i]))
174 check_duplicate.insert(_group_variables[i][j]);
175
176 if (check_duplicate.size() != num_variables_in_groups)
177 paramError("group_variables", "A variable cannot be included in multiple groups.");
178 }
179
180 _soln_vars.clear();
181 for (const auto i : make_range(n_soln_vars))
182 {
183 bool found_match = false;
184 for (const auto var_num : make_range(s.n_vars()))
185 if (_soln_var_names[i] == s.variable_name(var_num))
186 {
187 _soln_vars.push_back(var_num);
188 found_match = true;
189 break;
190 }
191
192 if (!found_match)
193 mooseError("Could not find solution variable '",
195 "' in system '",
196 s.name(),
197 "'.");
198 }
199
200 unsigned int ungroup_index = 0;
202 ungroup_index = _group_variables.size();
203
204 // Determine which group each variable belongs to
205 _group_index.resize(n_soln_vars);
206 _is_var_grouped.assign(n_soln_vars, false);
207 for (const auto i : index_range(_soln_vars))
208 {
210 {
211 for (const auto j : index_range(_group_variables))
212 if (std::find(_group_variables[j].begin(),
213 _group_variables[j].end(),
214 s.variable_name(_soln_vars[i])) != _group_variables[j].end())
215 {
216 if (!_converge_on_var[i])
217 paramError("converge_on",
218 "You added variable '",
220 "' to a group but excluded it from the convergence check. This is not "
221 "permitted.");
222
223 _group_index[i] = j;
224 _is_var_grouped[i] = true;
225 break;
226 }
227
228 if (!_is_var_grouped[i])
229 {
230 _group_index[i] = ungroup_index;
231 ungroup_index++;
232 }
233 }
234 else
235 _group_index[i] = i;
236 }
237
238 // Check for variable groups containing both field and scalar variables
239 for (const auto i : index_range(_group_variables))
240 {
241 unsigned int num_scalar_vars = 0;
242 unsigned int num_field_vars = 0;
243 if (_group_variables[i].size() > 1)
244 {
245 for (const auto j : index_range(_group_variables[i]))
246 for (const auto var_num : make_range(s.n_vars()))
247 if (_group_variables[i][j] == s.variable_name(var_num))
248 {
249 if (nonlinear_sys.isScalarVariable(_soln_vars[var_num]))
250 ++num_scalar_vars;
251 else
252 ++num_field_vars;
253 break;
254 }
255 }
256 if (num_scalar_vars > 0 && num_field_vars > 0)
257 paramWarning("group_variables",
258 "standard variables and scalar variables are grouped together in group ",
259 i);
260 }
261
262 for (const auto i : index_range(_group_names))
263 {
264 // Accumulate names for a given group
265 std::vector<NonlinearVariableName> names;
266 for (const auto j : index_range(_group_index))
267 if (_group_index[j] == i)
268 {
269 names.push_back(_soln_var_names[j]);
271 }
272 if (names.size() == 0)
273 mooseError("Internal error, something is wrong with variable grouping");
274 else if (names.size() == 1)
275 _group_names[i] = names[0];
276 else
277 {
278 _group_names[i] = "(";
279 for (const auto j : index_range(names))
280 {
281 _group_names[i] += names[j];
282 if (j != names.size() - 1)
283 _group_names[i] += ", ";
284 }
285 _group_names[i] += ")";
286 }
287 }
288}
289
290void
292{
293 // If no reference_vector is provided, this method is completely skipped
294
295 auto & nonlinear_sys = nonlinearSystem();
296 auto & s = nonlinear_sys.system();
297
298 for (const auto i : index_range(_scaling_factors))
299 if (nonlinear_sys.isScalarVariable(_soln_vars[i]))
300 _scaling_factors[i] = nonlinear_sys.getScalarVariable(0, _soln_vars[i]).scalingFactor();
301 else
302 _scaling_factors[i] = nonlinear_sys.getVariable(/*tid*/ 0, _soln_vars[i]).scalingFactor();
303
304 std::fill(_group_resid.begin(), _group_resid.end(), 0.0);
305 std::fill(_group_ref_resid.begin(), _group_ref_resid.end(), 0.0);
306
307 for (const auto i : index_range(_soln_vars))
308 {
309 if (_converge_on_var[i])
310 {
311 const auto group = _group_index[i];
312
313 // Prepare residual
314 auto resid = Utility::pow<2>(s.calculate_norm(*_residual_vector, _soln_vars[i], _norm_type));
316 {
317 mooseAssert(_scaling_factors[i], "Scaling factor must not be zero");
318 resid /= Utility::pow<2>(_scaling_factors[i]);
319 }
320 _group_resid[group] += resid;
321
322 // Prepare reference residual. If local norm, this is actually the ratio of the residual
323 // dividied by the reference at all DOF
324 Real ref_resid;
325 if (_local_norm)
326 {
327 mooseAssert((*_residual_vector).size() == (*_reference_vector).size(),
328 "Sizes of nonlinear RHS and reference vector should be the same.");
329 mooseAssert((*_reference_vector).size(), "Reference vector must be provided.");
330 auto ref = _reference_vector->clone();
331 // Add a tiny number to the reference to prevent a divide by zero.
332 ref->add(std::numeric_limits<Number>::min());
333 auto div = (*_residual_vector).clone();
334 *div /= *ref;
335 ref_resid = Utility::pow<2>(s.calculate_norm(*div, _soln_vars[i], _norm_type));
336 }
337 else
338 {
339 ref_resid =
340 Utility::pow<2>(s.calculate_norm(*_reference_vector, _soln_vars[i], _norm_type));
342 ref_resid /= Utility::pow<2>(_scaling_factors[i]);
343 }
344 _group_ref_resid[group] += ref_resid;
345 }
346 }
347
348 for (const auto i : index_range(_group_resid))
349 {
350 _group_resid[i] = std::sqrt(_group_resid[i]);
351 _group_ref_resid[i] = std::sqrt(_group_ref_resid[i]);
352 }
353}
354
355void
357{
358 // If no refernce_vector is provided, just revert to DefaultNonlinearConvergence behavior
360 return;
361
363
364 std::ostringstream out;
365 out << _name << ": " << _norm_type_enum << " Reference Residual check\n";
366
367 if (_group_names.size() > 0)
368 {
369 // Set residual and references so that they always have a spacing of 8
370 out << std::setprecision(2) << std::scientific;
371 unsigned int var_space = 0;
372 for (const auto i : index_range(_group_names))
373 if (_group_names[i].size() > var_space)
374 var_space = _group_names[i].size();
375
376 for (const auto i : index_range(_group_names))
377 {
378 if (_converge_on_group[i])
379 {
380 // Print residual
381 out << " " << std::setw(var_space + 8) << std::right << _group_names[i] + "-> res: "
382 << (_group_resid[i] < _nl_abs_tol ? COLOR_YELLOW : COLOR_DEFAULT) << std::setw(8)
383 << _group_resid[i] << COLOR_DEFAULT;
384
385 // Print res/ref ratio
386 if (_local_norm)
387 out << " local res/ref: "
388 << (_group_resid[i] / _group_ref_resid[i] < _nl_rel_tol ? COLOR_GREEN : COLOR_DEFAULT)
389 << std::setw(8) << _group_ref_resid[i] << COLOR_DEFAULT << "\n";
390 else
391 {
392 // Print reference first if not local norm
393 out << " ref: " << std::setw(8) << _group_ref_resid[i] << " res/ref: ";
394
395 if (!_group_ref_resid[i])
396 out << _group_resid[i] << "\n";
397 else
398 out << (_group_resid[i] / _group_ref_resid[i] < _nl_rel_tol ? COLOR_GREEN
399 : COLOR_DEFAULT)
400 << std::setw(8) << _group_resid[i] / _group_ref_resid[i] << COLOR_DEFAULT << "\n";
401 }
402 }
403 }
404 _console << out.str() << std::flush;
405 }
406}
407
408bool
410 const Real /*fnorm*/,
411 const Real abs_tol,
412 const Real rel_tol,
413 const Real /*initial_residual_before_preset_bcs*/)
414{
415 // Convergence is checked via:
416 // 1) Ratio of group residual to reference is less than relative tolerance
417 // 2) if group residual is less than absolute tolerance
418 // 3) if group reference residual is zero and:
419 // 3.1) Convergence type is ZERO_TOLERANCE and group residual is zero (rare, but possible, and
420 // historically implemented that way)
421 // 3.2) Convergence type is RELATIVE_TOLERANCE and group residual
422 // is less than relative tolerance. (i.e., using the relative tolerance to check group
423 // convergence in an absolute way)
424
425 bool convergedRelative = true;
426 for (const auto i : index_range(_group_resid))
427 convergedRelative &=
428 ((!_local_norm && _group_resid[i] < _group_ref_resid[i] * rel_tol) ||
429 (_local_norm && _group_ref_resid[i] < rel_tol) || _group_resid[i] < abs_tol ||
430 (!_group_ref_resid[i] && !_local_norm &&
433 _group_resid[i] <= rel_tol))));
434 return convergedRelative;
435}
436
437bool
439 const Real fnorm,
440 const Real ref_norm,
441 const Real rel_tol,
442 const Real abs_tol,
443 std::ostringstream & oss)
444{
445 // If no refernce_vector is provided, just revert to DefaultNonlinearConvergence behavior
448 n_iter, fnorm, ref_norm, rel_tol, abs_tol, oss);
449
450 if (checkConvergenceIndividVars(fnorm, abs_tol, rel_tol, ref_norm))
451 {
452 oss << "Converged normally";
453 return true;
454 }
455 else if (n_iter >= _accept_iters &&
457 fnorm, abs_tol * _accept_mult, rel_tol * _accept_mult, ref_norm))
458 {
459 oss << " Converged due a larger acceptable tolerance due to `acceptible_multiplier` after "
460 "`acceptible_iterations`.";
461 _console << " Converged due to ACCEPTABLE tolerances" << std::endl;
462 return true;
463 }
464
465 return false;
466}
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.
Default nonlinear convergence criteria for FEProblem.
virtual void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job.
Real _nl_rel_tol
Nonlinear relative tolerance (modifiable by setRelativeTolerance)
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.
Real _nl_abs_tol
Nonlinear absolute tolerance (modifiable by setAbsoluteTolerance)
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:195
virtual NumericVector< Number > & getVector(const std::string &name)
Get a raw NumericVector by name.
Definition SystemBase.C:933
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:995
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...