Loading [MathJax]/extensions/tex2jax.js
https://mooseframework.inl.gov
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends
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  _reference_vector(nullptr),
44  _converge_on(getParam<std::vector<NonlinearVariableName>>("converge_on")),
45  _zero_ref_type(
46  getParam<MooseEnum>("zero_reference_residual_treatment").getEnum<ZeroReferenceType>()),
47  _reference_vector_tag_id(Moose::INVALID_TAG_ID),
48  _initialized(false)
49 {
50  if (parameters.isParamValid("solution_variables"))
51  {
52  if (parameters.isParamValid("reference_vector"))
53  mooseDeprecated("The `solution_variables` parameter is deprecated, has no effect when "
54  "the tagging system is used, and will be removed on January 1, 2020. "
55  "Please simply delete this parameter from your input file.");
56  _soln_var_names = parameters.get<std::vector<NonlinearVariableName>>("solution_variables");
57  }
58 
59  if (parameters.isParamValid("reference_residual_variables") &&
60  parameters.isParamValid("reference_vector"))
61  mooseError(
62  "You may specify either the `reference_residual_variables` "
63  "or `reference_vector` parameter, not both. `reference_residual_variables` is deprecated "
64  "so we recommend using `reference_vector`");
65 
66  if (parameters.isParamValid("reference_residual_variables"))
67  {
69  "The save-in method for composing reference residual quantities is deprecated "
70  "and will be removed on January 1, 2020. Please use the tagging system instead; "
71  "specifically, please assign a TagName to the `reference_vector` parameter");
72 
74  parameters.get<std::vector<AuxVariableName>>("reference_residual_variables");
75 
76  if (_soln_var_names.size() != _ref_resid_var_names.size())
77  mooseError("Size of solution_variables (",
78  _soln_var_names.size(),
79  ") != size of reference_residual_variables (",
80  _ref_resid_var_names.size(),
81  ")");
82  }
83  else if (parameters.isParamValid("reference_vector"))
84  {
86  paramError(
87  "nl_sys_names",
88  "reference residual problem does not currently support multiple nonlinear systems");
89  _reference_vector_tag_id = _fe_problem.getVectorTagID(getParam<TagName>("reference_vector"));
91  }
92  else
93  mooseInfo("Neither the `reference_residual_variables` nor `reference_vector` parameter is "
94  "specified, which means that no reference "
95  "quantites are set. Because of this, the standard technique of comparing the "
96  "norm of the full residual vector with its initial value will be used.");
97 
98  _accept_mult = parameters.get<Real>("acceptable_multiplier");
99  _accept_iters = parameters.get<unsigned int>("acceptable_iterations");
100 
101  const auto norm_type_enum =
102  parameters.get<MooseEnum>("normalization_type").getEnum<NormalizationType>();
103  if (norm_type_enum == NormalizationType::LOCAL_L2)
104  {
106  _local_norm = true;
107  }
108  else if (norm_type_enum == NormalizationType::GLOBAL_L2)
109  {
111  _local_norm = false;
112  }
113  else if (norm_type_enum == NormalizationType::LOCAL_LINF)
114  {
116  _local_norm = true;
117  }
118  else if (norm_type_enum == NormalizationType::GLOBAL_LINF)
119  {
121  _local_norm = false;
122  }
123  else
124  {
125  mooseAssert(false, "This point should not be reached.");
126  }
127 
128  if (_local_norm && !parameters.isParamValid("reference_vector"))
129  paramError("reference_vector", "If local norm is used, a reference_vector must be provided.");
130 }
131 
132 void
134 {
136 
137  NonlinearSystemBase & nonlinear_sys = _fe_problem.getNonlinearSystemBase(/*nl_sys=*/0);
139  System & s = nonlinear_sys.system();
140  auto & as = aux_sys.sys();
141 
142  if (_soln_var_names.empty())
143  {
144  // If the user provides reference_vector, that implies that they want the
145  // individual variables compared against their reference quantities in the
146  // tag vector. The code depends on having _soln_var_names populated,
147  // so fill that out if they didn't specify solution_variables.
148  if (_reference_vector)
149  for (unsigned int var_num = 0; var_num < s.n_vars(); var_num++)
150  _soln_var_names.push_back(s.variable_name(var_num));
151 
152  // If they didn't provide reference_vector, that implies that they
153  // want to skip the individual variable comparison, so leave it alone.
154  }
155  else if (_soln_var_names.size() != s.n_vars())
156  mooseError("Size of solution_variables (",
157  _soln_var_names.size(),
158  ") != number of variables in system (",
159  s.n_vars(),
160  ")");
161 
162  const auto n_soln_vars = _soln_var_names.size();
163  _variable_group_num_index.resize(n_soln_vars);
164 
165  if (!_converge_on.empty())
166  {
167  _converge_on_var.assign(n_soln_vars, false);
168  for (std::size_t i = 0; i < n_soln_vars; ++i)
169  for (const auto & c : _converge_on)
171  {
172  _converge_on_var[i] = true;
173  break;
174  }
175  }
176  else
177  _converge_on_var.assign(n_soln_vars, true);
178 
179  unsigned int group_variable_num = 0;
181  {
182  for (unsigned int i = 0; i < _group_variables.size(); ++i)
183  {
184  group_variable_num += _group_variables[i].size();
185  if (_group_variables[i].size() == 1)
186  mooseError(" In the 'group_variables' parameter, variable ",
187  _group_variables[i][0],
188  " is not grouped with other variables.");
189  }
190 
191  unsigned int size = n_soln_vars - group_variable_num + _group_variables.size();
192  _group_ref_resid.resize(size);
193  _group_resid.resize(size);
194  _group_output_resid.resize(size);
195  _group_soln_var_names.resize(size);
196  _group_ref_resid_var_names.resize(size);
197  }
198  // If not using groups, use one group for each variable
199  else
200  {
201  _group_ref_resid.resize(n_soln_vars);
202  _group_resid.resize(n_soln_vars);
203  _group_output_resid.resize(n_soln_vars);
204  _group_soln_var_names.resize(n_soln_vars);
205  _group_ref_resid_var_names.resize(n_soln_vars);
206  }
207 
208  std::set<std::string> check_duplicate;
210  {
211  for (unsigned int i = 0; i < _group_variables.size(); ++i)
212  for (unsigned int j = 0; j < _group_variables[i].size(); ++j)
213  check_duplicate.insert(_group_variables[i][j]);
214 
215  if (check_duplicate.size() != group_variable_num)
216  mooseError(
217  "A variable cannot be included in multiple groups in the 'group_variables' parameter.");
218  }
219 
220  _soln_vars.clear();
221  for (unsigned int i = 0; i < n_soln_vars; ++i)
222  {
223  bool found_match = false;
224  for (unsigned int var_num = 0; var_num < s.n_vars(); var_num++)
225  if (_soln_var_names[i] == s.variable_name(var_num))
226  {
227  _soln_vars.push_back(var_num);
228  found_match = true;
229  break;
230  }
231 
232  if (!found_match)
233  mooseError("Could not find solution variable '",
234  _soln_var_names[i],
235  "' in system '",
236  s.name(),
237  "'.");
238  }
239 
240  if (!_reference_vector)
241  {
242  _ref_resid_vars.clear();
243  for (unsigned int i = 0; i < _ref_resid_var_names.size(); ++i)
244  {
245  bool foundMatch = false;
246  for (unsigned int var_num = 0; var_num < as.n_vars(); var_num++)
247  if (_ref_resid_var_names[i] == as.variable_name(var_num))
248  {
249  _ref_resid_vars.push_back(var_num);
250  foundMatch = true;
251  break;
252  }
253 
254  if (!foundMatch)
255  mooseError("Could not find variable '", _ref_resid_var_names[i], "' in auxiliary system");
256  }
257  }
258 
259  unsigned int ungroup_index = 0;
261  ungroup_index = _group_variables.size();
262 
263  for (const auto i : index_range(_soln_vars))
264  {
265  bool find_group = false;
267  {
268  for (unsigned int j = 0; j < _group_variables.size(); ++j)
269  if (std::find(_group_variables[j].begin(),
270  _group_variables[j].end(),
271  s.variable_name(_soln_vars[i])) != _group_variables[j].end())
272  {
273  if (!_converge_on_var[i])
274  paramError("converge_on",
275  "You added variable '",
276  _soln_var_names[i],
277  "' to a group but excluded it from the convergence check. This is not "
278  "permitted.");
279 
281  find_group = true;
282  break;
283  }
284 
285  if (!find_group)
286  {
287  _variable_group_num_index[i] = ungroup_index;
288  ungroup_index++;
289  }
290  }
291  else
293  }
294 
296  {
297  // Check for variable groups containing both field and scalar variables
298  for (unsigned int i = 0; i < _group_variables.size(); ++i)
299  {
300  unsigned int num_scalar_vars = 0;
301  unsigned int num_field_vars = 0;
302  if (_group_variables[i].size() > 1)
303  {
304  for (unsigned int j = 0; j < _group_variables[i].size(); ++j)
305  for (unsigned int var_num = 0; var_num < s.n_vars(); var_num++)
306  if (_group_variables[i][j] == s.variable_name(var_num))
307  {
308  if (nonlinear_sys.isScalarVariable(_soln_vars[var_num]))
309  ++num_scalar_vars;
310  else
311  ++num_field_vars;
312  break;
313  }
314  }
315  if (num_scalar_vars > 0 && num_field_vars > 0)
316  mooseWarning("In the 'group_variables' parameter, standard variables and scalar variables "
317  "are grouped together in group ",
318  i);
319  }
320  }
321 
322  // Keep track of the names of the variables in each group (of 1 variable if not using groups)
323  for (unsigned int i = 0; i < n_soln_vars; ++i)
324  {
326  {
330  }
331 
333  {
337  }
338  }
339 
340  if (!_reference_vector)
341  {
342  const unsigned int size_soln_vars = _soln_vars.size();
343  _scaling_factors.resize(size_soln_vars);
344  for (unsigned int i = 0; i < size_soln_vars; ++i)
345  if (nonlinear_sys.isScalarVariable(_soln_vars[i]))
346  _scaling_factors[i] = nonlinear_sys.getScalarVariable(0, _soln_vars[i]).scalingFactor();
347  else
348  _scaling_factors[i] = nonlinear_sys.getVariable(/*tid*/ 0, _soln_vars[i]).scalingFactor();
349  }
350  _initialized = true;
351 }
352 
353 void
355 {
358  System & s = _current_nl_sys.system();
359  auto & as = aux_sys.sys();
360 
361  std::fill(_group_resid.begin(), _group_resid.end(), 0.0);
362  std::fill(_group_output_resid.begin(), _group_output_resid.end(), 0.0);
363  if (_local_norm)
364  std::fill(_group_ref_resid.begin(), _group_ref_resid.end(), 1.0);
365  else
366  std::fill(_group_ref_resid.begin(), _group_ref_resid.end(), 0.0);
367 
368  for (const auto i : index_range(_soln_vars))
369  {
370  Real resid = 0.0;
371  const auto group = _variable_group_num_index[i];
372  if (_local_norm)
373  {
374  mooseAssert(_current_nl_sys.RHS().size() == (*_reference_vector).size(),
375  "Sizes of nonlinear RHS and reference vector should be the same.");
376  mooseAssert((*_reference_vector).size(), "Reference vector must be provided.");
377  // Add a tiny number to the reference to prevent a divide by zero.
378  auto ref = _reference_vector->clone();
380  auto div = _current_nl_sys.RHS().clone();
381  *div /= *ref;
382  resid = Utility::pow<2>(s.calculate_norm(*div, _soln_vars[i], _norm_type));
383  }
384  else
385  {
386  resid = Utility::pow<2>(s.calculate_norm(_current_nl_sys.RHS(), _soln_vars[i], _norm_type));
387  if (_reference_vector)
388  {
389  const auto ref_resid = s.calculate_norm(*_reference_vector, _soln_vars[i], _norm_type);
390  _group_ref_resid[group] += Utility::pow<2>(ref_resid);
391  }
392  }
393 
394  _group_resid[group] += _converge_on_var[i] ? resid : 0;
395  _group_output_resid[group] += resid;
396  }
397 
398  if (!_reference_vector)
399  {
400  for (unsigned int i = 0; i < _ref_resid_vars.size(); ++i)
401  {
402  const auto ref_resid =
403  as.calculate_norm(*as.current_local_solution, _ref_resid_vars[i], _norm_type) *
404  _scaling_factors[i];
406  }
407  }
408 
409  for (unsigned int i = 0; i < _group_resid.size(); ++i)
410  {
414  }
415 }
416 
417 void
419 {
420  if (!_initialized)
421  initialSetup();
422 
424 
425  std::ostringstream out;
426 
427  if (_group_soln_var_names.size() > 0)
428  {
429  out << std::setprecision(2) << std::scientific
430  << " Solution, reference convergence variable norms:\n";
431  unsigned int maxwsv = 0;
432  unsigned int maxwrv = 0;
433  for (unsigned int i = 0; i < _group_soln_var_names.size(); ++i)
434  {
435  if (_group_soln_var_names[i].size() > maxwsv)
436  maxwsv = _group_soln_var_names[i].size();
437  if (!_reference_vector && _group_ref_resid_var_names[i].size() > maxwrv)
438  maxwrv = _group_ref_resid_var_names[i].size();
439  }
440  if (_reference_vector)
441  // maxwrv is the width of maxwsv plus the length of "_ref" (e.g. 4)
442  maxwrv = maxwsv + 4;
443 
444  for (unsigned int i = 0; i < _group_soln_var_names.size(); ++i)
445  {
446  out << " " << std::setw(maxwsv + (_local_norm ? 5 : 2)) << std::left
447  << (_local_norm ? "norm " : "") + _group_soln_var_names[i] + ": ";
448 
449  if (_group_output_resid[i] == _group_resid[i])
450  out << std::setw(8) << _group_output_resid[i];
451  else
452  out << std::setw(8) << _group_resid[i] << " (" << _group_output_resid[i] << ')';
453 
454  if (!_local_norm)
455  {
456  const auto ref_var_name =
458  out << " " << std::setw(maxwrv + 2) << ref_var_name + ":" << std::setw(8)
459  << _group_ref_resid[i] << " (" << std::setw(8)
461  << ")";
462  }
463  out << '\n';
464  }
465  _console << out.str() << std::flush;
466  }
467 }
468 
469 bool
471  const Real fnorm,
472  const Real abstol,
473  const Real rtol,
474  const Real initial_residual_before_preset_bcs)
475 {
476  // Convergence is checked via:
477  // 1) if group residual is less than group reference residual by relative tolerance
478  // 2) if group residual is less than absolute tolerance
479  // 3) if group reference residual is zero and:
480  // 3.1) Convergence type is ZERO_TOLERANCE and group residual is zero (rare, but possible, and
481  // historically implemented that way)
482  // 3.2) Convergence type is RELATIVE_TOLERANCE and group residual
483  // is less than relative tolerance. (i.e., using the relative tolerance to check group
484  // convergence in an absolute way)
485 
486  bool convergedRelative = true;
487  if (_group_resid.size() > 0)
488  {
489  for (unsigned int i = 0; i < _group_resid.size(); ++i)
490  convergedRelative &=
491  (_group_resid[i] < _group_ref_resid[i] * rtol || _group_resid[i] < abstol ||
492  (_group_ref_resid[i] == 0.0 &&
495  _group_resid[i] <= rtol))));
496  }
497 
498  else if (fnorm > initial_residual_before_preset_bcs * rtol)
499  convergedRelative = false;
500 
501  return convergedRelative;
502 }
503 
504 bool
506  const Real fnorm,
507  const Real the_residual,
508  const Real rtol,
509  const Real abstol,
510  std::ostringstream & oss)
511 {
512  if (checkConvergenceIndividVars(fnorm, abstol, rtol, the_residual))
513  {
514  oss << "Converged due to function norm " << fnorm << " < relative tolerance (" << rtol
515  << ") or absolute tolerance (" << abstol << ") for all solution variables\n";
516  return true;
517  }
518  else if (it >= _accept_iters &&
520  fnorm, abstol * _accept_mult, rtol * _accept_mult, the_residual))
521  {
522  oss << "Converged due to function norm " << fnorm << " < acceptable relative tolerance ("
523  << rtol * _accept_mult << ") or acceptable absolute tolerance (" << abstol * _accept_mult
524  << ") for all solution variables\n";
525  _console << "Converged due to ACCEPTABLE tolerances" << std::endl;
526  return true;
527  }
528 
529  return false;
530 }
virtual TagID getVectorTagID(const TagName &tag_name) const
Get a TagID from a TagName.
Definition: SubProblem.C:204
bool globCompare(const std::string &candidate, const std::string &pattern, std::size_t c=0, std::size_t p=0)
Definition: MooseUtils.C:928
virtual void nonlinearConvergenceSetup() override
Performs setup necessary for each call to checkConvergence.
void mooseDeprecated(Args &&... args) const
Interface class shared between ReferenceResidualProblem and ReferenceResidualConvergence.
bool checkConvergenceIndividVars(const Real fnorm, const Real abstol, const Real rtol, 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 std::size_t numNonlinearSystems() const override
std::vector< NonlinearVariableName > _soln_var_names
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
Combine two vector parameters into a single vector of pairs.
void mooseInfo(Args &&... args) const
std::vector< std::vector< NonlinearVariableName > > _group_variables
Name of variables that are grouped together to check convergence.
virtual numeric_index_type size() const =0
ReferenceResidualConvergence(const InputParameters &parameters)
std::vector< bool > _converge_on_var
Flag for each solution variable being in &#39;converge_on&#39;.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
virtual bool checkRelativeConvergence(const unsigned int it, const Real fnorm, const Real the_residual, const Real rtol, const Real abstol, std::ostringstream &oss) override
Check the relative convergence of the nonlinear solution.
std::vector< NonlinearVariableName > _group_soln_var_names
virtual std::unique_ptr< NumericVector< Number > > clone() const =0
const NumericVector< Number > * _reference_vector
The vector storing the reference residual values.
std::vector< unsigned int > _variable_group_num_index
Group number index for each variable.
void updateReferenceResidual()
Computes the reference residuals for each group.
static InputParameters validParams()
void mooseWarning(Args &&... args) const
Emits a warning prefixed with object name and type.
std::vector< AuxVariableName > _ref_resid_var_names
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.
NonlinearSystemBase & currentNonlinearSystem()
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:33
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)
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 ...
std::vector< AuxVariableName > _group_ref_resid_var_names
AuxiliarySystem & getAuxiliarySystem()
std::vector< unsigned int > _ref_resid_vars
virtual void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job...
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.
virtual libMesh::System & sys()
virtual MooseVariableScalar & getScalarVariable(THREAD_ID tid, const std::string &var_name) const
Gets a reference to a scalar variable with specified number.
Definition: SystemBase.C:144
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
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type.
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...
const InputParameters & parameters() const
Get the parameters of the object.
virtual bool isScalarVariable(unsigned int var_name) const
Definition: SystemBase.C:839
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.
MooseVariableFieldBase & getVariable(THREAD_ID tid, const std::string &var_name) const
Gets a reference to a variable of with specified name.
Definition: SystemBase.C:89
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< NonlinearVariableName > _converge_on
Variables to use for individual variable convergence checks.
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:887
Default convergence criteria for FEProblem.
A system that holds auxiliary variables.
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
void scalingFactor(const std::vector< Real > &factor)
Set the scaling factor for this variable.
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.
virtual libMesh::System & system() override
Get the reference to the libMesh system.