// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "mfem.hpp" #include "unit_tests.hpp" #include #include using namespace mfem; TEST_CASE("First order ODE methods", "[ODE]") { real_t tol = 0.1; // Class for simple linear first order ODE. // du/dt + a u = 0 class ODE : public TimeDependentOperator { protected: DenseMatrix A,I,T; Vector r; public: ODE(real_t a00,real_t a01,real_t a10,real_t a11) : TimeDependentOperator(2, (real_t) 0.0) { A.SetSize(2,2); I.SetSize(2,2); T.SetSize(2,2); r.SetSize(2); A(0,0) = a00; A(0,1) = a01; A(1,0) = a10; A(1,1) = a11; I = 0.0; I(0,0) = I(1,1) = 1.0; }; void Mult(const Vector &u, Vector &dudt) const override { A.Mult(u,dudt); dudt.Neg(); } void ImplicitSolve(const real_t dt, const Vector &u, Vector &dudt) override { // Residual A.Mult(u,r); r.Neg(); // Jacobian Add(I,A,dt,T); // Solve T.Invert(); T.Mult(r,dudt); } ~ODE() override {}; }; // Class for checking order of convergence of first order ODE. class CheckODE { protected: int ti_steps,levels; Vector u0; real_t t_final,dt; ODE *oper; public: CheckODE() { oper = new ODE(0.0, 1.0, -1.0, 0.0); ti_steps = 6; levels = 8; u0.SetSize(2); u0 = 1.0; t_final = 3*M_PI; dt = t_final/real_t(ti_steps); }; void init_hist(ODESolverWithStates* ode_solver,real_t dt_) { int nstate = ode_solver->GetState().Size(); for (int s = 0; s< nstate; s++) { real_t t = -(s)*dt_; Vector uh(2); uh[0] = -cos(t) - sin(t); uh[1] = cos(t) - sin(t); ode_solver->GetState().Set(s,uh); } } void writeHeader(real_t error) { mfem::out<Init(*oper); init_hist(ode_solver,dt_order); ode_solver->Run(u, t, dt_order, t_final - 1e-12); u +=u0; error[0] = u.Norml2(); writeHeader(error[0]); std::vector uh(ode_solver->GetState().MaxSize()); for (int l = 1; l < levels; l++) { int lvl = static_cast(pow(2,l)); t = 0.0; dt_order *= 0.5; u = u0; ode_solver->Init(*oper); // Instead of single run command // Chop-up sequence with Get/Set in between // in order to test these routines for (int ti = 0; ti < steps; ti++) { ode_solver->Step(u, t, dt_order); } int nstate = ode_solver->GetState().Size(); for (int s = 0; s < nstate; s++) { uh[s] = ode_solver->GetState().Get(s); } for (int ll = 1; ll < lvl; ll++) { // Use alternating options for setting the StateVector if (ll%2 == 0) { for (int s = nstate - 1; s >= 0; s--) { ode_solver->GetState().Append(uh[s]); } } else { for (int s = 0; s < nstate; s++) { uh[s] = ode_solver->GetState().Get(s); } } for (int ti = 0; ti < steps; ti++) { ode_solver->Step(u, t, dt_order); } nstate = ode_solver->GetState().Size(); for (int s = 0; s< nstate; s++) { uh[s] = ode_solver->GetState().Get(s); } } u += u0; error[l] = u.Norml2(); order = writeErrorAndOrder(error[l-1],error[l]); if (error[l] < 1e-10) { break; } } delete ode_solver; return order; } real_t order(ODESolver* ode_solver) { real_t dt_order,t,order = -1; Vector u(2); Vector error(levels); int steps = ti_steps; t = 0.0; dt_order = t_final/real_t(steps); u = u0; ode_solver->Init(*oper); ode_solver->Run(u, t, dt_order, t_final - 1e-12); u +=u0; error[0] = u.Norml2(); writeHeader(error[0]); for (int l = 1; l < levels; l++) { t = 0.0; dt_order *= 0.5; u = u0; ode_solver->Init(*oper); ode_solver->Run(u, t, dt_order, t_final-1e-12); u += u0; error[l] = u.Norml2(); order = writeErrorAndOrder(error[l-1],error[l]); if (error[l] < 1e-10) { break; } } delete ode_solver; return order; } virtual ~CheckODE() {delete oper;}; }; CheckODE check; // Implicit L-stable methods SECTION("BackwardEuler") { mfem::out<<"BackwardEuler"< 1.0); } SECTION("SDIRK23Solver(2)") { mfem::out<<"SDIRK23Solver(2)"< 2.0); } SECTION("SDIRK33Solver") { mfem::out<<"SDIRK33Solver"< 3.0); } SECTION("ForwardEulerSolver") { mfem::out<<"ForwardEuler"< 1.0); } SECTION("RK2Solver(0.5)") { mfem::out<<"RK2Solver"< 2.0); } SECTION("RK3SSPSolver") { mfem::out<<"RK3SSPSolver"< 3.0); } SECTION("RK4Solver") { mfem::out<<"RK4Solver"< 4.0); } SECTION("RK6Solver") { mfem::out<<"RK6Solver"< 6.0); } SECTION("RK8Solver") { mfem::out<<"RK8Solver"< 8.0); } SECTION("ImplicitMidpointSolver") { mfem::out<<"ImplicitMidpoint"< 2.0); } SECTION("SDIRK23Solver") { mfem::out<<"SDIRK23Solver"< 3.0); } SECTION("SDIRK34Solver") { mfem::out<<"SDIRK34Solver"< 4.0); } SECTION("TrapezoidalRuleSolver") { mfem::out<<"TrapezoidalRule"< 2.0 ); } SECTION("ESDIRK32Solver") { mfem::out<<"ESDIRK32Solver"< 2.0 ); } SECTION("ESDIRK33Solver") { mfem::out<<"ESDIRK33Solver"< 3.0 ); } // Generalized-alpha SECTION("GeneralizedAlphaSolver(1.0)") { mfem::out<<"GeneralizedAlphaSolver(1)"< 2.0); } SECTION("GeneralizedAlphaSolver(0.5)") { mfem::out<<"GeneralizedAlphaSolver(0.5)"< 2.0); } SECTION("GeneralizedAlphaSolver(0.5) - restart") { mfem::out<<"GeneralizedAlphaSolver(0.5) - restart"< 2.0); } SECTION("GeneralizedAlphaSolver(0.0)") { mfem::out<<"GeneralizedAlphaSolver(0)"< 2.0); } // Adams-Bashforth SECTION("AB1Solver()") { mfem::out<<"AB1Solver()"< 1.0); } SECTION("AB2Solver()") { mfem::out<<"AB2Solver()"< 2.0); } SECTION("AB2Solver() - restart") { mfem::out<<"AB2Solver() - restart"< 2.0); } SECTION("AB3Solver()") { mfem::out<<"AB3Solver()"< 3.0); } SECTION("AB4Solver()") { mfem::out<<"AB4Solver()"< 4.0); } SECTION("AB5Solver()") { mfem::out<<"AB5Solver()"< 5.0); } SECTION("AB5Solver() - restart") { mfem::out<<"AB5Solver() - restart"< 5.0); } // Adams-Moulton SECTION("AM1Solver()") { mfem::out<<"AM1Solver()"< 2.0); } SECTION("AM1Solver() - restart") { mfem::out<<"AM1Solver() - restart"< 2.0); } SECTION("AM2Solver()") { mfem::out<<"AM2Solver()"< 3.0); } SECTION("AM2Solver() - restart") { mfem::out<<"AM2Solver() - restart"< 1.0); } SECTION("AM3Solver()") { mfem::out<<"AM3Solver()"< 4.0); } SECTION("AM4Solver()") { mfem::out<<"AM4Solver()"< 5.0); } SECTION("AM4Solver() - restart") { mfem::out<<"AM4Solver() - restart"< 5.0); } }