https://mooseframework.inl.gov
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 
33  params.addClassDescription(
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"));
65  &_fe_problem.getNonlinearSystemBase(_nl_sys_num).getVector(residual_vector_tag_id);
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 
115 {
117 }
118 
119 void
121 {
123  // If no refernce_vector is provided, just revert to DefaultNonlinearConvergence behavior
124  if (!_reference_vector)
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 '",
198  _soln_var_names[i],
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 '",
223  _soln_var_names[i],
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 
294 void
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.
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  {
356  }
357 }
358 
359 void
361 {
362  // If no refernce_vector is provided, just revert to DefaultNonlinearConvergence behavior
363  if (!_reference_vector)
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 
411 bool
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 
440 bool
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
449  if (!_reference_vector)
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 }
virtual TagID getVectorTagID(const TagName &tag_name) const
Get a TagID from a TagName.
Definition: SubProblem.C:204
virtual void nonlinearConvergenceSetup() override
Performs setup necessary for each call to checkConvergence.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:40
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.
const std::string & _name
The name of this class.
Definition: MooseBase.h:381
Interface class shared between ReferenceResidualProblem and ReferenceResidualConvergence.
PetscReal _abs_tol
Nonlinear absolute tolerance.
virtual void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job...
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
virtual std::size_t numNonlinearSystems() const override
std::vector< NonlinearVariableName > _group_names
std::vector< NonlinearVariableName > _soln_var_names
std::vector< std::vector< NonlinearVariableName > > _group_variables
Name of variables that are grouped together to check convergence.
ReferenceResidualConvergence(const InputParameters &parameters)
const MooseEnum _norm_type_enum
Enum holding the normalization type.
const InputParameters & parameters() const
Get the parameters of the object.
Definition: MooseBase.h:131
const unsigned int _nl_sys_num
Nonlinear system to which this convergence object applies.
std::vector< bool > _converge_on_var
Flag for each solution variable or group being in &#39;converge_on&#39;.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
virtual std::unique_ptr< NumericVector< Number > > clone() const =0
const NumericVector< Number > * _reference_vector
The vector storing the reference residual values.
void updateReferenceResidual()
Computes the reference residuals for each group.
static InputParameters validParams()
Nonlinear system to be solved.
registerMooseObject("MooseApp", ReferenceResidualConvergence)
bool _use_group_variables
True if any variables are grouped.
std::vector< unsigned int > _soln_vars
TagID _reference_vector_tag_id
The reference vector tag id.
const bool _unscale_the_residual
Bool to unscale the residual before convergence checks and screen output.
void mooseDeprecated(Args &&... args) const
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:54
bool _local_norm
Flag to optionally perform normalization of residual by reference residual before or after L2 norm is...
NonlinearSystemBase & getNonlinearSystemBase(const unsigned int sys_num)
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&#39;s residual separately against its refere...
virtual void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job...
Uses a reference residual to define relative convergence criteria.
const NumericVector< Number > * _residual_vector
The optional vector storing the reference residual values.
virtual NumericVector< Number > & RHS()=0
libMesh::FEMNormType _norm_type
Container for normalization type.
static InputParameters validParams()
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
enum ReferenceResidualConvergence::ZeroReferenceType _zero_ref_type
const TagID INVALID_TAG_ID
Definition: MooseTypes.C:23
OStreamProxy out
bool globCompare(const std::string &candidate, const std::string &pattern, std::size_t c, std::size_t p)
Definition: MooseUtils.C:943
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
Definition: MooseBase.h:271
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...
virtual NonlinearSystemBase & nonlinearSystem() override
Nonlinear system whose convergence state should be checked.
std::vector< unsigned int > _group_index
Group number index for each variable.
std::vector< bool > _is_var_grouped
Vector of bools to signify if variable is in a group.
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
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.
std::vector< Real > _scaling_factors
Local storage for the scaling factors applied to each of the variables to apply to _ref_resid_vars...
void paramWarning(const std::string &param, Args... args) const
auto min(const L &left, const R &right)
virtual NumericVector< Number > & getVector(const std::string &name)
Get a raw NumericVector by name.
Definition: SystemBase.C:934
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
Default nonlinear convergence criteria for FEProblem.
void ErrorVector unsigned int
auto index_range(const T &sizable)
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template pow< 2 >(tan(_arg))+1.0) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(sqrt
PetscReal _rel_tol
Nonlinear relative tolerance.
ZeroReferenceType
Container for convergence treatment when the reference residual is zero.
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another, i.e.