Skip to content
Snippets Groups Projects
EigenLinearSolver.cpp 11 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Copyright (c) 2012-2021, OpenGeoSys Community (http://www.opengeosys.org)
    
     *            Distributed under a Modified BSD License.
     *              See accompanying file LICENSE.txt or
     *              http://www.opengeosys.org/project/license
     *
     */
    
    #include "EigenLinearSolver.h"
    
    
    #include <Eigen/Sparse>
    
    
    #include "BaseLib/Logging.h"
    
    #ifdef USE_MKL
    #include <Eigen/PardisoSupport>
    #endif
    
    
    #ifdef USE_EIGEN_UNSUPPORTED
    
    #include <unsupported/Eigen/src/IterativeSolvers/GMRES.h>
    
    #include <unsupported/Eigen/src/IterativeSolvers/Scaling.h>
    #endif
    
    
    #include "BaseLib/ConfigTree.h"
    
    #include "EigenMatrix.h"
    #include "EigenTools.h"
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
    #include "EigenVector.h"
    
    #include "MathLib/LinAlg/LinearSolverOptions.h"
    
    
    // TODO change to LinearSolver
    class EigenLinearSolverBase
    {
    public:
        using Vector = EigenVector::RawVectorType;
        using Matrix = EigenMatrix::RawMatrixType;
    
        virtual ~EigenLinearSolverBase() = default;
    
        //! Solves the linear equation system \f$ A x = b \f$ for \f$ x \f$.
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
        virtual bool solve(Matrix& A, Vector const& b, Vector& x,
                           EigenOption& opt) = 0;
    
    /// Template class for Eigen direct linear solvers
    
    template <class T_SOLVER>
    class EigenDirectLinearSolver final : public EigenLinearSolverBase
    
        bool solve(Matrix& A, Vector const& b, Vector& x, EigenOption& opt) override
    
            INFO("-> solve with {:s}", EigenOption::getSolverName(opt.solver_type));
    
            if (!A.isCompressed())
            {
                A.makeCompressed();
            }
    
            solver_.compute(A);
            if (solver_.info() != Eigen::Success)
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            {
    
                ERR("Failed during Eigen linear solver initialization");
    
            x = solver_.solve(b);
            if (solver_.info() != Eigen::Success)
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            {
    
                ERR("Failed during Eigen linear solve");
    
    /// Template class for Eigen iterative linear solvers
    
    template <class T_SOLVER>
    class EigenIterativeLinearSolver final : public EigenLinearSolverBase
    
        bool solve(Matrix& A, Vector const& b, Vector& x, EigenOption& opt) override
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            INFO("-> solve with {:s} (precon {:s})",
    
                 EigenOption::getSolverName(opt.solver_type),
                 EigenOption::getPreconName(opt.precon_type));
    
            solver_.setTolerance(opt.error_tolerance);
            solver_.setMaxIterations(opt.max_iterations);
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            MathLib::details::EigenIterativeLinearSolver<T_SOLVER>::setRestart(
                opt.restart);
    
            solver_.compute(A);
            if (solver_.info() != Eigen::Success)
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            {
    
                ERR("Failed during Eigen linear solver initialization");
    
            x = solver_.solveWithGuess(b, x);
            INFO("\t iteration: {:d}/{:d}", solver_.iterations(),
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
                 opt.max_iterations);
    
            INFO("\t residual: {:e}\n", solver_.error());
    
            if (solver_.info() != Eigen::Success)
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            {
    
                ERR("Failed during Eigen linear solve");
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
        void setRestart(int const /*restart*/) {}
    
    joergbuchwald's avatar
    joergbuchwald committed
    /// Specialization for (all) three preconditioners separately
    
    template <>
    void EigenIterativeLinearSolver<
        Eigen::GMRES<EigenMatrix::RawMatrixType,
                     Eigen::IdentityPreconditioner>>::setRestart(int const restart)
    
        solver_.set_restart(restart);
        INFO("-> set restart value: {:d}", solver_.get_restart());
    
    template <>
    void EigenIterativeLinearSolver<Eigen::GMRES<
        EigenMatrix::RawMatrixType,
        Eigen::DiagonalPreconditioner<double>>>::setRestart(int const restart)
    {
    
        solver_.set_restart(restart);
        INFO("-> set restart value: {:d}", solver_.get_restart());
    
    joergbuchwald's avatar
    joergbuchwald committed
    }
    
    template <>
    
    void EigenIterativeLinearSolver<
        Eigen::GMRES<EigenMatrix::RawMatrixType,
                     Eigen::IncompleteLUT<double>>>::setRestart(int const restart)
    
        solver_.set_restart(restart);
        INFO("-> set restart value: {:d}", solver_.get_restart());
    
    template <template <typename, typename> class Solver, typename Precon>
    std::unique_ptr<EigenLinearSolverBase> createIterativeSolver()
    {
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
        using Slv =
            EigenIterativeLinearSolver<Solver<EigenMatrix::RawMatrixType, Precon>>;
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
        return std::make_unique<Slv>();
    
    }
    
    template <template <typename, typename> class Solver>
    std::unique_ptr<EigenLinearSolverBase> createIterativeSolver(
        EigenOption::PreconType precon_type)
    {
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
        switch (precon_type)
        {
    
            case EigenOption::PreconType::NONE:
                return createIterativeSolver<Solver,
                                             Eigen::IdentityPreconditioner>();
            case EigenOption::PreconType::DIAGONAL:
                return createIterativeSolver<
                    Solver, Eigen::DiagonalPreconditioner<double>>();
            case EigenOption::PreconType::ILUT:
                // TODO for this preconditioner further options can be passed.
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
                // see
                // https://eigen.tuxfamily.org/dox/classEigen_1_1IncompleteLUT.html
                return createIterativeSolver<Solver,
                                             Eigen::IncompleteLUT<double>>();
    
            default:
                OGS_FATAL("Invalid Eigen preconditioner type.");
        }
    }
    
    template <typename Mat, typename Precon>
    using EigenCGSolver = Eigen::ConjugateGradient<Mat, Eigen::Lower, Precon>;
    
    std::unique_ptr<EigenLinearSolverBase> createIterativeSolver(
        EigenOption::SolverType solver_type, EigenOption::PreconType precon_type)
    {
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
        switch (solver_type)
        {
            case EigenOption::SolverType::BiCGSTAB:
            {
    
                return createIterativeSolver<Eigen::BiCGSTAB>(precon_type);
            }
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            case EigenOption::SolverType::CG:
            {
    
                return createIterativeSolver<EigenCGSolver>(precon_type);
            }
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            case EigenOption::SolverType::GMRES:
            {
    
                return createIterativeSolver<Eigen::GMRES>(precon_type);
    
    #else
                OGS_FATAL(
                    "The code is not compiled with the Eigen unsupported modules. "
                    "Linear solver type GMRES is not available.");
    
            default:
                OGS_FATAL("Invalid Eigen iterative linear solver type. Aborting.");
        }
    }
    
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
    }  // namespace details
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
    EigenLinearSolver::EigenLinearSolver(const std::string& /*solver_name*/,
                                         const BaseLib::ConfigTree* const option)
    
        using Matrix = EigenMatrix::RawMatrixType;
    
    
            setOption(*option);
    
        // TODO for my taste it is much too unobvious that the default solver type
        //      currently is SparseLU.
    
        switch (option_.solver_type)
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
        {
            case EigenOption::SolverType::SparseLU:
            {
    
                using SolverType =
                    Eigen::SparseLU<Matrix, Eigen::COLAMDOrdering<int>>;
    
                solver_ = std::make_unique<
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
                    details::EigenDirectLinearSolver<SolverType>>();
    
                return;
            }
            case EigenOption::SolverType::BiCGSTAB:
            case EigenOption::SolverType::CG:
    
            case EigenOption::SolverType::GMRES:
    
                solver_ = details::createIterativeSolver(option_.solver_type,
                                                         option_.precon_type);
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            case EigenOption::SolverType::PardisoLU:
            {
    
                using SolverType = Eigen::PardisoLU<EigenMatrix::RawMatrixType>;
    
                solver_.reset(new details::EigenDirectLinearSolver<SolverType>);
    
    #else
                OGS_FATAL(
                    "The code is not compiled with Intel MKL. Linear solver type "
                    "PardisoLU is not available.");
    
    
        OGS_FATAL("Invalid Eigen linear solver type. Aborting.");
    
    EigenLinearSolver::~EigenLinearSolver() = default;
    
    
    void EigenLinearSolver::setOption(BaseLib::ConfigTree const& option)
    
        ignoreOtherLinearSolvers(option, "eigen");
    
        //! \ogs_file_param{prj__linear_solvers__linear_solver__eigen}
    
        auto const ptSolver = option.getConfigSubtreeOptional("eigen");
    
        if (auto solver_type =
    
                //! \ogs_file_param{prj__linear_solvers__linear_solver__eigen__solver_type}
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            ptSolver->getConfigParameterOptional<std::string>("solver_type"))
        {
    
            option_.solver_type = MathLib::EigenOption::getSolverType(*solver_type);
    
        if (auto precon_type =
    
                //! \ogs_file_param{prj__linear_solvers__linear_solver__eigen__precon_type}
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            ptSolver->getConfigParameterOptional<std::string>("precon_type"))
        {
    
            option_.precon_type = MathLib::EigenOption::getPreconType(*precon_type);
    
        if (auto error_tolerance =
    
                //! \ogs_file_param{prj__linear_solvers__linear_solver__eigen__error_tolerance}
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            ptSolver->getConfigParameterOptional<double>("error_tolerance"))
        {
    
            option_.error_tolerance = *error_tolerance;
    
        if (auto max_iteration_step =
    
                //! \ogs_file_param{prj__linear_solvers__linear_solver__eigen__max_iteration_step}
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            ptSolver->getConfigParameterOptional<int>("max_iteration_step"))
        {
    
            option_.max_iterations = *max_iteration_step;
    
        if (auto scaling =
    
                //! \ogs_file_param{prj__linear_solvers__linear_solver__eigen__scaling}
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            ptSolver->getConfigParameterOptional<bool>("scaling"))
        {
    
            option_.scaling = *scaling;
    
    #else
            OGS_FATAL(
                "The code is not compiled with the Eigen unsupported modules. "
                "scaling is not available.");
    
    joergbuchwald's avatar
    joergbuchwald committed
        if (auto restart =
                //! \ogs_file_param{prj__linear_solvers__linear_solver__eigen__restart}
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            ptSolver->getConfigParameterOptional<int>("restart"))
        {
    
    joergbuchwald's avatar
    joergbuchwald committed
    #ifdef USE_EIGEN_UNSUPPORTED
    
            option_.restart = *restart;
    
    joergbuchwald's avatar
    joergbuchwald committed
    #else
            OGS_FATAL(
                "The code is not compiled with the Eigen unsupported modules. "
    
                "GMRES/GMRES option restart is not available.");
    
    joergbuchwald's avatar
    joergbuchwald committed
    #endif
        }
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
    bool EigenLinearSolver::solve(EigenMatrix& A, EigenVector& b, EigenVector& x)
    
    {
        INFO("------------------------------------------------------------------");
        INFO("*** Eigen solver computation");
    
    #ifdef USE_EIGEN_UNSUPPORTED
        std::unique_ptr<Eigen::IterScaling<EigenMatrix::RawMatrixType>> scal;
    
        {
            INFO("-> scale");
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
            scal =
                std::make_unique<Eigen::IterScaling<EigenMatrix::RawMatrixType>>();
    
            scal->computeRef(A.getRawMatrix());
            b.getRawVector() = scal->LeftScaling().cwiseProduct(b.getRawVector());
        }
    #endif
    
        auto const success = solver_->solve(A.getRawMatrix(), b.getRawVector(),
                                            x.getRawVector(), option_);
    
    #ifdef USE_EIGEN_UNSUPPORTED
        if (scal)
    
            x.getRawVector() = scal->RightScaling().cwiseProduct(x.getRawVector());
    
        INFO("------------------------------------------------------------------");
    
    Dmitri Naumov's avatar
    Dmitri Naumov committed
    }  // namespace MathLib