www.mooseframework.org
ADSingleVariableReturnMappingSolution.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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 
11 
12 #include "Moose.h"
13 #include "MooseEnum.h"
14 #include "MooseObject.h"
15 #include "ConsoleStreamInterface.h"
16 #include "Conversion.h"
17 #include "MathUtils.h"
18 
19 #include "DualRealOps.h"
20 
21 #include "libmesh/auto_ptr.h" // libmesh_make_unique
22 
23 #include <limits>
24 #include <string>
25 #include <cmath>
26 #include <memory>
27 
29 
30 template <ComputeStage compute_stage>
31 InputParameters
33 {
34  InputParameters params = emptyInputParameters();
35  params.addParam<Real>(
36  "relative_tolerance", 1e-8, "Relative convergence tolerance for Newton iteration");
37  params.addParam<Real>(
38  "absolute_tolerance", 1e-11, "Absolute convergence tolerance for Newton iteration");
39  params.addParam<Real>("acceptable_multiplier",
40  10,
41  "Factor applied to relative and absolute "
42  "tolerance for acceptable convergence if "
43  "iterations are no longer making progress");
44 
45  // diagnostic output parameters
46  MooseEnum internal_solve_output_on_enum("never on_error always", "on_error");
47  params.addParam<MooseEnum>("internal_solve_output_on",
48  internal_solve_output_on_enum,
49  "When to output internal Newton solve information");
50  params.addParam<bool>("internal_solve_full_iteration_history",
51  false,
52  "Set true to output full internal Newton iteration history at times "
53  "determined by `internal_solve_output_on`. If false, only a summary is "
54  "output.");
55  params.addParamNamesToGroup("internal_solve_output_on internal_solve_full_iteration_history",
56  "Debug");
57  return params;
58 }
59 
60 template <ComputeStage compute_stage>
62  const InputParameters & parameters)
63  : _check_range(false),
64  _line_search(true),
65  _bracket_solution(true),
66  _internal_solve_output_on(
67  parameters.get<MooseEnum>("internal_solve_output_on").getEnum<InternalSolveOutput>()),
68  _max_its(1000), // Far larger than ever expected to be needed
69  _internal_solve_full_iteration_history(
70  parameters.get<bool>("internal_solve_full_iteration_history")),
71  _relative_tolerance(parameters.get<Real>("relative_tolerance")),
72  _absolute_tolerance(parameters.get<Real>("absolute_tolerance")),
73  _acceptable_multiplier(parameters.get<Real>("acceptable_multiplier")),
74  _num_resids(30),
75  _residual_history(_num_resids, std::numeric_limits<Real>::max()),
76  _iteration(0),
77  _initial_residual(0.0),
78  _residual(0.0),
79  _svrms_name(parameters.get<std::string>("_object_name"))
80 {
81 }
82 
83 template <ComputeStage compute_stage>
84 ADReal
86  const ADReal & /*effective_trial_stress*/) const
87 {
88  return std::numeric_limits<Real>::lowest();
89 }
90 
91 template <ComputeStage compute_stage>
92 ADReal
94  const ADReal & /*effective_trial_stress*/) const
95 {
96  return std::numeric_limits<Real>::max();
97 }
98 
99 template <ComputeStage compute_stage>
100 void
102  const ADReal & effective_trial_stress, ADReal & scalar, const ConsoleStream & console)
103 {
104  // construct the stringstream here only if the debug level is set to ALL
105  std::unique_ptr<std::stringstream> iter_output =
106  (_internal_solve_output_on == InternalSolveOutput::ALWAYS)
107  ? libmesh_make_unique<std::stringstream>()
108  : nullptr;
109 
110  // do the internal solve and capture iteration info during the first round
111  // iff full history output is requested regardless of whether the solve failed or succeeded
112  auto solve_state =
113  internalSolve(effective_trial_stress,
114  scalar,
115  _internal_solve_full_iteration_history ? iter_output.get() : nullptr);
116  if (solve_state != SolveState::SUCCESS &&
117  _internal_solve_output_on != InternalSolveOutput::ALWAYS)
118  {
119  // output suppressed by user, throw immediately
120  if (_internal_solve_output_on == InternalSolveOutput::NEVER)
121  throw MooseException("");
122 
123  // user expects some kind of output, if necessary setup output stream now
124  if (!iter_output)
125  iter_output = libmesh_make_unique<std::stringstream>();
126 
127  // add the appropriate error message to the output
128  switch (solve_state)
129  {
130  case SolveState::NAN_INF:
131  *iter_output << "Encountered inf or nan in material return mapping iterations.\n";
132  break;
133 
134  case SolveState::EXCEEDED_ITERATIONS:
135  *iter_output << "Exceeded maximum iterations in material return mapping iterations.\n";
136  break;
137 
138  default:
139  mooseError("Unhandled solver state");
140  }
141 
142  // if full history output is only requested for failed solves we have to repeat
143  // the solve a second time
144  if (_internal_solve_full_iteration_history)
145  internalSolve(effective_trial_stress, scalar, iter_output.get());
146 
147  // Append summary and throw exception
148  outputIterationSummary(iter_output.get(), _iteration);
149  throw MooseException(iter_output->str());
150  }
151 
152  if (_internal_solve_output_on == InternalSolveOutput::ALWAYS)
153  {
154  // the solve did not fail but the user requested debug output anyways
155  outputIterationSummary(iter_output.get(), _iteration);
156  console << iter_output->str();
157  }
158 }
159 
160 template <ComputeStage compute_stage>
163  const ADReal effective_trial_stress, ADReal & scalar, std::stringstream * iter_output)
164 {
165  scalar = initialGuess(effective_trial_stress);
166  ADReal scalar_old = scalar;
167  ADReal scalar_increment = 0.0;
168  const ADReal min_permissible_scalar = minimumPermissibleValue(effective_trial_stress);
169  const ADReal max_permissible_scalar = maximumPermissibleValue(effective_trial_stress);
170  ADReal scalar_upper_bound = max_permissible_scalar;
171  ADReal scalar_lower_bound = min_permissible_scalar;
172  _iteration = 0;
173 
174  _initial_residual = _residual = computeResidual(effective_trial_stress, scalar);
175 
176  ADReal residual_old = _residual;
177  Real init_resid_sign = MathUtils::sign(MetaPhysicL::raw_value(_residual));
178  Real reference_residual = computeReferenceResidual(effective_trial_stress, scalar);
179 
180  if (converged(_residual, reference_residual))
181  {
182  iterationFinalize(scalar);
183  outputIterationStep(iter_output, effective_trial_stress, scalar, reference_residual);
184  return SolveState::SUCCESS;
185  }
186 
187  _residual_history.assign(_num_resids, std::numeric_limits<Real>::max());
188  _residual_history[0] = MetaPhysicL::raw_value(_residual);
189 
190  while (_iteration < _max_its && !converged(_residual, reference_residual) &&
191  !convergedAcceptable(_iteration, reference_residual))
192  {
193  scalar_increment = -_residual / computeDerivative(effective_trial_stress, scalar);
194  scalar = scalar_old + scalar_increment;
195 
196  if (_check_range)
197  checkPermissibleRange(scalar,
198  scalar_increment,
199  scalar_old,
200  min_permissible_scalar,
201  max_permissible_scalar,
202  iter_output);
203 
204  _residual = computeResidual(effective_trial_stress, scalar);
205  reference_residual = computeReferenceResidual(effective_trial_stress, scalar);
206  iterationFinalize(scalar);
207 
208  if (_bracket_solution)
209  updateBounds(
210  scalar, _residual, init_resid_sign, scalar_upper_bound, scalar_lower_bound, iter_output);
211 
212  if (converged(_residual, reference_residual))
213  {
214  outputIterationStep(iter_output, effective_trial_stress, scalar, reference_residual);
215  break;
216  }
217  else
218  {
219  bool modified_increment = false;
220 
221  // Line Search
222  if (_line_search)
223  {
224  if (residual_old - _residual != 0.0)
225  {
226  ADReal alpha = residual_old / (residual_old - _residual);
227  alpha = MathUtils::clamp(alpha, 1.0e-2, 1.0);
228 
229  if (alpha != 1.0)
230  {
231  modified_increment = true;
232  scalar_increment *= alpha;
233  if (iter_output)
234  *iter_output << " Line search alpha = " << MetaPhysicL::raw_value(alpha)
235  << " increment = " << MetaPhysicL::raw_value(scalar_increment)
236  << std::endl;
237  }
238  }
239  }
240 
241  if (_bracket_solution)
242  {
243  // Check to see whether trial scalar_increment is outside the bounds, and set it to a point
244  // within the bounds if it is
245  if (scalar_old + scalar_increment >= scalar_upper_bound ||
246  scalar_old + scalar_increment <= scalar_lower_bound)
247  {
248  if (scalar_upper_bound != max_permissible_scalar &&
249  scalar_lower_bound != min_permissible_scalar)
250  {
251  const Real frac = 0.5;
252  scalar_increment =
253  (1.0 - frac) * scalar_lower_bound + frac * scalar_upper_bound - scalar_old;
254  modified_increment = true;
255  if (iter_output)
256  *iter_output << " Trial scalar_increment exceeded bounds. Setting between "
257  "lower/upper bounds. frac: "
258  << frac << std::endl;
259  }
260  }
261  }
262 
263  // Update the trial scalar and recompute residual if the line search or bounds checking
264  // modified the increment
265  if (modified_increment)
266  {
267  scalar = scalar_old + scalar_increment;
268  _residual = computeResidual(effective_trial_stress, scalar);
269  reference_residual = computeReferenceResidual(effective_trial_stress, scalar);
270  iterationFinalize(scalar);
271 
272  if (_bracket_solution)
273  updateBounds(scalar,
274  _residual,
275  init_resid_sign,
276  scalar_upper_bound,
277  scalar_lower_bound,
278  iter_output);
279  }
280  }
281 
282  outputIterationStep(iter_output, effective_trial_stress, scalar, reference_residual);
283 
284  ++_iteration;
285  residual_old = _residual;
286  scalar_old = scalar;
287  _residual_history[_iteration % _num_resids] = MetaPhysicL::raw_value(_residual);
288  }
289 
290  if (std::isnan(_residual) || std::isinf(MetaPhysicL::raw_value(_residual)))
291  return SolveState::NAN_INF;
292 
293  if (_iteration == _max_its)
294  return SolveState::EXCEEDED_ITERATIONS;
295 
296  return SolveState::SUCCESS;
297 }
298 
299 template <ComputeStage compute_stage>
300 bool
302  const Real reference)
303 {
304  const Real residual = MetaPhysicL::raw_value(ad_residual);
305  return (std::abs(residual) <= _absolute_tolerance ||
306  std::abs(residual / reference) <= _relative_tolerance);
307 }
308 
309 template <ComputeStage compute_stage>
310 bool
312  const Real reference)
313 {
314  // Require that we have at least done _num_resids evaluations before we allow for
315  // acceptable convergence
316  if (it < _num_resids)
317  return false;
318 
319  // Check to see whether the residual has dropped by convergence_history_factor over
320  // the last _num_resids iterations. If it has (which means it's still making progress),
321  // don't consider it to be converged within the acceptable limits.
322  const Real convergence_history_factor = 10.0;
323  if (std::abs(_residual * convergence_history_factor) <
324  std::abs(_residual_history[(it + 1) % _num_resids]))
325  return false;
326 
327  // Now that it's determined that progress is not being made, treat it as converged if
328  // we're within the acceptable convergence limits
329  return converged(_residual / _acceptable_multiplier, reference);
330 }
331 
332 template <ComputeStage compute_stage>
333 void
335  ADReal & scalar,
336  ADReal & scalar_increment,
337  const ADReal & scalar_old,
338  const ADReal min_permissible_scalar,
339  const ADReal max_permissible_scalar,
340  std::stringstream * iter_output)
341 {
342  if (scalar > max_permissible_scalar)
343  {
344  scalar_increment = (max_permissible_scalar - scalar_old) / 2.0;
345  scalar = scalar_old + scalar_increment;
346  if (iter_output)
347  *iter_output << "Scalar greater than maximum ("
348  << MetaPhysicL::raw_value(max_permissible_scalar)
349  << ") adjusted scalar=" << MetaPhysicL::raw_value(scalar)
350  << " scalar_increment=" << MetaPhysicL::raw_value(scalar_increment) << std::endl;
351  }
352  else if (scalar < min_permissible_scalar)
353  {
354  scalar_increment = (min_permissible_scalar - scalar_old) / 2.0;
355  scalar = scalar_old + scalar_increment;
356  if (iter_output)
357  *iter_output << "Scalar less than minimum (" << MetaPhysicL::raw_value(min_permissible_scalar)
358  << ") adjusted scalar=" << MetaPhysicL::raw_value(scalar)
359  << " scalar_increment=" << MetaPhysicL::raw_value(scalar_increment) << std::endl;
360  }
361 }
362 
363 template <ComputeStage compute_stage>
364 void
366  const ADReal & residual,
367  const Real init_resid_sign,
368  ADReal & scalar_upper_bound,
369  ADReal & scalar_lower_bound,
370  std::stringstream * iter_output)
371 {
372  // Update upper/lower bounds as applicable
373  if (residual * init_resid_sign < 0.0 && scalar < scalar_upper_bound)
374  {
375  scalar_upper_bound = scalar;
376  if (scalar_upper_bound < scalar_lower_bound)
377  {
378  scalar_upper_bound = scalar_lower_bound;
379  scalar_lower_bound = 0.0;
380  if (iter_output)
381  *iter_output << " Corrected for scalar_upper_bound < scalar_lower_bound" << std::endl;
382  }
383  }
384  // Don't permit setting scalar_lower_bound > scalar_upper_bound (but do permit the reverse).
385  // This ensures that if we encounter multiple roots, we pick the lowest one.
386  else if (residual * init_resid_sign > 0.0 && scalar > scalar_lower_bound &&
387  scalar < scalar_upper_bound)
388  scalar_lower_bound = scalar;
389 }
390 
391 template <ComputeStage compute_stage>
392 void
394  std::stringstream * iter_output,
395  const ADReal & effective_trial_stress,
396  const ADReal & scalar,
397  const Real reference_residual)
398 {
399  if (iter_output)
400  {
401  const unsigned int it = _iteration;
402  const Real residual = MetaPhysicL::raw_value(_residual);
403 
404  *iter_output << " iteration=" << it
405  << " trial_stress=" << MetaPhysicL::raw_value(effective_trial_stress)
406  << " scalar=" << MetaPhysicL::raw_value(scalar) << " residual=" << residual
407  << " ref_res=" << reference_residual
408  << " rel_res=" << std::abs(residual) / reference_residual
409  << " rel_tol=" << _relative_tolerance << " abs_res=" << std::abs(residual)
410  << " abs_tol=" << _absolute_tolerance << '\n';
411  }
412 }
413 
414 template <ComputeStage compute_stage>
415 void
417  std::stringstream * iter_output, const unsigned int total_it)
418 {
419  if (iter_output)
420  *iter_output << "In " << total_it << " iterations the residual went from "
421  << MetaPhysicL::raw_value(_initial_residual) << " to "
422  << MetaPhysicL::raw_value(_residual) << " in '" << _svrms_name << "'.\n";
423 }
424 
425 // explicit instantiation is required for AD base classes
ADSingleVariableReturnMappingSolution::returnMappingSolve
void returnMappingSolve(const ADReal &effective_trial_stress, ADReal &scalar, const ConsoleStream &console)
Perform the return mapping iterations.
Definition: ADSingleVariableReturnMappingSolution.C:101
adBaseClass
adBaseClass(ADSingleVariableReturnMappingSolution)
ADSingleVariableReturnMappingSolution::outputIterationStep
virtual void outputIterationStep(std::stringstream *iter_output, const ADReal &effective_trial_stress, const ADReal &scalar, const Real reference_residual)
Output information for a single iteration step to build the convergence history of the model.
Definition: ADSingleVariableReturnMappingSolution.C:393
ADSingleVariableReturnMappingSolution.h
ADSingleVariableReturnMappingSolution::convergedAcceptable
bool convergedAcceptable(const unsigned int it, const Real reference)
Check to see whether the residual is within acceptable convergence limits.
Definition: ADSingleVariableReturnMappingSolution.C:311
ADSingleVariableReturnMappingSolution::maximumPermissibleValue
virtual ADReal maximumPermissibleValue(const ADReal &effective_trial_stress) const
Compute the maximum permissible value of the scalar.
Definition: ADSingleVariableReturnMappingSolution.C:93
ADSingleVariableReturnMappingSolution::validParams
static InputParameters validParams()
Definition: ADSingleVariableReturnMappingSolution.C:32
ADSingleVariableReturnMappingSolution::converged
bool converged(const ADReal &residual, const Real reference)
Check to see whether the residual is within the convergence limits.
Definition: ADSingleVariableReturnMappingSolution.C:301
ADSingleVariableReturnMappingSolution::updateBounds
void updateBounds(const ADReal &scalar, const ADReal &residual, const Real init_resid_sign, ADReal &scalar_upper_bound, ADReal &scalar_lower_bound, std::stringstream *iter_output)
Update the upper and lower bounds of the root for the effective inelastic strain.
Definition: ADSingleVariableReturnMappingSolution.C:365
ADSingleVariableReturnMappingSolution::checkPermissibleRange
void checkPermissibleRange(ADReal &scalar, ADReal &scalar_increment, const ADReal &scalar_old, const ADReal min_permissible_scalar, const ADReal max_permissible_scalar, std::stringstream *iter_output)
Check to see whether solution is within admissible range, and set it within that range if it is not.
Definition: ADSingleVariableReturnMappingSolution.C:334
defineADLegacyParams
defineADLegacyParams(ADSingleVariableReturnMappingSolution)
ADSingleVariableReturnMappingSolution::minimumPermissibleValue
virtual ADReal minimumPermissibleValue(const ADReal &effective_trial_stress) const
Compute the minimum permissible value of the scalar.
Definition: ADSingleVariableReturnMappingSolution.C:85
ADSingleVariableReturnMappingSolution::ADSingleVariableReturnMappingSolution
ADSingleVariableReturnMappingSolution(const InputParameters &parameters)
Definition: ADSingleVariableReturnMappingSolution.C:61
ADSingleVariableReturnMappingSolution::InternalSolveOutput
InternalSolveOutput
Definition: ADSingleVariableReturnMappingSolution.h:156
ADSingleVariableReturnMappingSolution::SolveState
SolveState
Definition: ADSingleVariableReturnMappingSolution.h:163
ADSingleVariableReturnMappingSolution::outputIterationSummary
virtual void outputIterationSummary(std::stringstream *iter_output, const unsigned int total_it)
Output summary information for the convergence history of the model.
Definition: ADSingleVariableReturnMappingSolution.C:416
ADSingleVariableReturnMappingSolution::internalSolve
SolveState internalSolve(const ADReal effective_trial_stress, ADReal &scalar, std::stringstream *iter_output=nullptr)
Method called from within this class to perform the actual return mappping iterations.
Definition: ADSingleVariableReturnMappingSolution.C:162
ADSingleVariableReturnMappingSolution
Base class that provides capability for Newton return mapping iterations on a single variable.
Definition: ADSingleVariableReturnMappingSolution.h:32