// 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 "../general/communication.hpp" #include "../general/forall.hpp" #include "operator.hpp" #include "ode.hpp" namespace mfem { std::string ODESolver::ExplicitTypes = "\n\tExplicit solver: \n\t" " RK : 1 - Forward Euler, 2 - RK2(0.5), 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t" " AB : 11 - AB1, 12 - AB2, 13 - AB3, 14 - AB4, 15 - AB5\n"; std::string ODESolver::ImplicitTypes = "\n\tImplicit solver: \n\t" " (L-Stab): 21 - Backward Euler, 22 - SDIRK23(2), 23 - SDIRK33,\n\t" " (A-Stab): 32 - Implicit Midpoint, 33 - SDIRK23, 34 - SDIRK34,\n\t" " GA : 40 -- 50 - Generalized-alpha,\n\t" " AM : 51 - AM1, 52 - AM2, 53 - AM3, 54 - AM4\n"; std::string ODESolver::IMEXTypes = "\n\tIMEX solver: \n\t" " (L-Stab): 61 - Forward Backward Euler, 62 - IMEXRK2(2,2,2),\n\t" " 63 - IMEXRK2(2,3,2), 64 - IMEX_DIRK_RK3\n"; std::string ODESolver::Types = ODESolver::ExplicitTypes + ODESolver::ImplicitTypes + ODESolver::IMEXTypes; std::unique_ptr ODESolver::Select(int ode_solver_type) { if (ode_solver_type < 20) { return SelectExplicit(ode_solver_type); } else { return SelectImplicit(ode_solver_type); } } std::unique_ptr ODESolver::SelectExplicit(int ode_solver_type) { using ode_ptr = std::unique_ptr; switch (ode_solver_type) { // Explicit RK methods case 1: return ode_ptr(new ForwardEulerSolver); case 2: return ode_ptr(new RK2Solver(0.5)); // midpoint method case 3: return ode_ptr(new RK3SSPSolver); case 4: return ode_ptr(new RK4Solver); case 6: return ode_ptr(new RK6Solver); // Explicit AB methods case 11: return ode_ptr(new AB1Solver); case 12: return ode_ptr(new AB2Solver); case 13: return ode_ptr(new AB3Solver); case 14: return ode_ptr(new AB4Solver); case 15: return ode_ptr(new AB5Solver); default: MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type); } } std::unique_ptr ODESolver::SelectImplicit(int ode_solver_type) { using ode_ptr = std::unique_ptr; switch (ode_solver_type) { // Implicit L-stable methods case 21: return ode_ptr(new BackwardEulerSolver); case 22: return ode_ptr(new SDIRK23Solver(2)); case 23: return ode_ptr(new SDIRK33Solver); // Implicit A-stable methods (not L-stable) case 32: return ode_ptr(new ImplicitMidpointSolver); case 33: return ode_ptr(new SDIRK23Solver); case 34: return ode_ptr(new SDIRK34Solver); // Implicit generalized alpha case 40: return ode_ptr(new GeneralizedAlphaSolver(0.0)); case 41: return ode_ptr(new GeneralizedAlphaSolver(0.1)); case 42: return ode_ptr(new GeneralizedAlphaSolver(0.2)); case 43: return ode_ptr(new GeneralizedAlphaSolver(0.3)); case 44: return ode_ptr(new GeneralizedAlphaSolver(0.4)); case 45: return ode_ptr(new GeneralizedAlphaSolver(0.5)); case 46: return ode_ptr(new GeneralizedAlphaSolver(0.6)); case 47: return ode_ptr(new GeneralizedAlphaSolver(0.7)); case 48: return ode_ptr(new GeneralizedAlphaSolver(0.8)); case 49: return ode_ptr(new GeneralizedAlphaSolver(0.9)); case 50: return ode_ptr(new GeneralizedAlphaSolver(1.0)); // Implicit AM methods case 51: return ode_ptr(new AM1Solver); case 52: return ode_ptr(new AM2Solver); case 53: return ode_ptr(new AM3Solver); case 54: return ode_ptr(new AM4Solver); default: MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type ); } } std::unique_ptr ODESolver::SelectIMEX(const int ode_solver_type) { using ode_ptr = std::unique_ptr; switch (ode_solver_type) { // L-stable IMEX methods case 61: return ode_ptr(new IMEXExpImplEuler); case 62: return ode_ptr(new IMEXRK2); case 63: return ode_ptr(new IMEXRK2_3StageExplicit); case 64: return ode_ptr(new IMEX_DIRK_RK3); default: MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type ); } } void ODEStateDataVector::SetSize( int vsize, MemoryType m_t) { mem_type = m_t; for (int i = 0; i < smax; i++) { idx[i] = smax - i - 1; data[i].SetSize(vsize, mem_type); } ss = 0; } const Vector &ODEStateDataVector::Get(int i) const { MFEM_ASSERT_INDEX_IN_RANGE(i,0,ss); return data[idx[i]]; } Vector &ODEStateDataVector::Get(int i) { MFEM_ASSERT_INDEX_IN_RANGE(i,0,ss); return data[idx[i]]; } void ODEStateDataVector::Get(int i, Vector &vec) const { MFEM_ASSERT_INDEX_IN_RANGE(i,0,ss); vec = data[idx[i]]; } void ODEStateDataVector::Set(int i, Vector &state) { MFEM_ASSERT_INDEX_IN_RANGE(i,0,smax); data[idx[i]] = state; } void ODEStateDataVector::Append(Vector &state) { ShiftStages(); data[idx[0]] = state; Increment(); } void ODEStateDataVector::Print(std::ostream &os) const { os << ss <<"/" <f = &f_; mem_type = GetMemoryType(f_.GetMemoryClass()); } void ODESolver::ComputeSlopeFromState(const real_t dt, const Vector &u, Vector &k) { // k currently holds state u(t+dt), // convert to slope k = du/dt ~= (u(t+dt)-u(t))/dt const int usz = u.Size(); real_t fac = 1.0/dt; auto d_u = u.Read(); auto d_k = k.ReadWrite(); mfem::forall(usz, [=] MFEM_HOST_DEVICE (int i) { d_k[i] -= d_u[i]; d_k[i] *= fac; }); } void ForwardEulerSolver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); dxdt.SetSize(f->Width(), mem_type); } void ForwardEulerSolver::Step(Vector &x, real_t &t, real_t &dt) { f->SetTime(t); f->Mult(x, dxdt); x.Add(dt, dxdt); t += dt; } void RK2Solver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); int n = f->Width(); dxdt.SetSize(n, mem_type); x1.SetSize(n, mem_type); } void RK2Solver::Step(Vector &x, real_t &t, real_t &dt) { // 0 | // a | a // ---+-------- // | 1-b b b = 1/(2a) const real_t b = 0.5/a; f->SetTime(t); f->Mult(x, dxdt); add(x, (1. - b)*dt, dxdt, x1); x.Add(a*dt, dxdt); f->SetTime(t + a*dt); f->Mult(x, dxdt); add(x1, b*dt, dxdt, x); t += dt; } void RK3SSPSolver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); int n = f->Width(); y.SetSize(n, mem_type); k.SetSize(n, mem_type); } void RK3SSPSolver::Step(Vector &x, real_t &t, real_t &dt) { // x0 = x, t0 = t, k0 = dt*f(t0, x0) f->SetTime(t); f->Mult(x, k); // x1 = x + k0, t1 = t + dt, k1 = dt*f(t1, x1) add(x, dt, k, y); f->SetTime(t + dt); f->Mult(y, k); // x2 = 3/4*x + 1/4*(x1 + k1), t2 = t + 1/2*dt, k2 = dt*f(t2, x2) y.Add(dt, k); add(3./4, x, 1./4, y, y); f->SetTime(t + dt/2); f->Mult(y, k); // x3 = 1/3*x + 2/3*(x2 + k2), t3 = t + dt y.Add(dt, k); add(1./3, x, 2./3, y, x); t += dt; } void RK4Solver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); int n = f->Width(); y.SetSize(n, mem_type); k.SetSize(n, mem_type); z.SetSize(n, mem_type); } void RK4Solver::Step(Vector &x, real_t &t, real_t &dt) { // 0 | // 1/2 | 1/2 // 1/2 | 0 1/2 // 1 | 0 0 1 // -----+------------------- // | 1/6 1/3 1/3 1/6 f->SetTime(t); f->Mult(x, k); // k1 add(x, dt/2, k, y); add(x, dt/6, k, z); f->SetTime(t + dt/2); f->Mult(y, k); // k2 add(x, dt/2, k, y); z.Add(dt/3, k); f->Mult(y, k); // k3 add(x, dt, k, y); z.Add(dt/3, k); f->SetTime(t + dt); f->Mult(y, k); // k4 add(z, dt/6, k, x); t += dt; } ExplicitRKSolver::ExplicitRKSolver(int s_, const real_t *a_, const real_t *b_, const real_t *c_) { s = s_; a = a_; b = b_; c = c_; k = new Vector[s]; } void ExplicitRKSolver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); int n = f->Width(); y.SetSize(n, mem_type); for (int i = 0; i < s; i++) { k[i].SetSize(n, mem_type); } } void ExplicitRKSolver::Step(Vector &x, real_t &t, real_t &dt) { // 0 | // c[0] | a[0] // c[1] | a[1] a[2] // ... | ... // c[s-2] | ... a[s(s-1)/2-1] // --------+--------------------- // | b[0] b[1] ... b[s-1] f->SetTime(t); f->Mult(x, k[0]); for (int l = 0, i = 1; i < s; i++) { add(x, a[l++]*dt, k[0], y); for (int j = 1; j < i; j++) { y.Add(a[l++]*dt, k[j]); } f->SetTime(t + c[i-1]*dt); f->Mult(y, k[i]); } for (int i = 0; i < s; i++) { x.Add(b[i]*dt, k[i]); } t += dt; } ExplicitRKSolver::~ExplicitRKSolver() { delete [] k; } const real_t RK6Solver::a[] = { .6e-1, .1923996296296296296296296296296296296296e-1, .7669337037037037037037037037037037037037e-1, .35975e-1, 0., .107925, 1.318683415233148260919747276431735612861, 0., -5.042058063628562225427761634715637693344, 4.220674648395413964508014358283902080483, -41.87259166432751461803757780644346812905, 0., 159.4325621631374917700365669070346830453, -122.1192135650100309202516203389242140663, 5.531743066200053768252631238332999150076, -54.43015693531650433250642051294142461271, 0., 207.0672513650184644273657173866509835987, -158.6108137845899991828742424365058599469, 6.991816585950242321992597280791793907096, -.1859723106220323397765171799549294623692e-1, -54.66374178728197680241215648050386959351, 0., 207.9528062553893734515824816699834244238, -159.2889574744995071508959805871426654216, 7.018743740796944434698170760964252490817, -.1833878590504572306472782005141738268361e-1, -.5119484997882099077875432497245168395840e-3 }; const real_t RK6Solver::b[] = { .3438957868357036009278820124728322386520e-1, 0., 0., .2582624555633503404659558098586120858767, .4209371189673537150642551514069801967032, 4.405396469669310170148836816197095664891, -176.4831190242986576151740942499002125029, 172.3641334014150730294022582711902413315 }; const real_t RK6Solver::c[] = { .6e-1, .9593333333333333333333333333333333333333e-1, .1439, .4973, .9725, .9995, 1., }; const real_t RK8Solver::a[] = { .5e-1, -.69931640625e-2, .1135556640625, .399609375e-1, 0., .1198828125, .3613975628004575124052940721184028345129, 0., -1.341524066700492771819987788202715834917, 1.370126503900035259414693716084313000404, .490472027972027972027972027972027972028e-1, 0., 0., .2350972042214404739862988335493427143122, .180855592981356728810903963653454488485, .6169289044289044289044289044289044289044e-1, 0., 0., .1123656831464027662262557035130015442303, -.3885046071451366767049048108111244567456e-1, .1979188712522045855379188712522045855379e-1, -1.767630240222326875735597119572145586714, 0., 0., -62.5, -6.061889377376669100821361459659331999758, 5.650823198222763138561298030600840174201, 65.62169641937623283799566054863063741227, -1.180945066554970799825116282628297957882, 0., 0., -41.50473441114320841606641502701994225874, -4.434438319103725011225169229846100211776, 4.260408188586133024812193710744693240761, 43.75364022446171584987676829438379303004, .787142548991231068744647504422630755086e-2, -1.281405999441488405459510291182054246266, 0., 0., -45.04713996013986630220754257136007322267, -4.731362069449576477311464265491282810943, 4.514967016593807841185851584597240996214, 47.44909557172985134869022392235929015114, .1059228297111661135687393955516542875228e-1, -.5746842263844616254432318478286296232021e-2, -1.724470134262485191756709817484481861731, 0., 0., -60.92349008483054016518434619253765246063, -5.95151837622239245520283276706185486829, 5.556523730698456235979791650843592496839, 63.98301198033305336837536378635995939281, .1464202825041496159275921391759452676003e-1, .6460408772358203603621865144977650714892e-1, -.7930323169008878984024452548693373291447e-1, -3.301622667747079016353994789790983625569, 0., 0., -118.011272359752508566692330395789886851, -10.14142238845611248642783916034510897595, 9.139311332232057923544012273556827000619, 123.3759428284042683684847180986501894364, 4.623244378874580474839807625067630924792, -3.383277738068201923652550971536811240814, 4.527592100324618189451265339351129035325, -5.828495485811622963193088019162985703755 }; const real_t RK8Solver::b[] = { .4427989419007951074716746668098518862111e-1, 0., 0., 0., 0., .3541049391724448744815552028733568354121, .2479692154956437828667629415370663023884, -15.69420203883808405099207034271191213468, 25.08406496555856261343930031237186278518, -31.73836778626027646833156112007297739997, 22.93828327398878395231483560344797018313, -.2361324633071542145259900641263517600737 }; const real_t RK8Solver::c[] = { .5e-1, .1065625, .15984375, .39, .465, .155, .943, .901802041735856958259707940678372149956, .909, .94, 1., }; AdamsBashforthSolver::AdamsBashforthSolver(int s_, const real_t *a_): stages(s_), state(s_) { a = a_; } void AdamsBashforthSolver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); if (RKsolver) { RKsolver->Init(f_); } state.SetSize(f->Width(), mem_type); dt_ = -1.0; } void AdamsBashforthSolver::Step(Vector &x, real_t &t, real_t &dt) { CheckTimestep(dt); if (state.Size() >= stages -1) { f->SetTime(t); f->Mult(x, state[0]); state.Increment(); for (int i = 0; i < stages; i++) { x.Add(a[i]*dt, state[i]); } t += dt; } else { f->Mult(x,state[0]); RKsolver->Step(x,t,dt); state.Increment(); } state.ShiftStages(); } void AdamsBashforthSolver::CheckTimestep(real_t dt) { if (dt_ < 0.0) { dt_ = dt; return; } else if (fabs(dt-dt_) >10*std::numeric_limits::epsilon()) { state.Reset(); dt_ = dt; if (print()) { mfem::out << "WARNING:" << std::endl; mfem::out << " - Time step changed" << std::endl; mfem::out << " - Purging time stepping history" << std::endl; mfem::out << " - Will run Runge-Kutta to rebuild history" << std::endl; } } } const real_t AB1Solver::a[] = {1.0}; const real_t AB2Solver::a[] = {1.5,-0.5}; const real_t AB3Solver::a[] = {23.0/12.0,-4.0/3.0, 5.0/12.0}; const real_t AB4Solver::a[] = {55.0/24.0,-59.0/24.0, 37.0/24.0,-9.0/24.0}; const real_t AB5Solver::a[] = {1901.0/720.0,-2774.0/720.0, 2616.0/720.0,-1274.0/720.0, 251.0/720.0}; AdamsMoultonSolver::AdamsMoultonSolver(int s_, const real_t *a_): stages(s_), state(s_) { a = a_; } void AdamsMoultonSolver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); if (RKsolver) { RKsolver->Init(f_); } state.SetSize(f->Width(), mem_type); dt_ = -1.0; } void AdamsMoultonSolver::Step(Vector &x, real_t &t, real_t &dt) { if (dt_ < 0.0) { dt_ = dt; } else if (fabs(dt-dt_) > 10*std::numeric_limits::epsilon()) { state.Reset(); dt_ = dt; if (print()) { mfem::out << "WARNING:" << std::endl; mfem::out << " - Time step changed" << std::endl; mfem::out << " - Purging time stepping history" << std::endl; mfem::out << " - Will run Runge-Kutta to rebuild history" << std::endl; } } if ((state.Size() == 0)&&(stages>1)) { f->Mult(x,state[0]); state.Increment(); } if (state.Size() >= stages ) { f->SetTime(t); for (int i = 0; i < stages; i++) { x.Add(a[i+1]*dt, state[i]); } state.ShiftStages(); f->ImplicitSolve(a[0]*dt, x, state[0]); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a[0]*dt, x, state[0]); } x.Add(a[0]*dt, state[0]); t += dt; } else { state.ShiftStages(); RKsolver->Step(x,t,dt); f->Mult(x,state[0]); state.Increment(); } } const real_t AM1Solver::a[] = {0.5, 0.5}; const real_t AM2Solver::a[] = {5.0/12.0, 2.0/3.0, -1.0/12.0}; const real_t AM3Solver::a[] = {3.0/8.0, 19.0/24.0,-5.0/24.0, 1.0/24.0}; const real_t AM4Solver::a[] = {251.0/720.0,646.0/720.0,-264.0/720.0, 106.0/720.0, -19.0/720.0}; void BackwardEulerSolver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); k.SetSize(f->Width(), mem_type); } void BackwardEulerSolver::Step(Vector &x, real_t &t, real_t &dt) { f->SetTime(t + dt); f->ImplicitSolve(dt, x, k); // solve for k: k = f(x + dt*k, t + dt) if (f->ImplicitVarTypeIsState()) { x = k; // x = u_{i+1} } else { x.Add(dt, k); } t += dt; } void ImplicitMidpointSolver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); k.SetSize(f->Width(), mem_type); } void ImplicitMidpointSolver::Step(Vector &x, real_t &t, real_t &dt) { f->SetTime(t + dt/2); f->ImplicitSolve(dt/2, x, k); if (f->ImplicitVarTypeIsState()) { x.Neg(); x.Add(2.0, k); } else { x.Add(dt, k); } t += dt; } SDIRK23Solver::SDIRK23Solver(int gamma_opt) { if (gamma_opt == 0) { gamma = (3. - sqrt(3.))/6.; // not A-stable, order 3 } else if (gamma_opt == 2) { gamma = (2. - sqrt(2.))/2.; // L-stable, order 2 } else if (gamma_opt == 3) { gamma = (2. + sqrt(2.))/2.; // L-stable, order 2 } else { gamma = (3. + sqrt(3.))/6.; // A-stable, order 3 } } void SDIRK23Solver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); k.SetSize(f->Width(), mem_type); y.SetSize(f->Width(), mem_type); } void SDIRK23Solver::Step(Vector &x, real_t &t, real_t &dt) { // with a = gamma: // a | a // 1-a | 1-2a a // ------+----------- // | 1/2 1/2 // note: with gamma_opt=3, both solve are outside [t,t+dt] since a>1 f->SetTime(t + gamma*dt); f->ImplicitSolve(gamma*dt, x, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(gamma*dt, x, k); } add(x, (1.-2.*gamma)*dt, k, y); // y = x + (1-2*gamma)*dt*k x.Add(dt/2, k); f->SetTime(t + (1.-gamma)*dt); f->ImplicitSolve(gamma*dt, y, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(gamma*dt, y, k); } x.Add(dt/2, k); t += dt; } void SDIRK34Solver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); k.SetSize(f->Width(), mem_type); y.SetSize(f->Width(), mem_type); z.SetSize(f->Width(), mem_type); } void SDIRK34Solver::Step(Vector &x, real_t &t, real_t &dt) { // a | a // 1/2 | 1/2-a a // 1-a | 2a 1-4a a // ------+-------------------- // | b 1-2b b // note: two solves are outside [t,t+dt] since c1=a>1, c3=1-a<0 const real_t a = 1./sqrt(3.)*cos(M_PI/18.) + 0.5; const real_t b = 1./(6.*(2.*a-1.)*(2.*a-1.)); f->SetTime(t + a*dt); f->ImplicitSolve(a*dt, x, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, x, k); } add(x, (0.5-a)*dt, k, y); add(x, (2.*a)*dt, k, z); x.Add(b*dt, k); f->SetTime(t + dt/2); f->ImplicitSolve(a*dt, y, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, y, k); } z.Add((1.-4.*a)*dt, k); x.Add((1.-2.*b)*dt, k); f->SetTime(t + (1.-a)*dt); f->ImplicitSolve(a*dt, z, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, z, k); } x.Add(b*dt, k); t += dt; } void SDIRK33Solver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); k.SetSize(f->Width(), mem_type); y.SetSize(f->Width(), mem_type); } void SDIRK33Solver::Step(Vector &x, real_t &t, real_t &dt) { // a | a // c | c-a a // 1 | b 1-a-b a // -----+---------------- // | b 1-a-b a const real_t a = 0.435866521508458999416019; const real_t b = 1.20849664917601007033648; const real_t c = 0.717933260754229499708010; f->SetTime(t + a*dt); f->ImplicitSolve(a*dt, x, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, x, k); } add(x, (c-a)*dt, k, y); x.Add(b*dt, k); f->SetTime(t + c*dt); f->ImplicitSolve(a*dt, y, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, y, k); } x.Add((1.0-a-b)*dt, k); f->SetTime(t + dt); f->ImplicitSolve(a*dt, x, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, x, k); } x.Add(a*dt, k); t += dt; } void TrapezoidalRuleSolver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); k.SetSize(f->Width(), mem_type); y.SetSize(f->Width(), mem_type); } void TrapezoidalRuleSolver::Step(Vector &x, real_t &t, real_t &dt) { // 0 | 0 0 // 1 | 1/2 1/2 // ------+----------- // | 1/2 1/2 f->SetTime(t); f->Mult(x,k); add(x, dt/2.0, k, y); x.Add(dt/2.0, k); f->SetTime(t + dt); f->ImplicitSolve(dt/2.0, y, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(0.5*dt, y, k); } x.Add(dt/2.0, k); t += dt; } void ESDIRK32Solver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); k.SetSize(f->Width(), mem_type); y.SetSize(f->Width(), mem_type); z.SetSize(f->Width(), mem_type); } void ESDIRK32Solver::Step(Vector &x, real_t &t, real_t &dt) { // 0 | 0 0 0 // 2a | a a 0 // 1 | 1-b-a b a // ------+-------------------- // | 1-b-a b a const real_t a = (2.0 - sqrt(2.0)) / 2.0; const real_t b = (1.0 - 2.0*a) / (4.0*a); f->SetTime(t); f->Mult(x,k); add(x, a*dt, k, y); add(x, (1.0-b-a)*dt, k, z); x.Add((1.0-b-a)*dt, k); f->SetTime(t + (2.0*a)*dt); f->ImplicitSolve(a*dt, y, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, y, k); } z.Add(b*dt, k); x.Add(b*dt, k); f->SetTime(t + dt); f->ImplicitSolve(a*dt, z, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, z, k); } x.Add(a*dt, k); t += dt; } void ESDIRK33Solver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); k.SetSize(f->Width(), mem_type); y.SetSize(f->Width(), mem_type); z.SetSize(f->Width(), mem_type); } void ESDIRK33Solver::Step(Vector &x, real_t &t, real_t &dt) { // 0 | 0 0 0 // 2a | a a 0 // 1 | 1-b-a b a // ------+---------------------------- // | 1-b_2-b_3 b_2 b_3 const real_t a = (3.0 + sqrt(3.0)) / 6.0; const real_t b = (1.0 - 2.0*a) / (4.0*a); const real_t b_2 = 1.0 / ( 12.0*a*(1.0 - 2.0*a) ); const real_t b_3 = (1.0 - 3.0*a) / ( 3.0*(1.0 - 2.0*a) ); f->SetTime(t); f->Mult(x,k); add(x, a*dt, k, y); add(x, (1.0-b-a)*dt, k, z); x.Add((1.0-b_2-b_3)*dt, k); f->SetTime(t + (2.0*a)*dt); f->ImplicitSolve(a*dt, y, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, y, k); } z.Add(b*dt, k); x.Add(b_2*dt, k); f->SetTime(t + dt); f->ImplicitSolve(a*dt, z, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(a*dt, z, k); } x.Add(b_3*dt, k); t += dt; } void GeneralizedAlphaSolver::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); k.SetSize(f->Width(), mem_type); y.SetSize(f->Width(), mem_type); state.SetSize(f->Width(), mem_type); } void GeneralizedAlphaSolver::SetRhoInf(real_t rho_inf) { rho_inf = (rho_inf > 1.0) ? 1.0 : rho_inf; rho_inf = (rho_inf < 0.0) ? 0.0 : rho_inf; // According to Jansen alpha_m = 0.5*(3.0 - rho_inf)/(1.0 + rho_inf); alpha_f = 1.0/(1.0 + rho_inf); gamma = 0.5 + alpha_m - alpha_f; } void GeneralizedAlphaSolver::PrintProperties(std::ostream &os) { os << "Generalized alpha time integrator:" << std::endl; os << "alpha_m = " << alpha_m << std::endl; os << "alpha_f = " << alpha_f << std::endl; os << "gamma = " << gamma << std::endl; if (gamma == 0.5 + alpha_m - alpha_f) { os<<"Second order"<<" and "; } else { os<<"First order"<<" and "; } if ((alpha_m >= alpha_f)&&(alpha_f >= 0.5)) { os<<"Stable"<Mult(x,state[0]); state.Increment(); } // Set y = x + alpha_f*(1.0 - (gamma/alpha_m))*dt*xdot add(x, alpha_f*(1.0 - (gamma/alpha_m))*dt, state[0], y); // Solve k = f(y + dt_eff*k) real_t dt_eff = (gamma*alpha_f/alpha_m)*dt; f->SetTime(t + alpha_f*dt); f->ImplicitSolve(dt_eff, y, k); if (f->ImplicitVarTypeIsState()) { ComputeSlopeFromState(dt_eff, y, k); } // Update x and xdot x.Add((1.0 - (gamma/alpha_m))*dt, state[0]); x.Add( (gamma/alpha_m) *dt, k); state[0] *= (1.0-(1.0/alpha_m)); state[0].Add((1.0/alpha_m),k); t += dt; } void SIASolver::Init(Operator &P, TimeDependentOperator & F) { P_ = &P; F_ = &F; dp_.SetSize(F_->Height()); dq_.SetSize(P_->Height()); } void SIA1Solver::Step(Vector &q, Vector &p, real_t &t, real_t &dt) { F_->SetTime(t); F_->Mult(q,dp_); p.Add(dt,dp_); P_->Mult(p,dq_); q.Add(dt,dq_); t += dt; } void SIA2Solver::Step(Vector &q, Vector &p, real_t &t, real_t &dt) { P_->Mult(p,dq_); q.Add(0.5*dt,dq_); F_->SetTime(t+0.5*dt); F_->Mult(q,dp_); p.Add(dt,dp_); P_->Mult(p,dq_); q.Add(0.5*dt,dq_); t += dt; } SIAVSolver::SIAVSolver(int order) : order_(order) { a_.SetSize(order); b_.SetSize(order); switch (order_) { case 1: a_[0] = 1.0; b_[0] = 1.0; break; case 2: a_[0] = 0.5; a_[1] = 0.5; b_[0] = 0.0; b_[1] = 1.0; break; case 3: a_[0] = 2.0/3.0; a_[1] = -2.0/3.0; a_[2] = 1.0; b_[0] = 7.0/24.0; b_[1] = 0.75; b_[2] = -1.0/24.0; break; case 4: a_[0] = (2.0+pow(2.0,1.0/3.0)+pow(2.0,-1.0/3.0))/6.0; a_[1] = (1.0-pow(2.0,1.0/3.0)-pow(2.0,-1.0/3.0))/6.0; a_[2] = a_[1]; a_[3] = a_[0]; b_[0] = 0.0; b_[1] = 1.0/(2.0-pow(2.0,1.0/3.0)); b_[2] = 1.0/(1.0-pow(2.0,2.0/3.0)); b_[3] = b_[1]; break; default: MFEM_ASSERT(false, "Unsupported order in SIAVSolver"); }; } void SIAVSolver::Step(Vector &q, Vector &p, real_t &t, real_t &dt) { for (int i=0; iSetTime(t); if ( F_->isExplicit() ) { F_->Mult(q, dp_); } else { F_->ImplicitSolve(b_[i] * dt, q, dp_); } p.Add(b_[i] * dt, dp_); } P_->Mult(p, dq_); q.Add(a_[i] * dt, dq_); t += a_[i] * dt; } } std::string SecondOrderODESolver::Types = "ODE solver: \n\t" " [0--10] - GeneralizedAlpha(0.1 * s),\n\t" " 11 - Average Acceleration, 12 - Linear Acceleration\n\t" " 13 - CentralDifference, 14 - FoxGoodwin"; SecondOrderODESolver* SecondOrderODESolver::Select(int ode_solver_type) { SecondOrderODESolver* ode_solver = NULL; switch (ode_solver_type) { // Implicit methods case 0: ode_solver = new GeneralizedAlpha2Solver(0.0); break; case 1: ode_solver = new GeneralizedAlpha2Solver(0.1); break; case 2: ode_solver = new GeneralizedAlpha2Solver(0.2); break; case 3: ode_solver = new GeneralizedAlpha2Solver(0.3); break; case 4: ode_solver = new GeneralizedAlpha2Solver(0.4); break; case 5: ode_solver = new GeneralizedAlpha2Solver(0.5); break; case 6: ode_solver = new GeneralizedAlpha2Solver(0.6); break; case 7: ode_solver = new GeneralizedAlpha2Solver(0.7); break; case 8: ode_solver = new GeneralizedAlpha2Solver(0.8); break; case 9: ode_solver = new GeneralizedAlpha2Solver(0.9); break; case 10: ode_solver = new GeneralizedAlpha2Solver(1.0); break; case 11: ode_solver = new AverageAccelerationSolver(); break; case 12: ode_solver = new LinearAccelerationSolver(); break; case 13: ode_solver = new CentralDifferenceSolver(); break; case 14: ode_solver = new FoxGoodwinSolver(); break; default: MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type); } return ode_solver; } // In this routine state[0] represents d2xdt2 void SecondOrderODESolver::EulerStep(Vector &x, Vector &dxdt, real_t &t, real_t &dt) { x.Add(dt, dxdt); f->SetTime(t + dt); f->ImplicitSolve(0.5*dt*dt, dt, x, dxdt, state[0]); x.Add(0.5*dt*dt, state[0]); dxdt.Add(dt, state[0]); t += dt; } // In this routine state[0] represents d2xdt2 void SecondOrderODESolver::MidPointStep(Vector &x, Vector &dxdt, real_t &t, real_t &dt) { x.Add(0.5*dt, dxdt); f->SetTime(t + dt); f->ImplicitSolve(0.25*dt*dt, 0.5*dt, x, dxdt, state[0]); x.Add(0.5*dt, dxdt); x.Add(0.5*dt*dt, state[0]); dxdt.Add(dt, state[0]); t += dt; } void SecondOrderODESolver::Init(SecondOrderTimeDependentOperator &f_) { this->f = &f_; mem_type = GetMemoryType(f_.GetMemoryClass()); state.SetSize(f->Width(), mem_type); } void NewmarkSolver::PrintProperties(std::ostream &os) { os << "Newmark time integrator:" << std::endl; os << "beta = " << beta << std::endl; os << "gamma = " << gamma << std::endl; if (gamma == 0.5) { os<<"Second order"<<" and "; } else { os<<"First order"<<" and "; } if ((gamma >= 0.5) && (beta >= (gamma + 0.5)*(gamma + 0.5)/4)) { os<<"A-Stable"<= 0.5) && (beta >= 0.5*gamma)) { os<<"Conditionally stable"<Mult(x, dxdt, state[0]); } state.Increment(); } f->SetTime(t + dt); x.Add(dt, dxdt); x.Add(fac0*dt*dt, state[0]); dxdt.Add(fac2*dt, state[0]); f->SetTime(t + dt); f->ImplicitSolve(fac3*dt*dt, fac4*dt, x, dxdt, state[0]); x.Add(fac3*dt*dt, state[0]); dxdt.Add(fac4*dt, state[0]); t += dt; } void GeneralizedAlpha2Solver::Init(SecondOrderTimeDependentOperator &f_) { SecondOrderODESolver::Init(f_); xa.SetSize(f->Width(), mem_type); va.SetSize(f->Width(), mem_type); aa.SetSize(f->Width(), mem_type); } void GeneralizedAlpha2Solver::PrintProperties(std::ostream &os) { os << "Generalized alpha time integrator:" << std::endl; os << "alpha_m = " << alpha_m << std::endl; os << "alpha_f = " << alpha_f << std::endl; os << "beta = " << beta << std::endl; os << "gamma = " << gamma << std::endl; if (gamma == 0.5 + alpha_m - alpha_f) { os<<"Second order"<<" and "; } else { os<<"First order"<<" and "; } if ((alpha_m >= alpha_f)&& (alpha_f >= 0.5) && (beta >= 0.25 + 0.5*(alpha_m - alpha_f))) { os<<"Stable"<Mult(x, dxdt, state[0]); } state.Increment(); } // Predict alpha levels add(dxdt, fac0*dt, state[0], va); add(x, fac1*dt, va, xa); add(dxdt, fac2*dt, state[0], va); // Solve alpha levels f->SetTime(t + dt); f->ImplicitSolve(fac3*dt*dt, fac4*dt, xa, va, aa); // Correct alpha levels xa.Add(fac3*dt*dt, aa); va.Add(fac4*dt, aa); // Extrapolate x *= 1.0 - 1.0/fac1; x.Add (1.0/fac1, xa); dxdt *= 1.0 - 1.0/fac1; dxdt.Add (1.0/fac1, va); state[0] *= 1.0 - 1.0/fac5; state[0].Add (1.0/fac5, aa); t += dt; } void IMEXExpImplEuler::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); int n = f->Width(); k1.SetSize(n, mem_type); k2.SetSize(n, mem_type); } void IMEXExpImplEuler::Step(Vector &x, real_t &t, real_t &dt) { f->SetTime(t); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(x, k1); f->SetTime(t+dt); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_2); f->ImplicitSolve(dt, x, k2); f->SetTime(t); x.Add(dt, k1); x.Add(dt, k2); t += dt; } void IMEXRK2::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); int n = f->Width(); k1_exp.SetSize(n, mem_type); k2_exp.SetSize(n, mem_type); k_imp.SetSize(n, mem_type); y.SetSize(n, mem_type); } void IMEXRK2::Step(Vector &x, real_t &t, real_t &dt) { double gamma = 1 - sqrt(2)/2; double delta = 1 - 1/(2*gamma); f->SetTime(t); //K1 exp is just f_1(t, x) f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(x, k1_exp); //K2 exp is f_1(t + gamma dt, x + dt gamma K1) f->SetTime(t + gamma*dt); add(x, dt*gamma, k1_exp, y); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(y, k2_exp); //K2_imp = f_2(t + gamma dt, x + dt gamma K2_imp) f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_2); f->ImplicitSolve(dt*gamma, x, k_imp); //reuse k_imp to avoid extra vector //K3_imp = f_2(t+dt,x + dt(1-gamma)K2_imp + dt gamma K3_imp) f -> SetTime(t + dt); //add(x, dt*(1-gamma), k2_imp, z); //optimization to avoid extra vector x.Add(dt*(1-gamma), k_imp); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_2); //f->ImplicitSolve(dt*gamma, z, k3_imp); //reuse k_imp to avoid extra vector f->ImplicitSolve(dt*gamma, x, k_imp); //add it all up x.Add(dt*delta, k1_exp); x.Add(dt*(1-delta), k2_exp); //x.Add(dt*(1-gamma), k2_imp); it is already added to x above x.Add(dt*gamma, k_imp); t += dt; } void IMEXRK2_3StageExplicit::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); int n = f->Width(); k1_exp.SetSize(n, mem_type); k2_exp.SetSize(n, mem_type); k3_exp.SetSize(n, mem_type); k_imp.SetSize(n, mem_type); y.SetSize(n, mem_type); } void IMEXRK2_3StageExplicit::Step(Vector &x, real_t &t, real_t &dt) { double gamma = 1 - sqrt(2)/2; double delta = -2*sqrt(2)/3; f->SetTime(t); //K1 exp is just f_1(t, x) f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(x, k1_exp); //K2 exp is f_1(t + gamma dt, x + dt gamma K1) f->SetTime(t + gamma*dt); add(x, dt*gamma, k1_exp, y); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(y, k2_exp); //K3 Exp is f_1(t + dt, x + dt delta K1_exp + dt (1-delta) K2_exp) f->SetTime(t + dt); add(x, dt*delta, k1_exp, y); //add(y, dt*(1-delta), k2_exp, w); //optimization to avoid extra vector y.Add(dt*(1-delta), k2_exp); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(y, k3_exp); //K2_imp = f_2(t + gamma dt, x + dt gamma K2_imp) f->SetTime(t + gamma*dt); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_2); f->ImplicitSolve(dt*gamma, x, k_imp); //K3_imp = f_2(t+dt,x + dt(1-gamma)K2_imp + dt gamma K3_imp) f -> SetTime(t + dt); //add(x, dt*(1-gamma), k2_imp, z); x.Add(dt*(1-gamma), k_imp); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_2); f->ImplicitSolve(dt*gamma, x, k_imp); //add it all up x.Add(dt*delta, k2_exp); x.Add(dt*(1-delta), k3_exp); //x.Add(dt*(1-gamma), k2_imp); // it is already added to x above x.Add(dt*gamma, k_imp); t += dt; } void IMEX_DIRK_RK3::Init(TimeDependentOperator &f_) { ODESolver::Init(f_); int n = f->Width(); k1_exp.SetSize(n, mem_type); k2_exp.SetSize(n, mem_type); k3_exp.SetSize(n, mem_type); k4_exp.SetSize(n, mem_type); k2_imp.SetSize(n, mem_type); k3_imp.SetSize(n, mem_type); y.SetSize(n, mem_type); } void IMEX_DIRK_RK3::Step(Vector &x, real_t &t, real_t &dt) { double gamma = 0.4358665215; double b1 = 1.208496649; double b2 = -0.644363171; double a_31 = 0.3212788860; double a_32 = 0.3966543747; double a_41 = -0.105858296; double a_42 = 0.5529291479; double a_43 = 0.5529291479; //K1_exp f->SetTime(t); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(x, k1_exp); //K2_imp, K2_exp f->SetTime(t + gamma*dt); add(x, dt*gamma, k1_exp, y); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(y, k2_exp); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_2); f->ImplicitSolve(dt*gamma, x, k2_imp); //K3_imp, K3_exp f->SetTime(t + (1+gamma)/2*dt); add(x, dt*a_31, k1_exp, y); //add(y, dt*a_32, k2_exp, w); //optimization to avoid extra vector y.Add(dt*a_32, k2_exp); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(y, k3_exp); add(x, dt*(1-gamma)/2, k2_imp, y); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_2); f->ImplicitSolve(dt*gamma, y, k3_imp); //K4_imp, K4_exp f->SetTime(t+dt); add(x, dt*a_41, k1_exp, y); //add(y, dt*a_42, k2_exp, v); y.Add(dt*a_42, k2_exp); //add(w, dt*a_43, k3_exp, v); y.Add(dt*a_43, k3_exp); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_1); f->Mult(y, k4_exp); //add(x, dt*b1, k2_imp, z); //add(z, dt*b2, k3_imp, u); //optimization to avoid extra vector x.Add(dt*b1, k2_imp); x.Add(dt*b2, k3_imp); f->SetEvalMode(TimeDependentOperator::ADDITIVE_TERM_2); f->ImplicitSolve(dt*gamma, x, k3_imp); //add it all together x.Add(dt*b1, k2_exp); x.Add(dt*b2, k3_exp); x.Add(dt*gamma, k4_exp); //x.Add(dt*b1, k2_imp); //already added above //x.Add(dt*b2, k3_imp); //already added above x.Add(dt*gamma, k3_imp); t += dt; } }