LCOV - code coverage report
Current view: top level - src/postprocessors - PseudoTimestep.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 104 107 97.2 %
Date: 2025-08-08 20:01:16 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "PseudoTimestep.h"
      11             : #include "FEProblemBase.h"
      12             : #include "NonlinearSystemBase.h"
      13             : #include "MathUtils.h"
      14             : #include "TransientBase.h"
      15             : #include "Restartable.h"
      16             : #include "libmesh/enum_norm_type.h"
      17             : 
      18             : registerMooseObject("MooseApp", PseudoTimestep);
      19             : 
      20             : InputParameters
      21       14343 : PseudoTimestep::validParams()
      22             : {
      23       14343 :   InputParameters params = GeneralPostprocessor::validParams();
      24             : 
      25             :   /// Enum that is used to select the timestep-selection method.
      26       14343 :   MooseEnum method("SER RDM EXP", "SER");
      27             : 
      28       14343 :   method.addDocumentation("SER", "Switched evolution relaxation");
      29       14343 :   method.addDocumentation("EXP", "Exponential progression");
      30       14343 :   method.addDocumentation("RDM", "Residual difference method");
      31             : 
      32       14343 :   params.addRequiredParam<MooseEnum>(
      33             :       "method", method, "The method used for pseudotimestep timemarching");
      34       14343 :   params.addRequiredRangeCheckedParam<Real>("initial_dt", "initial_dt > 0", "Initial timestep");
      35       14343 :   params.addRequiredRangeCheckedParam<Real>(
      36             :       "alpha", "alpha > 0", "The parameter alpha used in the scaling of the timestep");
      37       14343 :   params.addParam<Real>("max_dt", "The largest timestep allowed");
      38       14343 :   params.addParam<unsigned int>(
      39             :       "iterations_window",
      40             :       "For how many iterations should the residual be tracked (only applies to the SER method)");
      41             :   // Because this post-processor is meant to be used with PostprocessorDT, it
      42             :   // should be executed on initial (not included by default) and timestep end.
      43       43029 :   params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_END, EXEC_INITIAL};
      44       14343 :   params.suppressParameter<ExecFlagEnum>("execute_on");
      45             : 
      46       14343 :   params.addClassDescription("Computes pseudo-time steps for obtaining steady-state solutions "
      47             :                              "through a pseudo transient process.");
      48             : 
      49       28686 :   return params;
      50       28686 : }
      51             : 
      52          39 : PseudoTimestep::PseudoTimestep(const InputParameters & parameters)
      53             :   : GeneralPostprocessor(parameters),
      54          39 :     _method(getParam<MooseEnum>("method").getEnum<PseudotimeMethod>()),
      55          39 :     _initial_dt(getParam<Real>("initial_dt")),
      56          39 :     _alpha(getParam<Real>("alpha")),
      57          39 :     _bound(isParamValid("max_dt")),
      58          39 :     _max_dt(_bound ? getParam<Real>("max_dt") : std::numeric_limits<Real>::max()),
      59          39 :     _residual_norms_sequence(declareRestartableData<std::vector<Real>>("residual_norms_sequence")),
      60          78 :     _iterations_step_sequence(declareRestartableData<std::vector<Real>>("iterations_step_sequence"))
      61             : {
      62          39 :   if (!_fe_problem.isTransient())
      63           0 :     mooseError("This pseudotimestepper can only be used if the steady state is computed via time "
      64             :                "integration.");
      65             : 
      66          39 :   if (_method == PseudotimeMethod::SER)
      67             :   {
      68          13 :     if (parameters.isParamValid("iterations_window"))
      69          13 :       _iterations_window = getParam<unsigned int>("iterations_window");
      70             :     else
      71           0 :       _iterations_window = 1;
      72             :   }
      73          26 :   else if (parameters.isParamValid("iterations_window"))
      74           0 :     mooseError("The iterations window can be only provided for the SER method.");
      75          39 : }
      76             : 
      77             : void
      78         243 : PseudoTimestep::initialize()
      79             : {
      80             :   // start with the max
      81         243 :   _dt = std::numeric_limits<Real>::max();
      82         243 : }
      83             : 
      84             : Real
      85         243 : PseudoTimestep::getValue() const
      86             : {
      87         243 :   return _dt;
      88             : }
      89             : 
      90             : Real
      91         207 : PseudoTimestep::currentResidualNorm() const
      92             : {
      93         207 :   NonlinearSystemBase & nl = _fe_problem.getNonlinearSystemBase(_sys.number());
      94         207 :   Real res_norm = 0.0;
      95         414 :   for (const auto var_num : make_range(nl.system().n_vars()))
      96             :   {
      97             :     auto var_res =
      98         207 :         nl.system().calculate_norm(nl.getResidualNonTimeVector(), var_num, libMesh::DISCRETE_L2);
      99         207 :     res_norm = res_norm + std::pow(var_res, 2);
     100             :   }
     101         207 :   res_norm = std::sqrt(res_norm);
     102         207 :   return res_norm;
     103             : }
     104             : 
     105             : void
     106         207 : PseudoTimestep::outputPseudoTimestep(Real curr_time) const
     107             : {
     108         207 :   unsigned int curr_step = _fe_problem.timeStep();
     109         207 :   Real step_dt = _fe_problem.dt();
     110             : 
     111         207 :   unsigned int res_size = _residual_norms_sequence.size();
     112             : 
     113         207 :   _console << "Current step " << curr_step << " and current dt " << step_dt
     114         207 :            << " for  steady state residual " << _residual_norms_sequence[res_size - 1]
     115         207 :            << " at time " << curr_time << std::endl;
     116         207 : }
     117             : 
     118             : Real
     119          49 : PseudoTimestep::timestepSER() const
     120             : {
     121          49 :   const unsigned int curr_step = _fe_problem.timeStep();
     122             : 
     123          49 :   const unsigned int steps_size = _iterations_step_sequence.size();
     124          49 :   const unsigned int res_size = _residual_norms_sequence.size();
     125          49 :   const unsigned int prev_steps = std::min(_iterations_window + 1, curr_step);
     126             : 
     127         147 :   const Real factor = std::pow(_residual_norms_sequence[res_size - prev_steps] /
     128          49 :                                    _residual_norms_sequence[res_size - 1],
     129          49 :                                _alpha);
     130             : 
     131          49 :   const Real update_dt = _iterations_step_sequence[steps_size - 1] * factor;
     132          49 :   return update_dt;
     133             : }
     134             : 
     135             : Real
     136          73 : PseudoTimestep::timestepRDM() const
     137             : {
     138          73 :   const unsigned int res_size = _residual_norms_sequence.size();
     139          73 :   const unsigned int steps_size = _iterations_step_sequence.size();
     140             : 
     141          73 :   Real exponent = 0.0;
     142             : 
     143          73 :   if (_residual_norms_sequence[res_size - 1] < _residual_norms_sequence[res_size - 2])
     144         146 :     exponent = (_residual_norms_sequence[res_size - 2] - _residual_norms_sequence[res_size - 1]) /
     145          73 :                _residual_norms_sequence[res_size - 2];
     146          73 :   const Real update_dt = _iterations_step_sequence[steps_size - 1] * std::pow(_alpha, exponent);
     147          73 :   return update_dt;
     148             : }
     149             : 
     150             : Real
     151          49 : PseudoTimestep::timestepEXP() const
     152             : {
     153          49 :   const Real factor = MathUtils::pow(_alpha, _fe_problem.timeStep() - 1);
     154             : 
     155          49 :   const Real update_dt = _initial_dt * factor;
     156          49 :   return update_dt;
     157             : }
     158             : 
     159             : void
     160         243 : PseudoTimestep::execute()
     161             : {
     162         243 :   TransientBase * transient = dynamic_cast<TransientBase *>(_app.getExecutioner());
     163             : 
     164             :   Real res_norm;
     165             :   Real curr_dt;
     166             :   Real update_dt;
     167             : 
     168         243 :   if (_current_execute_flag == EXEC_INITIAL)
     169          36 :     _dt = _initial_dt;
     170             : 
     171             :   // at the end of each timestep call the postprocessor to set values for dt
     172         243 :   if (_current_execute_flag == EXEC_TIMESTEP_END)
     173             :   {
     174             :     // This is all in case a timestep fails and needs to be re-done
     175             :     // Otherwise this is a simply a push_back operation for the vectors
     176             :     mooseAssert(_fe_problem.timeStep() >= 1, "Should at least be on the first time step.");
     177         207 :     const std::size_t curr_step = _fe_problem.timeStep();
     178             :     mooseAssert(_residual_norms_sequence.size() <= curr_step &&
     179             :                     curr_step - _residual_norms_sequence.size() <= 1,
     180             :                 "Vector is improper length.");
     181             :     mooseAssert(_residual_norms_sequence.size() == _iterations_step_sequence.size(),
     182             :                 "Vectors should be same length.");
     183         207 :     _residual_norms_sequence.resize(curr_step);
     184         207 :     _iterations_step_sequence.resize(curr_step);
     185             : 
     186         207 :     res_norm = currentResidualNorm();
     187         207 :     _residual_norms_sequence[curr_step - 1] = res_norm;
     188             : 
     189         207 :     curr_dt = transient->getDT();
     190         207 :     _iterations_step_sequence[curr_step - 1] = curr_dt;
     191             : 
     192         207 :     _dt = curr_dt;
     193             : 
     194             :     // since some of the residual methods require previous residuals the pseudo timesteppers
     195             :     // start at the second step in the computation
     196         207 :     if (_fe_problem.timeStep() > 1)
     197             :     {
     198         171 :       update_dt = curr_dt;
     199         171 :       switch (_method)
     200             :       {
     201          49 :         case PseudotimeMethod::SER:
     202          49 :           update_dt = timestepSER();
     203          49 :           break;
     204          49 :         case PseudotimeMethod::EXP:
     205          49 :           update_dt = timestepEXP();
     206          49 :           break;
     207          73 :         case PseudotimeMethod::RDM:
     208          73 :           update_dt = timestepRDM();
     209          73 :           break;
     210             :       }
     211         171 :       if (_bound)
     212          49 :         _dt = std::min(_max_dt, update_dt);
     213             :       else
     214         122 :         _dt = update_dt;
     215             :     }
     216         207 :     outputPseudoTimestep(_fe_problem.time());
     217             :   }
     218         243 : }

Generated by: LCOV version 1.14