LCOV - code coverage report
Current view: top level - src/materials - SingleVariableReturnMappingSolution.C (source / functions) Hit Total Coverage
Test: idaholab/moose tensor_mechanics: d6b47a Lines: 183 193 94.8 %
Date: 2024-02-27 11:53:14 Functions: 22 26 84.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14