LCOV - code coverage report
Current view: top level - src/timeintegrators - ExplicitSSPRungeKutta.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 84 96 87.5 %
Date: 2025-07-17 01:28:37 Functions: 7 9 77.8 %
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             : // MOOSE includes
      11             : #include "ExplicitSSPRungeKutta.h"
      12             : #include "NonlinearSystem.h"
      13             : #include "FEProblem.h"
      14             : 
      15             : // libMesh includes
      16             : #include "libmesh/nonlinear_solver.h"
      17             : 
      18             : using namespace libMesh;
      19             : 
      20             : registerMooseObject("MooseApp", ExplicitSSPRungeKutta);
      21             : 
      22             : InputParameters
      23       14385 : ExplicitSSPRungeKutta::validParams()
      24             : {
      25       14385 :   InputParameters params = ExplicitTimeIntegrator::validParams();
      26             : 
      27       14385 :   MooseEnum orders("1=1 2 3");
      28       14385 :   params.addRequiredParam<MooseEnum>("order", orders, "Order of time integration");
      29       14385 :   params.addClassDescription("Explicit strong stability preserving Runge-Kutta methods");
      30       28770 :   return params;
      31       14385 : }
      32             : 
      33          80 : ExplicitSSPRungeKutta::ExplicitSSPRungeKutta(const InputParameters & parameters)
      34             :   : ExplicitTimeIntegrator(parameters),
      35          80 :     _order(getParam<MooseEnum>("order")),
      36          80 :     _stage(0),
      37          80 :     _solution_intermediate_stage(addVector("solution_intermediate_stage", false, GHOSTED)),
      38          80 :     _tmp_solution(addVector("tmp_solution", false, GHOSTED)),
      39         240 :     _tmp_mass_solution_product(addVector("tmp_mass_solution_product", false, GHOSTED))
      40             : {
      41             :   // For SSPRK methods up to order 3, the number of stages equals the order
      42          80 :   _n_stages = _order;
      43             : 
      44          80 :   if (_order == 1)
      45             :   {
      46          64 :     _a = {{1.0}};
      47           0 :     _b = {1.0};
      48          32 :     _c = {0.0};
      49             :   }
      50          48 :   else if (_order == 2)
      51             :   {
      52          72 :     _a = {{1.0, 0.0}, {0.5, 0.5}};
      53           0 :     _b = {1.0, 0.5};
      54          24 :     _c = {0.0, 1.0};
      55             :   }
      56          24 :   else if (_order == 3)
      57             :   {
      58          96 :     _a = {{1.0, 0.0, 0.0}, {0.75, 0.25, 0.0}, {1.0 / 3.0, 0.0, 2.0 / 3.0}};
      59           0 :     _b = {1.0, 0.25, 2.0 / 3.0};
      60          24 :     _c = {0.0, 1.0, 0.5};
      61             :   }
      62             :   else
      63           0 :     mooseError("Invalid time integration order.");
      64             : 
      65          80 :   _solution_stage.resize(_n_stages + 1, nullptr);
      66         240 : }
      67             : 
      68             : void
      69         858 : ExplicitSSPRungeKutta::computeTimeDerivatives()
      70             : {
      71             :   // Only the Jacobian needs to be computed, since the mass matrix needs it
      72         858 :   computeDuDotDu();
      73         858 : }
      74             : 
      75             : void
      76           0 : ExplicitSSPRungeKutta::computeADTimeDerivatives(ADReal & ad_u_dot,
      77             :                                                 const dof_id_type & dof,
      78             :                                                 ADReal & /*ad_u_dotdot*/) const
      79             : {
      80             :   // Note that if the solution for the current stage is not a nullptr, then neither
      81             :   // are the previous stages.
      82           0 :   if (_solution_stage[_stage])
      83             :   {
      84           0 :     for (unsigned int k = 0; k <= _stage; k++)
      85           0 :       ad_u_dot -= _a[_stage][k] * (*(_solution_stage[k]))(dof);
      86           0 :     ad_u_dot *= 1.0 / (_b[_stage] * _dt);
      87             :   }
      88             :   else
      89             :   {
      90             :     // We must be outside the solve loop in order to meet this criterion. In that case are we at
      91             :     // timestep_begin or timestep_end? We don't know, so I don't think it's meaningful to compute
      92             :     // derivatives here. Let's put in a quiet NaN which will only signal if we try to do something
      93             :     // meaningful with it (and then we do want to signal because time derivatives may not be
      94             :     // meaningful right now)
      95           0 :     ad_u_dot = std::numeric_limits<typename ADReal::value_type>::quiet_NaN();
      96             :   }
      97           0 : }
      98             : 
      99             : void
     100         165 : ExplicitSSPRungeKutta::solve()
     101             : {
     102             :   // Reset iteration counts
     103         165 :   _n_nonlinear_iterations = 0;
     104         165 :   _n_linear_iterations = 0;
     105             : 
     106         165 :   _current_time = _fe_problem.time();
     107         165 :   const Real time_old = _fe_problem.timeOld();
     108         165 :   const Real dt = _current_time - time_old;
     109             : 
     110         165 :   bool converged = false;
     111             : 
     112         165 :   _solution_stage[0] = &_solution_old;
     113         495 :   for (_stage = 0; _stage < _n_stages; _stage++)
     114             :   {
     115         330 :     if (_stage == 0)
     116             :     {
     117             :       // Nothing needs to be done
     118             :     }
     119         165 :     else if (_stage == _n_stages - 1)
     120             :     {
     121         110 :       _solution_stage[_stage] = _solution;
     122             :     }
     123             :     else
     124             :     {
     125             :       // Else must be the intermediate stage of the 3-stage method
     126          55 :       *_solution_intermediate_stage = *_solution;
     127          55 :       _solution_intermediate_stage->close();
     128          55 :       _solution_stage[_stage] = _solution_intermediate_stage;
     129             :     }
     130             : 
     131             :     // Set stage time for residual evaluation
     132         330 :     _fe_problem.time() = time_old + _c[_stage] * dt;
     133         330 :     _nonlinear_implicit_system->update();
     134             : 
     135         330 :     converged = solveStage();
     136         330 :     if (!converged)
     137           0 :       return;
     138             :   }
     139             : 
     140         165 :   if (_stage == _n_stages)
     141             :     // We made it to the end of the solve. We may call functions like computeTimeDerivatives later
     142             :     // for postprocessing purposes in which case we need to ensure we're accessing our data
     143             :     // correctly (e.g. not out-of-bounds)
     144         165 :     --_stage;
     145             : }
     146             : 
     147             : bool
     148         330 : ExplicitSSPRungeKutta::solveStage()
     149             : {
     150             :   // Compute the mass matrix
     151         330 :   computeTimeDerivatives();
     152         330 :   auto & mass_matrix = _nonlinear_implicit_system->get_system_matrix();
     153         330 :   _fe_problem.computeJacobianTag(
     154         330 :       *_nonlinear_implicit_system->current_local_solution, mass_matrix, _Ke_time_tag);
     155             : 
     156             :   // Compute RHS vector using previous stage solution in steady-state residual
     157         330 :   _explicit_residual->zero();
     158         330 :   _fe_problem.computeResidual(
     159         330 :       *_nonlinear_implicit_system->current_local_solution, *_explicit_residual, _nl->number());
     160             : 
     161             :   // Move the residual to the RHS
     162         330 :   *_explicit_residual *= -1.0;
     163             : 
     164             :   // Perform the linear solve
     165         330 :   bool converged = performExplicitSolve(mass_matrix);
     166             : 
     167             :   // Update the solution: u^(s) = u^(s-1) + du^(s)
     168         330 :   (*_nonlinear_implicit_system->solution).zero();
     169         330 :   *_nonlinear_implicit_system->solution = *(_solution_stage[_stage]);
     170         330 :   *_nonlinear_implicit_system->solution += *_solution_update;
     171             : 
     172             :   // Enforce contraints on the solution
     173         330 :   DofMap & dof_map = _nonlinear_implicit_system->get_dof_map();
     174         330 :   dof_map.enforce_constraints_exactly(*_nonlinear_implicit_system,
     175         330 :                                       _nonlinear_implicit_system->solution.get());
     176         330 :   _nonlinear_implicit_system->update();
     177             : 
     178         330 :   _nl->setSolution(*_nonlinear_implicit_system->current_local_solution);
     179             : 
     180         330 :   _nonlinear_implicit_system->nonlinear_solver->converged = converged;
     181             : 
     182         330 :   return converged;
     183             : }
     184             : 
     185             : void
     186         330 : ExplicitSSPRungeKutta::postResidual(NumericVector<Number> & residual)
     187             : {
     188             :   // The time residual is not included in the steady-state residual
     189         330 :   residual += *_Re_non_time;
     190             : 
     191             :   // Compute \sum_{k=0}^{s-1} a_{s,k} u^(k) - u^(s-1)
     192         330 :   _tmp_solution->zero();
     193         880 :   for (unsigned int k = 0; k <= _stage; k++)
     194         550 :     _tmp_solution->add(_a[_stage][k], *(_solution_stage[k]));
     195         330 :   _tmp_solution->add(-1.0, *(_solution_stage[_stage]));
     196         330 :   _tmp_solution->close();
     197             : 
     198             :   // Perform mass matrix product with the above vector
     199         330 :   auto & mass_matrix = _nonlinear_implicit_system->get_system_matrix();
     200         330 :   mass_matrix.vector_mult(*_tmp_mass_solution_product, *_tmp_solution);
     201             : 
     202             :   // Finish computing residual vector (before modification by nodal BCs)
     203         330 :   residual -= *_tmp_mass_solution_product;
     204             : 
     205         330 :   residual.close();
     206             : 
     207             :   // Set time at which to evaluate nodal BCs
     208         330 :   _fe_problem.time() = _current_time;
     209         330 : }
     210             : 
     211             : Real
     212         858 : ExplicitSSPRungeKutta::duDotDuCoeff() const
     213             : {
     214         858 :   return Real(1) / _b[_stage];
     215             : }

Generated by: LCOV version 1.14