// Copyright (c) 2010-2023, 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 using namespace mfem; TEST_CASE("Second order ODE methods", "[ODE]") { double tol = 0.1; /** Class for simple linear second order ODE. * * du2/dt^2 + b du/dt + a u = 0 * */ class ODE2 : public SecondOrderTimeDependentOperator { protected: double a, b; public: ODE2(double a, double b) : SecondOrderTimeDependentOperator(1, 0.0), a(a), b(b) {}; using SecondOrderTimeDependentOperator::Mult; virtual void Mult(const Vector &u, const Vector &dudt, Vector &d2udt2) const { d2udt2[0] = -a*u[0] - b*dudt[0]; } using SecondOrderTimeDependentOperator::ImplicitSolve; virtual void ImplicitSolve(const double fac0, const double fac1, const Vector &u, const Vector &dudt, Vector &d2udt2) { double T = 1.0 + a*fac0 + fac1*b; d2udt2[0] = (-a*u[0] - b*dudt[0])/T; } virtual ~ODE2() {}; }; // Class for checking order of convergence of second order ODE. class CheckODE2 { protected: int ti_steps,levels; Vector u0; Vector dudt0; double t_final,dt; ODE2 *oper; public: CheckODE2() { oper = new ODE2(1.0, 0.0); ti_steps = 20; levels = 5; u0.SetSize(1); u0 = 1.0; dudt0.SetSize(1); dudt0 = 1.0; t_final = 2*M_PI; dt = t_final/double(ti_steps); }; void init_hist(SecondOrderODESolver* ode_solver,double dt_) { int nstate = ode_solver->GetStateSize(); for (int s = 0; s< nstate; s++) { double t = -(s)*dt_; Vector uh(1); uh[0] = -cos(t) - sin(t); ode_solver->SetStateVector(s,uh); } } double order(SecondOrderODESolver* ode_solver, bool init_hist_ = false) { double dt_order,t; Vector u(1); Vector du(1); Vector err_u(levels); Vector err_du(levels); int steps = ti_steps; t = 0.0; dt_order = t_final/double(steps); u = u0; du = dudt0; ode_solver->Init(*oper); if (init_hist_) { init_hist(ode_solver,dt_order); } ode_solver->Run(u, du, t, dt_order, t_final - 1e-12); u -= u0; du -= dudt0; err_u[0] = u.Norml2(); err_du[0] = du.Norml2(); mfem::out< uh(ode_solver->GetMaxStateSize()); for (int l = 1; l< levels; l++) { int lvl = static_cast(pow(2,l)); t = 0.0; dt_order *= 0.5; u = u0; du = dudt0; ode_solver->Init(*oper); if (init_hist_) { init_hist(ode_solver,dt_order); } // Instead of single run command: // ode_solver->Run(u, du, t, dt_order, t_final - 1e-12); // 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, du, t, dt_order); } int nstate = ode_solver->GetStateSize(); for (int s = 0; s < nstate; s++) { ode_solver->GetStateVector(s,uh[s]); } for (int ll = 1; ll < lvl; ll++) { for (int s = 0; s < nstate; s++) { ode_solver->SetStateVector(s,uh[s]); } for (int ti = 0; ti < steps; ti++) { ode_solver->Step(u, du, t, dt_order); } nstate = ode_solver->GetStateSize(); for (int s = 0; s< nstate; s++) { uh[s] = ode_solver->GetStateVector(s); } } u -= u0; du -= dudt0; err_u[l] = u.Norml2(); err_du[l] = du.Norml2(); mfem::out< 2.0); } SECTION("LinearAcceleration") { double conv_rate = check.order(new LinearAccelerationSolver); REQUIRE(conv_rate + tol > 2.0); } SECTION("CentralDifference") { double conv_rate = check.order(new CentralDifferenceSolver); REQUIRE(conv_rate + tol > 2.0); } SECTION("FoxGoodwin") { double conv_rate = check.order(new FoxGoodwinSolver); REQUIRE(conv_rate + tol > 4.0); } // Generalized-alpha based solvers SECTION("GeneralizedAlpha(0.0)") { double conv_rate = check.order(new GeneralizedAlpha2Solver(0.0)); REQUIRE(conv_rate + tol > 2.0); } SECTION("GeneralizedAlpha(0.5)") { double conv_rate = check.order(new GeneralizedAlpha2Solver(0.5)); REQUIRE(conv_rate + tol > 2.0); } SECTION("GeneralizedAlpha(0.5) - restart") { double conv_rate = check.order(new GeneralizedAlpha2Solver(0.5),true); REQUIRE(conv_rate + tol > 2.0); } SECTION("GeneralizedAlpha(1.0)") { double conv_rate = check.order(new GeneralizedAlpha2Solver(1.0)); REQUIRE(conv_rate + tol > 2.0); } SECTION("AverageAcceleration") { double conv_rate = check.order(new AverageAccelerationSolver); REQUIRE(conv_rate + tol > 2.0); } SECTION("HHTAlpha(2/3)") { double conv_rate = check.order(new HHTAlphaSolver(2.0/3.0)); REQUIRE(conv_rate + tol > 2.0); } SECTION("HHTAlpha(0.75)") { double conv_rate = check.order(new HHTAlphaSolver(0.75)); REQUIRE(conv_rate + tol > 2.0); } SECTION("HHTAlpha(1.0)") { double conv_rate = check.order(new HHTAlphaSolver(1.0)); REQUIRE(conv_rate + tol > 2.0); } SECTION("WBZAlpha(0.0)") { double conv_rate = check.order(new WBZAlphaSolver(0.0)); REQUIRE(conv_rate + tol > 2.0); } SECTION("WBZAlpha(0.5)") { double conv_rate = check.order(new WBZAlphaSolver(0.5)); REQUIRE(conv_rate + tol > 2.0); } SECTION("WBZAlpha(1.0)") { double conv_rate = check.order(new WBZAlphaSolver(1.0)); REQUIRE(conv_rate + tol > 2.0); } }