LCOV - code coverage report
Current view: top level - src/timeintegrators - ExplicitRK2.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 59 66 89.4 %
Date: 2025-07-17 01:28:37 Functions: 6 8 75.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 "ExplicitRK2.h"
      11             : #include "NonlinearSystem.h"
      12             : #include "FEProblem.h"
      13             : #include "PetscSupport.h"
      14             : 
      15             : using namespace libMesh;
      16             : 
      17             : InputParameters
      18       43368 : ExplicitRK2::validParams()
      19             : {
      20       43368 :   InputParameters params = TimeIntegrator::validParams();
      21             : 
      22       43368 :   return params;
      23             : }
      24             : 
      25         382 : ExplicitRK2::ExplicitRK2(const InputParameters & parameters)
      26             :   : TimeIntegrator(parameters),
      27         382 :     _stage(1),
      28         382 :     _residual_old(addVector("residual_old", false, GHOSTED)),
      29         764 :     _solution_older(_sys.solutionState(2))
      30             : {
      31         382 :   mooseInfo("ExplicitRK2-derived TimeIntegrators (ExplicitMidpoint, Heun, Ralston) and other "
      32             :             "multistage TimeIntegrators are known not to work with "
      33             :             "Materials/AuxKernels that accumulate 'state' and should be used with caution.");
      34         382 : }
      35             : 
      36             : void
      37        2360 : ExplicitRK2::preSolve()
      38             : {
      39        2360 :   if (_dt == _dt_old)
      40        2360 :     _fe_problem.setConstJacobian(true);
      41             :   else
      42           0 :     _fe_problem.setConstJacobian(false);
      43        2360 : }
      44             : 
      45             : void
      46        7944 : ExplicitRK2::computeTimeDerivatives()
      47             : {
      48             :   // Since advanceState() is called in between stages 2 and 3, this
      49             :   // changes the meaning of "_solution_old".  In the second stage,
      50             :   // "_solution_older" is actually the original _solution_old.
      51        7944 :   if (!_sys.solutionUDot())
      52           0 :     mooseError("ExplicitRK2: Time derivative of solution (`u_dot`) is not stored. Please set "
      53             :                "uDotRequested() to true in FEProblemBase befor requesting `u_dot`.");
      54             : 
      55        7944 :   NumericVector<Number> & u_dot = *_sys.solutionUDot();
      56        7944 :   u_dot = *_solution;
      57        7944 :   computeTimeDerivativeHelper(u_dot, _solution_old, _solution_older);
      58        7944 :   computeDuDotDu();
      59        7944 : }
      60             : 
      61             : void
      62           0 : ExplicitRK2::computeADTimeDerivatives(ADReal & ad_u_dot,
      63             :                                       const dof_id_type & dof,
      64             :                                       ADReal & /*ad_u_dotdot*/) const
      65             : {
      66           0 :   computeTimeDerivativeHelper(ad_u_dot, _solution_old(dof), _solution_older(dof));
      67           0 : }
      68             : 
      69             : void
      70        2360 : ExplicitRK2::solve()
      71             : {
      72        2360 :   Real time_new = _fe_problem.time();
      73        2360 :   Real time_old = _fe_problem.timeOld();
      74        2360 :   Real time_stage2 = time_old + a() * _dt;
      75             : 
      76             :   // Reset iteration counts
      77        2360 :   _n_nonlinear_iterations = 0;
      78        2360 :   _n_linear_iterations = 0;
      79             : 
      80             :   // There is no work to do for the first stage (Y_1 = y_n).  The
      81             :   // first solve therefore happens in the second stage.  Note that the
      82             :   // non-time Kernels (which should be marked implicit=false) are
      83             :   // evaluated at the old solution during this stage.
      84        2360 :   _fe_problem.initPetscOutputAndSomeSolverSettings();
      85        2360 :   _console << "1st solve" << std::endl;
      86        2360 :   _stage = 2;
      87        2360 :   _fe_problem.timeOld() = time_old;
      88        2360 :   _fe_problem.time() = time_stage2;
      89        2360 :   _nl->system().solve();
      90        2360 :   _n_nonlinear_iterations += getNumNonlinearIterationsLastSolve();
      91        2360 :   _n_linear_iterations += getNumLinearIterationsLastSolve();
      92             : 
      93             :   // Abort time step immediately on stage failure - see TimeIntegrator doc page
      94        2360 :   if (!_fe_problem.converged(_nl->number()))
      95           0 :     return;
      96             : 
      97             :   // Advance solutions old->older, current->old.  Also moves Material
      98             :   // properties and other associated state forward in time.
      99        2360 :   _fe_problem.advanceState();
     100             : 
     101             :   // The "update" stage (which we call stage 3) requires an additional
     102             :   // solve with the mass matrix.
     103        2360 :   _fe_problem.initPetscOutputAndSomeSolverSettings();
     104        2360 :   _console << "2nd solve" << std::endl;
     105        2360 :   _stage = 3;
     106        2360 :   _fe_problem.timeOld() = time_stage2;
     107        2360 :   _fe_problem.time() = time_new;
     108        2360 :   _nl->system().solve();
     109        2360 :   _n_nonlinear_iterations += getNumNonlinearIterationsLastSolve();
     110        2360 :   _n_linear_iterations += getNumLinearIterationsLastSolve();
     111             : 
     112             :   // Reset time at beginning of step to its original value
     113        2360 :   _fe_problem.timeOld() = time_old;
     114             : }
     115             : 
     116             : void
     117        5480 : ExplicitRK2::postResidual(NumericVector<Number> & residual)
     118             : {
     119        5480 :   if (_stage == 1)
     120             :   {
     121             :     // If postResidual() is called before solve(), _stage==1 and we don't
     122             :     // need to do anything.
     123             :   }
     124        5480 :   else if (_stage == 2)
     125             :   {
     126             :     // In the standard RK notation, the stage 2 residual is given by:
     127             :     //
     128             :     // R := M*(Y_2 - y_n)/dt - a*f(t_n, Y_1) = 0
     129             :     //
     130             :     // where:
     131             :     // .) M is the mass matrix.
     132             :     // .) f(t_n, Y_1) is the residual we are currently computing,
     133             :     //    since this method is intended to be used with "implicit=false"
     134             :     //    kernels.
     135             :     // .) M*(Y_2 - y_n)/dt corresponds to the residual of the time kernels.
     136             :     // .) The minus signs are "baked in" to the non-time residuals, so
     137             :     //    they do not appear here.
     138             :     // .) The current non-time residual is saved for the next stage.
     139        2832 :     *_residual_old = *_Re_non_time;
     140        2832 :     _residual_old->close();
     141             : 
     142        2832 :     residual.add(1., *_Re_time);
     143        2832 :     residual.add(a(), *_residual_old);
     144        2832 :     residual.close();
     145             :   }
     146        2648 :   else if (_stage == 3)
     147             :   {
     148             :     // In the standard RK notation, the update step residual is given by:
     149             :     //
     150             :     // R := M*(y_{n+1} - y_n)/dt - f(t_n+dt/2, Y_2) = 0
     151             :     //
     152             :     // where:
     153             :     // .) M is the mass matrix.
     154             :     // .) f(t_n+dt/2, Y_2) is the residual from stage 2.
     155             :     // .) The minus sign is already baked in to the non-time
     156             :     //    residuals, so it does not appear here.
     157             :     // .) Although this is an update step, we have to do a "solve"
     158             :     //    using the mass matrix.
     159        2648 :     residual.add(1., *_Re_time);
     160        2648 :     residual.add(b1(), *_residual_old);
     161        2648 :     residual.add(b2(), *_Re_non_time);
     162        2648 :     residual.close();
     163             :   }
     164             :   else
     165           0 :     mooseError("ExplicitRK2::postResidual(): _stage = ", _stage, ", only _stage = 1-3 is allowed.");
     166        5480 : }

Generated by: LCOV version 1.14