#ifndef IMPLICITEULER_HPP #define IMPLICITEULER_HPP #include #include #include #include #include #include "TimeIntegrator.hpp" namespace temporal { class ImplicitEuler : public TimeIntegrator { private: Eigen::SparseMatrix M_lhs_; Eigen::SparseLU> LUsolver_; bool isInitialized_ = false; public: ImplicitEuler(double timestep) : TimeIntegrator(timestep) {}; void setUp(const spatial::SpatialDiscretization2D& sd) override { const Eigen::SparseMatrix& A = sd.getMatrix(); M_lhs_ = Eigen::SparseMatrix(A.rows(), A.cols()); M_lhs_.setIdentity(); M_lhs_ -= timestep_ * A; LUsolver_.compute(M_lhs_); if (LUsolver_.info() != Eigen::Success) throw std::runtime_error("LU factorization for Implicit Euler failed\n"); isInitialized_ = true; } void step(spatial::SpatialDiscretization2D& sd, double t, Eigen::VectorXd& u) const override { if (!isInitialized_) throw std::logic_error("\nStep function for Implicit Euler time integration was used before SetUp.\n"); sd.updateRHS(t + timestep_); const Eigen::VectorXd& b = sd.getVector(); // Create temporary to avoid aliasing Eigen::VectorXd tmp = u + timestep_ * b; u = LUsolver_.solve(tmp); if (LUsolver_.info() != Eigen::Success) throw std::runtime_error("IE solve failed\n"); }; // Virtual factory for timestep remainder operations. Note that the clone does not transfer precomputed matrices. The caller must invoke setUp() on the clone. std::unique_ptr cloneWithTimestep(double timestep) const override { return std::make_unique(timestep); } }; } // namespace #endif // ifndef