From c72934b2bf57e85a4edabf650c9b439eac26b7c8 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Thu, 23 Jan 2025 09:00:27 +0100 Subject: [PATCH 1/8] Begin Eigen3 interfacing [ci-skip] --- CMakeLists.txt | 30 +++ src/algebra/Eigen3Utils.hpp | 116 ++++++++++ src/algebra/LinearSolver.cpp | 55 +++++ src/algebra/LinearSolverOptions.hpp | 4 + src/utils/BuildInfo.cpp | 14 ++ src/utils/BuildInfo.hpp | 1 + src/utils/PugsUtils.cpp | 1 + src/utils/pugs_config.hpp.in | 1 + tests/CMakeLists.txt | 1 + tests/test_Eigen3Utils.cpp | 107 +++++++++ tests/test_LinearSolver.cpp | 344 +++++++++++++++++++++++++++- tests/test_LinearSolverOptions.cpp | 1 + tests/test_PugsUtils.cpp | 1 + 13 files changed, 675 insertions(+), 1 deletion(-) create mode 100644 src/algebra/Eigen3Utils.hpp create mode 100644 tests/test_Eigen3Utils.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e0b29900..52579983f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -245,6 +245,26 @@ else() endif() endif() +# ----------------------------------------------------- +# Check for Eigen3 +# defaults use Eigen3 +set(PUGS_ENABLE_EIGEN3 AUTO CACHE STRING + "Choose one of: AUTO ON OFF") + +if (PUGS_ENABLE_EIGEN3 MATCHES "^(AUTO|ON)$") + find_package (Eigen3 NO_MODULE) + + if (TARGET Eigen3::Eigen) + set(EIGEN3_TARGET Eigen3::Eigen) + set(EIGEN3_FOUND TRUE) + else() + set(EIGEN3_FOUND FALSE) + endif (TARGET Eigen3::Eigen) + set(PUGS_HAS_EIGEN3 ${EIGEN3_FOUND}) +else() + unset(PUGS_HAS_EIGEN3) +endif() + # ----------------------------------------------------- if (${MPI_FOUND}) @@ -829,6 +849,16 @@ else() endif() endif() +if (EIGEN3_FOUND) + message(" Eigen3: ${Eigen3_VERSION}") +else() + if (PUGS_ENABLE_EIGEN3 MATCHES "^(AUTO|ON)$") + message(" Eigen3: not found!") + else() + message(" Eigen3: explicitly deactivated!") + endif() +endif() + if (HDF5_FOUND) message(" HDF5: ${HDF5_VERSION} parallel: ${HDF5_IS_PARALLEL}") else() diff --git a/src/algebra/Eigen3Utils.hpp b/src/algebra/Eigen3Utils.hpp new file mode 100644 index 000000000..14434fe94 --- /dev/null +++ b/src/algebra/Eigen3Utils.hpp @@ -0,0 +1,116 @@ +#ifndef EIGEN3_UTILS_HPP +#define EIGEN3_UTILS_HPP + +#include <utils/pugs_config.hpp> + +#ifdef PUGS_HAS_EIGEN3 + +#include <algebra/CRSMatrix.hpp> +#include <algebra/SmallMatrix.hpp> +#include <algebra/TinyMatrix.hpp> + +#include <eigen3/Eigen/Eigen> + +class Eigen3DenseMatrixEmbedder +{ + public: + using MatrixType = Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>; + + private: + MatrixType m_eigen_matrix; + + Eigen3DenseMatrixEmbedder(const size_t nb_rows, const size_t nb_columns, const double* A) + : m_eigen_matrix{A, int(nb_rows), int(nb_columns)} + {} + + public: + PUGS_INLINE + size_t + numberOfRows() const + { + return m_eigen_matrix.rows(); + } + + PUGS_INLINE + size_t + numberOfColumns() const + { + return m_eigen_matrix.cols(); + } + + PUGS_INLINE + MatrixType& + matrix() + { + return m_eigen_matrix; + } + + PUGS_INLINE + const MatrixType& + matrix() const + { + return m_eigen_matrix; + } + + template <size_t N> + Eigen3DenseMatrixEmbedder(const TinyMatrix<N>& A) : Eigen3DenseMatrixEmbedder{N, N, &A(0, 0)} + {} + + Eigen3DenseMatrixEmbedder(const SmallMatrix<double>& A) + : Eigen3DenseMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), &A(0, 0)} + {} + + ~Eigen3DenseMatrixEmbedder() = default; +}; + +class Eigen3SparseMatrixEmbedder +{ + public: + using MatrixType = Eigen::Map<const Eigen::SparseMatrix<double, Eigen::RowMajor>>; + + private: + MatrixType m_eigen_matrix; + + public: + PUGS_INLINE + size_t + numberOfRows() const + { + return m_eigen_matrix.rows(); + } + + PUGS_INLINE + size_t + numberOfColumns() const + { + return m_eigen_matrix.cols(); + } + + PUGS_INLINE + MatrixType& + matrix() + { + return m_eigen_matrix; + } + + PUGS_INLINE + const MatrixType& + matrix() const + { + return m_eigen_matrix; + } + + Eigen3SparseMatrixEmbedder(const CRSMatrix<double>& A) + : m_eigen_matrix{A.numberOfRows(), + A.numberOfColumns(), + static_cast<int>(A.values().size()), // + (A.rowMap().size() > 0) ? &(A.rowMap()[0]) : nullptr, // + (A.columnIndices().size() > 0) ? &(A.columnIndices()[0]) : nullptr, // + (A.values().size() > 0) ? &(A.values()[0]) : nullptr} + {} + ~Eigen3SparseMatrixEmbedder() = default; +}; + +#endif // PUGS_HAS_EIGEN3 + +#endif // EIGEN3_UTILS_HPP diff --git a/src/algebra/LinearSolver.cpp b/src/algebra/LinearSolver.cpp index 99bff927c..7504ced5f 100644 --- a/src/algebra/LinearSolver.cpp +++ b/src/algebra/LinearSolver.cpp @@ -18,6 +18,13 @@ struct LinearSolver::Internals case LSLibrary::builtin: { return true; } + case LSLibrary::eigen3: { +#ifdef PUGS_HAS_EIGEN3 + return true; +#else + return false; +#endif + } case LSLibrary::petsc: { #ifdef PUGS_HAS_PETSC return true; @@ -57,6 +64,28 @@ struct LinearSolver::Internals } } + static void + checkEigen3Method(const LSMethod method) + { + switch (method) { + case LSMethod::cg: +#warning clean-up + // case LSMethod::bicgstab: + // case LSMethod::bicgstab2: + // case LSMethod::gmres: + // case LSMethod::lu: + // case LSMethod::cholesky: + { + break; + } + // LCOV_EXCL_START + default: { + throw NormalError(name(method) + " is not an Eigen3 linear solver!"); + } + // LCOV_EXCL_STOP + } + } + static void checkPETScMethod(const LSMethod method) { @@ -90,6 +119,27 @@ struct LinearSolver::Internals } } + static void + checkEigen3Precond(const LSPrecond precond) + { + switch (precond) { + case LSPrecond::none: +#warning clean-up + // case LSPrecond::amg: + // case LSPrecond::diagonal: + // case LSPrecond::incomplete_cholesky: + // case LSPrecond::incomplete_LU: + { + break; + } + // LCOV_EXCL_START + default: { + throw NormalError(name(precond) + " is not an Eigen3 preconditioner!"); + } + // LCOV_EXCL_STOP + } + } + static void checkPETScPrecond(const LSPrecond precond) { @@ -118,6 +168,11 @@ struct LinearSolver::Internals checkBuiltinPrecond(options.precond()); break; } + case LSLibrary::eigen3: { + checkEigen3Method(options.method()); + checkEigen3Precond(options.precond()); + break; + } case LSLibrary::petsc: { checkPETScMethod(options.method()); checkPETScPrecond(options.precond()); diff --git a/src/algebra/LinearSolverOptions.hpp b/src/algebra/LinearSolverOptions.hpp index 940ce1ed6..97a174878 100644 --- a/src/algebra/LinearSolverOptions.hpp +++ b/src/algebra/LinearSolverOptions.hpp @@ -10,6 +10,7 @@ enum class LSLibrary : int8_t LS__begin = 0, // builtin = LS__begin, + eigen3, petsc, // LS__end @@ -49,6 +50,9 @@ name(const LSLibrary library) case LSLibrary::builtin: { return "builtin"; } + case LSLibrary::eigen3: { + return "Eigen3"; + } case LSLibrary::petsc: { return "PETSc"; } diff --git a/src/utils/BuildInfo.cpp b/src/utils/BuildInfo.cpp index 81d756775..2413bdbae 100644 --- a/src/utils/BuildInfo.cpp +++ b/src/utils/BuildInfo.cpp @@ -17,6 +17,10 @@ #include <slepc.h> #endif // PUGS_HAS_PETSC +#ifdef PUGS_HAS_EIGEN3 +#include <eigen3/Eigen/Eigen> +#endif // PUGS_HAS_EIGEN3 + #ifdef PUGS_HAS_HDF5 #include <highfive/highfive.hpp> #endif // PUGS_HAS_HDF5 @@ -82,6 +86,16 @@ BuildInfo::slepcLibrary() #endif // PUGS_HAS_SLEPC } +std::string +BuildInfo::eigen3Library() +{ +#ifdef PUGS_HAS_EIGEN3 + return stringify(EIGEN_WORLD_VERSION) + "." + stringify(EIGEN_MAJOR_VERSION) + "." + stringify(EIGEN_MINOR_VERSION); +#else // PUGS_HAS_EIGEN3 + return "none"; +#endif // PUGS_HAS_EIGEN3 +} + std::string BuildInfo::hdf5Library() { diff --git a/src/utils/BuildInfo.hpp b/src/utils/BuildInfo.hpp index df4a32d93..86f7c04f0 100644 --- a/src/utils/BuildInfo.hpp +++ b/src/utils/BuildInfo.hpp @@ -11,6 +11,7 @@ struct BuildInfo static std::string mpiLibrary(); static std::string petscLibrary(); static std::string slepcLibrary(); + static std::string eigen3Library(); static std::string hdf5Library(); static std::string slurmLibrary(); }; diff --git a/src/utils/PugsUtils.cpp b/src/utils/PugsUtils.cpp index 1ac38b326..ac64a50cf 100644 --- a/src/utils/PugsUtils.cpp +++ b/src/utils/PugsUtils.cpp @@ -64,6 +64,7 @@ pugsBuildInfo() os << "MPI: " << rang::style::bold << BuildInfo::mpiLibrary() << rang::style::reset << '\n'; os << "PETSc: " << rang::style::bold << BuildInfo::petscLibrary() << rang::style::reset << '\n'; os << "SLEPc: " << rang::style::bold << BuildInfo::slepcLibrary() << rang::style::reset << '\n'; + os << "Eigen3: " << rang::style::bold << BuildInfo::eigen3Library() << rang::style::reset << '\n'; os << "HDF5: " << rang::style::bold << BuildInfo::hdf5Library() << rang::style::reset << '\n'; os << "SLURM: " << rang::style::bold << BuildInfo::slurmLibrary() << rang::style::reset << '\n'; os << "-------------------------------------------------------"; diff --git a/src/utils/pugs_config.hpp.in b/src/utils/pugs_config.hpp.in index 7402a526d..a07d8c314 100644 --- a/src/utils/pugs_config.hpp.in +++ b/src/utils/pugs_config.hpp.in @@ -5,6 +5,7 @@ #cmakedefine PUGS_HAS_MPI #cmakedefine PUGS_HAS_PETSC #cmakedefine PUGS_HAS_SLEPC +#cmakedefine PUGS_HAS_EIGEN3 #cmakedefine PUGS_HAS_HDF5 #cmakedefine PUGS_HAS_SLURM diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 23a4ac309..1dd13e6e3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -104,6 +104,7 @@ add_executable (unit_tests test_DualMeshManager.cpp test_DualMeshType.cpp test_EdgeIntegrator.cpp + test_Eigen3Utils.cpp test_EigenvalueSolver.cpp test_EmbeddedData.cpp test_EmbeddedDiscreteFunctionUtils.cpp diff --git a/tests/test_Eigen3Utils.cpp b/tests/test_Eigen3Utils.cpp new file mode 100644 index 000000000..0c51aad3c --- /dev/null +++ b/tests/test_Eigen3Utils.cpp @@ -0,0 +1,107 @@ +#include <utils/pugs_config.hpp> + +#ifdef PUGS_HAS_EIGEN3 + +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <algebra/Eigen3Utils.hpp> + +#include <algebra/CRSMatrixDescriptor.hpp> + +#include <eigen3/Eigen/Core> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("Eigen3Utils", "[algebra]") +{ + SECTION("Eigen3DenseMatrixEmbedder") + { + SECTION("from TinyMatrix") + { + TinyMatrix<3> A{1, 2, 3, 4, 5, 6, 7, 8, 9}; + Eigen3DenseMatrixEmbedder eigen3_A{A}; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(eigen3_A.matrix().coeff(i, j) == 1 + i * 3 + j); + } + } + } + + SECTION("from SmallMatrix") + { + SmallMatrix<double> A(3, 3); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + A(i, j) = 1 + i * 3 + j; + } + } + + Eigen3DenseMatrixEmbedder eigen3_A{A}; + + REQUIRE(eigen3_A.numberOfRows() == 3); + REQUIRE(eigen3_A.numberOfColumns() == 3); + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(eigen3_A.matrix().coeff(i, j) == 1 + i * 3 + j); + } + } + } + + SECTION("from SmallMatrix [non-square]") + { + SmallMatrix<double> A(4, 3); + for (size_t i = 0; i < 4; ++i) { + for (size_t j = 0; j < 3; ++j) { + A(i, j) = 1 + i * 3 + j; + } + } + + Eigen3DenseMatrixEmbedder eigen3_A{A}; + + REQUIRE(eigen3_A.numberOfRows() == 4); + REQUIRE(eigen3_A.numberOfColumns() == 3); + + for (size_t i = 0; i < 4; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(eigen3_A.matrix().coeff(i, j) == 1 + i * 3 + j); + } + } + } + } + + SECTION("from CRSMatrix") + { + Array<int> non_zeros(4); + non_zeros[0] = 1; + non_zeros[1] = 2; + non_zeros[2] = 3; + non_zeros[3] = 2; + + CRSMatrixDescriptor<double, int> A(4, 3, non_zeros); + + A(0, 0) = 1; + A(1, 0) = 2; + A(1, 2) = 3; + A(2, 0) = 4; + A(2, 1) = 5; + A(2, 2) = 6; + A(3, 1) = 7; + A(3, 2) = 8; + + Eigen3SparseMatrixEmbedder eigen3_A{A.getCRSMatrix()}; + + REQUIRE(eigen3_A.matrix().coeff(0, 0) == 1); + REQUIRE(eigen3_A.matrix().coeff(1, 0) == 2); + REQUIRE(eigen3_A.matrix().coeff(1, 2) == 3); + REQUIRE(eigen3_A.matrix().coeff(2, 0) == 4); + REQUIRE(eigen3_A.matrix().coeff(2, 1) == 5); + REQUIRE(eigen3_A.matrix().coeff(2, 2) == 6); + REQUIRE(eigen3_A.matrix().coeff(3, 1) == 7); + REQUIRE(eigen3_A.matrix().coeff(3, 2) == 8); + } +} + +#endif // PUGS_HAS_EIGEN3 diff --git a/tests/test_LinearSolver.cpp b/tests/test_LinearSolver.cpp index 11195faab..01c71cbea 100644 --- a/tests/test_LinearSolver.cpp +++ b/tests/test_LinearSolver.cpp @@ -21,6 +21,12 @@ TEST_CASE("LinearSolver", "[algebra]") #else // PUGS_HAS_PETSC REQUIRE(linear_solver.hasLibrary(LSLibrary::petsc) == false); #endif // PUGS_HAS_PETSC + +#ifdef PUGS_HAS_EIGEN3 + REQUIRE(linear_solver.hasLibrary(LSLibrary::eigen3) == true); +#else // PUGS_HAS_PETSC + REQUIRE(linear_solver.hasLibrary(LSLibrary::eigen3) == false); +#endif // PUGS_HAS_PETSC } SECTION("check linear solver building") @@ -108,7 +114,7 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE_NOTHROW(linear_solver.checkOptions(options)); } - SECTION("builtin precond") + SECTION("PETSc precond") { options.library() = LSLibrary::petsc; options.method() = LSMethod::cg; @@ -140,6 +146,72 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!"); } #endif // PUGS_HAS_PETSC + + SECTION("Eigen3") + { + LinearSolverOptions always_valid; + always_valid.library() = LSLibrary::builtin; + always_valid.method() = LSMethod::cg; + always_valid.precond() = LSPrecond::none; + + LinearSolver linear_solver{always_valid}; + + SECTION("Eigen3 methods") + { + options.library() = LSLibrary::eigen3; + options.precond() = LSPrecond::none; + + options.method() = LSMethod::cg; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.method() = LSMethod::bicgstab; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.method() = LSMethod::bicgstab2; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.method() = LSMethod::lu; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.method() = LSMethod::cholesky; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.method() = LSMethod::gmres; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + } + + SECTION("Eigen3 precond") + { + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + + options.precond() = LSPrecond::none; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.precond() = LSPrecond::diagonal; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.precond() = LSPrecond::incomplete_LU; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.precond() = LSPrecond::incomplete_cholesky; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + + options.precond() = LSPrecond::amg; + REQUIRE_NOTHROW(linear_solver.checkOptions(options)); + } + } + +#ifndef PUGS_HAS_EIGEN3 + SECTION("not linked Eigen3") + { + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_EIGEN3 } SECTION("Sparse Matrices") @@ -205,6 +277,105 @@ TEST_CASE("LinearSolver", "[algebra]") } } + SECTION("Eigen3") + { +#ifdef PUGS_HAS_EIGEN3 + + SECTION("CG") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + options.verbose() = true; + + SECTION("CG no preconditioner") + { + options.precond() = LSPrecond::none; + + Vector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } +#warning test all options + // SECTION("CG Diagonal") + // { + // options.precond() = LSPrecond::diagonal; + + // Vector<double> x{5}; + // x = zero; + + // LinearSolver solver{options}; + + // solver.solveLocalSystem(A, x, b); + // Vector error = x - x_exact; + // REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + // } + + // SECTION("CG ICholesky") + // { + // options.precond() = LSPrecond::incomplete_cholesky; + + // Vector<double> x{5}; + // x = zero; + + // LinearSolver solver{options}; + + // solver.solveLocalSystem(A, x, b); + // Vector error = x - x_exact; + // REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + // } + + // SECTION("CG AMG") + // { + // options.precond() = LSPrecond::amg; + + // Vector<double> x{5}; + // x = zero; + + // LinearSolver solver{options}; + + // solver.solveLocalSystem(A, x, b); + // Vector error = x - x_exact; + // REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + // } + } + + SECTION("Cholesky") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cholesky; + options.precond() = LSPrecond::none; + + Vector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + +#else // PUGS_HAS_EIGEN3 + SECTION("Eigen3 not linked") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_EIGEN3 + } + SECTION("PETSc") { #ifdef PUGS_HAS_PETSC @@ -753,6 +924,177 @@ TEST_CASE("LinearSolver", "[algebra]") } } + SECTION("Eigen3") + { +#ifdef PUGS_HAS_EIGEN3 + + SECTION("BICGStab") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::bicgstab; + options.precond() = LSPrecond::none; + options.verbose() = true; + + SECTION("BICGStab no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("BICGStab2") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::bicgstab2; + options.precond() = LSPrecond::none; + + SECTION("BICGStab2 no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab2 Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("GMRES") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::gmres; + options.precond() = LSPrecond::none; + + SECTION("GMRES no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("LU") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::lu; + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + +#else // PUGS_HAS_EIGEN3 + SECTION("Eigen3 not linked") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_EIGEN3 + } + SECTION("PETSc") { #ifdef PUGS_HAS_PETSC diff --git a/tests/test_LinearSolverOptions.cpp b/tests/test_LinearSolverOptions.cpp index 90519ddf9..620959714 100644 --- a/tests/test_LinearSolverOptions.cpp +++ b/tests/test_LinearSolverOptions.cpp @@ -135,6 +135,7 @@ TEST_CASE("LinearSolverOptions", "[algebra]") const std::string library_list = R"( - builtin + - Eigen3 - PETSc )"; diff --git a/tests/test_PugsUtils.cpp b/tests/test_PugsUtils.cpp index fb8648652..5ba8817fc 100644 --- a/tests/test_PugsUtils.cpp +++ b/tests/test_PugsUtils.cpp @@ -51,6 +51,7 @@ TEST_CASE("PugsUtils", "[utils]") os << "MPI: " << rang::style::bold << BuildInfo::mpiLibrary() << rang::style::reset << '\n'; os << "PETSc: " << rang::style::bold << BuildInfo::petscLibrary() << rang::style::reset << '\n'; os << "SLEPc: " << rang::style::bold << BuildInfo::slepcLibrary() << rang::style::reset << '\n'; + os << "Eigen3: " << rang::style::bold << BuildInfo::eigen3Library() << rang::style::reset << '\n'; os << "HDF5: " << rang::style::bold << BuildInfo::hdf5Library() << rang::style::reset << '\n'; os << "SLURM: " << rang::style::bold << BuildInfo::slurmLibrary() << rang::style::reset << '\n'; os << "-------------------------------------------------------"; -- GitLab From b1ebe1e7048fd1947e00507c72451f6ba52da0d3 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Sat, 25 Jan 2025 12:57:19 +0100 Subject: [PATCH 2/8] Plug a bunch of Eigen3 linear solvers --- src/algebra/Eigen3Utils.hpp | 18 +- src/algebra/LinearSolver.cpp | 197 ++++++++- tests/test_LinearSolver.cpp | 754 +++++++++++++++++++++++------------ 3 files changed, 686 insertions(+), 283 deletions(-) diff --git a/src/algebra/Eigen3Utils.hpp b/src/algebra/Eigen3Utils.hpp index 14434fe94..5224a4b0b 100644 --- a/src/algebra/Eigen3Utils.hpp +++ b/src/algebra/Eigen3Utils.hpp @@ -14,10 +14,11 @@ class Eigen3DenseMatrixEmbedder { public: - using MatrixType = Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>; + using Eigen3MatrixType = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; + using Eigen3MapMatrixType = Eigen::Map<const Eigen3MatrixType>; private: - MatrixType m_eigen_matrix; + Eigen3MapMatrixType m_eigen_matrix; Eigen3DenseMatrixEmbedder(const size_t nb_rows, const size_t nb_columns, const double* A) : m_eigen_matrix{A, int(nb_rows), int(nb_columns)} @@ -39,14 +40,14 @@ class Eigen3DenseMatrixEmbedder } PUGS_INLINE - MatrixType& + Eigen3MapMatrixType& matrix() { return m_eigen_matrix; } PUGS_INLINE - const MatrixType& + const Eigen3MapMatrixType& matrix() const { return m_eigen_matrix; @@ -66,10 +67,11 @@ class Eigen3DenseMatrixEmbedder class Eigen3SparseMatrixEmbedder { public: - using MatrixType = Eigen::Map<const Eigen::SparseMatrix<double, Eigen::RowMajor>>; + using Eigen3MatrixType = Eigen::SparseMatrix<double, Eigen::RowMajor>; + using Eigen3MapMatrixType = Eigen::Map<const Eigen3MatrixType>; private: - MatrixType m_eigen_matrix; + Eigen3MapMatrixType m_eigen_matrix; public: PUGS_INLINE @@ -87,14 +89,14 @@ class Eigen3SparseMatrixEmbedder } PUGS_INLINE - MatrixType& + Eigen3MapMatrixType& matrix() { return m_eigen_matrix; } PUGS_INLINE - const MatrixType& + const Eigen3MapMatrixType& matrix() const { return m_eigen_matrix; diff --git a/src/algebra/LinearSolver.cpp b/src/algebra/LinearSolver.cpp index 7504ced5f..1ebb59663 100644 --- a/src/algebra/LinearSolver.cpp +++ b/src/algebra/LinearSolver.cpp @@ -3,8 +3,13 @@ #include <algebra/BiCGStab.hpp> #include <algebra/CG.hpp> +#include <algebra/Eigen3Utils.hpp> #include <algebra/PETScUtils.hpp> +#ifdef PUGS_HAS_EIGEN3 +#include <eigen3/unsupported/Eigen/IterativeSolvers> +#endif // PUGS_HAS_EIGEN3 + #ifdef PUGS_HAS_PETSC #include <petsc.h> #endif // PUGS_HAS_PETSC @@ -69,15 +74,12 @@ struct LinearSolver::Internals { switch (method) { case LSMethod::cg: -#warning clean-up - // case LSMethod::bicgstab: - // case LSMethod::bicgstab2: - // case LSMethod::gmres: - // case LSMethod::lu: - // case LSMethod::cholesky: - { - break; - } + case LSMethod::bicgstab: + case LSMethod::gmres: + case LSMethod::lu: + case LSMethod::cholesky: { + break; + } // LCOV_EXCL_START default: { throw NormalError(name(method) + " is not an Eigen3 linear solver!"); @@ -124,14 +126,11 @@ struct LinearSolver::Internals { switch (precond) { case LSPrecond::none: -#warning clean-up - // case LSPrecond::amg: - // case LSPrecond::diagonal: - // case LSPrecond::incomplete_cholesky: - // case LSPrecond::incomplete_LU: - { - break; - } + case LSPrecond::diagonal: + case LSPrecond::incomplete_cholesky: + case LSPrecond::incomplete_LU: { + break; + } // LCOV_EXCL_START default: { throw NormalError(name(precond) + " is not an Eigen3 preconditioner!"); @@ -215,6 +214,163 @@ struct LinearSolver::Internals } } +#ifdef PUGS_HAS_EIGEN3 + template <typename PreconditionerType, + typename Eigen3MatrixEmbedderType, + typename SolutionVectorType, + typename RHSVectorType> + static void + _preconditionedEigen3SolveLocalSystem(const Eigen3MatrixEmbedderType& eigen3_A, + SolutionVectorType& x, + const RHSVectorType& b, + const LinearSolverOptions& options) + { + Eigen::Map<Eigen::VectorX<double>> eigen3_x{(x.size() > 0) ? (&x[0]) : nullptr, static_cast<int>(x.size())}; + Eigen::Map<const Eigen::VectorX<double>> eigen3_b{(b.size() > 0) ? (&b[0]) : nullptr, static_cast<int>(b.size())}; + + using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType; + + switch (options.method()) { + case LSMethod::bicgstab: { + Eigen::BiCGSTAB<Eigen3MatrixType, PreconditionerType> solver; + solver.compute(eigen3_A.matrix()); + solver.setMaxIterations(options.maximumIteration()); + solver.setTolerance(options.epsilon()); + eigen3_x = solver.solve(eigen3_b); + break; + } + case LSMethod::cg: { + Eigen::ConjugateGradient<Eigen3MatrixType, Eigen::Lower | Eigen::Upper, PreconditionerType> solver; + solver.compute(eigen3_A.matrix()); + solver.setMaxIterations(options.maximumIteration()); + solver.setTolerance(options.epsilon()); + eigen3_x = solver.solve(eigen3_b); + break; + } + case LSMethod::gmres: { + Eigen::GMRES<Eigen3MatrixType, PreconditionerType> solver; + solver.compute(eigen3_A.matrix()); + solver.setMaxIterations(options.maximumIteration()); + solver.setTolerance(options.epsilon()); + eigen3_x = solver.solve(eigen3_b); + break; + } + case LSMethod::lu: { + if constexpr (std::is_same_v<Eigen3MatrixEmbedderType, Eigen3DenseMatrixEmbedder>) { + Eigen::FullPivLU<Eigen3MatrixType> solver; + solver.compute(eigen3_A.matrix()); + eigen3_x = solver.solve(eigen3_b); + } else { + Eigen::SparseLU<Eigen3MatrixType> solver; + solver.compute(eigen3_A.matrix()); + eigen3_x = solver.solve(eigen3_b); + } + break; + } + case LSMethod::cholesky: { + if constexpr (std::is_same_v<Eigen3MatrixEmbedderType, Eigen3DenseMatrixEmbedder>) { + Eigen::LLT<Eigen3MatrixType> solver; + solver.compute(eigen3_A.matrix()); + eigen3_x = solver.solve(eigen3_b); + } else { + Eigen::SimplicialLLT<Eigen3MatrixType> solver; + solver.compute(eigen3_A.matrix()); + eigen3_x = solver.solve(eigen3_b); + } + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected method: " + name(options.method())); + } + // LCOV_EXCL_STOP + } + } + + template <typename Eigen3MatrixEmbedderType, typename SolutionVectorType, typename RHSVectorType> + static void + _eigen3SolveLocalSystem(const Eigen3MatrixEmbedderType& eigen3_A, + SolutionVectorType& x, + const RHSVectorType& b, + const LinearSolverOptions& options) + { + switch (options.precond()) { + case LSPrecond::none: { + _preconditionedEigen3SolveLocalSystem<Eigen::IdentityPreconditioner, Eigen3MatrixEmbedderType, SolutionVectorType, + RHSVectorType>(eigen3_A, x, b, options); + break; + } + case LSPrecond::diagonal: { + _preconditionedEigen3SolveLocalSystem<Eigen::DiagonalPreconditioner<double>, Eigen3MatrixEmbedderType, + SolutionVectorType, RHSVectorType>(eigen3_A, x, b, options); + break; + } + case LSPrecond::incomplete_cholesky: { + if constexpr (std::is_same_v<Eigen3SparseMatrixEmbedder, Eigen3MatrixEmbedderType>) { + _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteCholesky<double>, Eigen3MatrixEmbedderType, + SolutionVectorType, RHSVectorType>(eigen3_A, x, b, options); + } else { + throw NormalError("incomplete cholesky is not available for dense matrices in Eigen3"); + } + break; + } + case LSPrecond::incomplete_LU: { + if constexpr (std::is_same_v<Eigen3SparseMatrixEmbedder, Eigen3MatrixEmbedderType>) { + _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteLUT<double>, Eigen3MatrixEmbedderType, + SolutionVectorType, RHSVectorType>(eigen3_A, x, b, options); + } else { + throw NormalError("incomplete LU is not available for dense matrices in Eigen3"); + } + break; + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("unexpected preconditioner: " + name(options.precond())); + } + // LCOV_EXCL_STOP + } + } + + template <typename DataType, typename SolutionVectorType, typename RHSVectorType> + static void + eigen3SolveLocalSystem(const CRSMatrix<DataType>& A, + SolutionVectorType& x, + const RHSVectorType& b, + const LinearSolverOptions& options) + { + Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); + + Eigen3SparseMatrixEmbedder eigen3_A{A}; + _eigen3SolveLocalSystem(eigen3_A, x, b, options); + } + + template <typename DataType, typename SolutionVectorType, typename RHSVectorType> + static void + eigen3SolveLocalSystem(const SmallMatrix<DataType>& A, + SolutionVectorType& x, + const RHSVectorType& b, + const LinearSolverOptions& options) + { + Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); + + Eigen3DenseMatrixEmbedder eigen3_A{A}; + _eigen3SolveLocalSystem(eigen3_A, x, b, options); + } + +#else // PUGS_HAS_EIGEN3 + + // LCOV_EXCL_START + template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> + static void + eigen3SolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&) + { + checkHasLibrary(LSLibrary::eigen3); + throw UnexpectedError("unexpected situation should not reach this point!"); + } + // LCOV_EXCL_STOP + +#endif // PUGS_HAS_EIGEN3 + #ifdef PUGS_HAS_PETSC static int petscMonitor(KSP, int i, double residu, void*) @@ -357,6 +513,13 @@ struct LinearSolver::Internals break; } // LCOV_EXCL_START + case LSLibrary::eigen3: { + // not covered since if Eigen3 is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + eigen3SolveLocalSystem(A, x, b, options); + break; + } case LSLibrary::petsc: { // not covered since if PETSc is not linked this point is // unreachable: LinearSolver throws an exception at construction diff --git a/tests/test_LinearSolver.cpp b/tests/test_LinearSolver.cpp index 01c71cbea..4d01df2f0 100644 --- a/tests/test_LinearSolver.cpp +++ b/tests/test_LinearSolver.cpp @@ -167,9 +167,6 @@ TEST_CASE("LinearSolver", "[algebra]") options.method() = LSMethod::bicgstab; REQUIRE_NOTHROW(linear_solver.checkOptions(options)); - options.method() = LSMethod::bicgstab2; - REQUIRE_NOTHROW(linear_solver.checkOptions(options)); - options.method() = LSMethod::lu; REQUIRE_NOTHROW(linear_solver.checkOptions(options)); @@ -196,9 +193,6 @@ TEST_CASE("LinearSolver", "[algebra]") options.precond() = LSPrecond::incomplete_cholesky; REQUIRE_NOTHROW(linear_solver.checkOptions(options)); - - options.precond() = LSPrecond::amg; - REQUIRE_NOTHROW(linear_solver.checkOptions(options)); } } @@ -302,48 +296,44 @@ TEST_CASE("LinearSolver", "[algebra]") Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } -#warning test all options - // SECTION("CG Diagonal") - // { - // options.precond() = LSPrecond::diagonal; - // Vector<double> x{5}; - // x = zero; + SECTION("CG Diagonal") + { + options.precond() = LSPrecond::diagonal; - // LinearSolver solver{options}; + Vector<double> x{5}; + x = zero; - // solver.solveLocalSystem(A, x, b); - // Vector error = x - x_exact; - // REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - // } + LinearSolver solver{options}; - // SECTION("CG ICholesky") - // { - // options.precond() = LSPrecond::incomplete_cholesky; + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } - // Vector<double> x{5}; - // x = zero; + SECTION("CG ICholesky") + { + options.precond() = LSPrecond::incomplete_cholesky; - // LinearSolver solver{options}; + Vector<double> x{5}; + x = zero; - // solver.solveLocalSystem(A, x, b); - // Vector error = x - x_exact; - // REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - // } + LinearSolver solver{options}; - // SECTION("CG AMG") - // { - // options.precond() = LSPrecond::amg; + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } - // Vector<double> x{5}; - // x = zero; + SECTION("CG AMG") + { + options.precond() = LSPrecond::amg; - // LinearSolver solver{options}; + Vector<double> x{5}; + x = zero; - // solver.solveLocalSystem(A, x, b); - // Vector error = x - x_exact; - // REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - // } + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: AMG is not an Eigen3 preconditioner!"); + } } SECTION("Cholesky") @@ -706,170 +696,186 @@ TEST_CASE("LinearSolver", "[algebra]") } #endif // PUGS_HAS_PETSC } - } - } - } - SECTION("Dense Matrices") - { - SECTION("check linear solvers") - { - SECTION("symmetric system") - { - SmallMatrix<double> A{5}; - A = zero; + SECTION("Eigen3") + { +#ifdef PUGS_HAS_EIGEN3 - A(0, 0) = 2; - A(0, 1) = -1; + SECTION("BICGStab") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::bicgstab; + options.precond() = LSPrecond::none; + options.verbose() = true; - A(1, 0) = -1; - A(1, 1) = 2; - A(1, 2) = -1; + SECTION("BICGStab no preconditioner") + { + options.precond() = LSPrecond::none; - A(2, 1) = -1; - A(2, 2) = 2; - A(2, 3) = -1; + Vector<double> x{5}; + x = zero; - A(3, 2) = -1; - A(3, 3) = 2; - A(3, 4) = -1; + LinearSolver solver{options}; - A(4, 3) = -1; - A(4, 4) = 2; + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } - SmallVector<const double> x_exact = [] { - SmallVector<double> y{5}; - y[0] = 1; - y[1] = 3; - y[2] = 2; - y[3] = 4; - y[4] = 5; - return y; - }(); + SECTION("BICGStab Diagonal") + { + options.precond() = LSPrecond::diagonal; - SmallVector<double> b = A * x_exact; + Vector<double> x{5}; + x = zero; - SECTION("builtin") - { - SECTION("CG no preconditioner") - { - LinearSolverOptions options; - options.library() = LSLibrary::builtin; - options.method() = LSMethod::cg; - options.precond() = LSPrecond::none; + LinearSolver solver{options}; - SmallVector<double> x{5}; - x = zero; + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } - LinearSolver solver{options}; + SECTION("BICGStab ILU") + { + options.precond() = LSPrecond::incomplete_LU; - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - } - } + Vector<double> x{5}; + x = zero; - SECTION("PETSc") - { -#ifdef PUGS_HAS_PETSC + LinearSolver solver{options}; - SECTION("CG") + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("BICGStab2") { LinearSolverOptions options; options.library() = LSLibrary::petsc; - options.method() = LSMethod::cg; + options.method() = LSMethod::bicgstab2; options.precond() = LSPrecond::none; - options.verbose() = true; - SECTION("CG no preconditioner") + SECTION("BICGStab2 no preconditioner") { options.precond() = LSPrecond::none; - SmallVector<double> x{5}; + Vector<double> x{5}; x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("CG Diagonal") + SECTION("BICGStab2 Diagonal") { options.precond() = LSPrecond::diagonal; - SmallVector<double> x{5}; + Vector<double> x{5}; x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } + } - SECTION("CG ICholesky") + SECTION("GMRES") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::gmres; + options.precond() = LSPrecond::none; + + SECTION("GMRES no preconditioner") { - options.precond() = LSPrecond::incomplete_cholesky; + options.precond() = LSPrecond::none; - SmallVector<double> x{5}; + Vector<double> x{5}; x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("CG AMG") + SECTION("GMRES Diagonal") { - options.precond() = LSPrecond::amg; + options.precond() = LSPrecond::diagonal; - SmallVector<double> x{5}; + Vector<double> x{5}; x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + Vector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } } - SECTION("Cholesky") + SECTION("LU") { LinearSolverOptions options; options.library() = LSLibrary::petsc; - options.method() = LSMethod::cholesky; + options.method() = LSMethod::lu; options.precond() = LSPrecond::none; - SmallVector<double> x{5}; + Vector<double> x{5}; x = zero; LinearSolver solver{options}; solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; + Vector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } -#else // PUGS_HAS_PETSC - SECTION("PETSc not linked") +#else // PUGS_HAS_EIGEN3 + SECTION("Eigen3 not linked") { LinearSolverOptions options; options.library() = LSLibrary::petsc; options.method() = LSMethod::cg; options.precond() = LSPrecond::none; - REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!"); + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); } -#endif // PUGS_HAS_PETSC +#endif // PUGS_HAS_EIGEN3 } } + } + } - SECTION("none symmetric system") + SECTION("Dense Matrices") + { + SECTION("check linear solvers") + { + SECTION("symmetric system") { SmallMatrix<double> A{5}; A = zero; @@ -877,20 +883,20 @@ TEST_CASE("LinearSolver", "[algebra]") A(0, 0) = 2; A(0, 1) = -1; - A(1, 0) = -0.2; + A(1, 0) = -1; A(1, 1) = 2; A(1, 2) = -1; A(2, 1) = -1; - A(2, 2) = 4; - A(2, 3) = -2; + A(2, 2) = 2; + A(2, 3) = -1; A(3, 2) = -1; A(3, 3) = 2; - A(3, 4) = -0.1; + A(3, 4) = -1; - A(4, 3) = 1; - A(4, 4) = 3; + A(4, 3) = -1; + A(4, 4) = 2; SmallVector<const double> x_exact = [] { SmallVector<double> y{5}; @@ -906,11 +912,11 @@ TEST_CASE("LinearSolver", "[algebra]") SECTION("builtin") { - SECTION("BICGStab no preconditioner") + SECTION("CG no preconditioner") { LinearSolverOptions options; options.library() = LSLibrary::builtin; - options.method() = LSMethod::bicgstab; + options.method() = LSMethod::cg; options.precond() = LSPrecond::none; SmallVector<double> x{5}; @@ -928,15 +934,15 @@ TEST_CASE("LinearSolver", "[algebra]") { #ifdef PUGS_HAS_EIGEN3 - SECTION("BICGStab") + SECTION("CG") { LinearSolverOptions options; options.library() = LSLibrary::eigen3; - options.method() = LSMethod::bicgstab; + options.method() = LSMethod::cg; options.precond() = LSPrecond::none; options.verbose() = true; - SECTION("BICGStab no preconditioner") + SECTION("CG no preconditioner") { options.precond() = LSPrecond::none; @@ -950,7 +956,7 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("BICGStab Diagonal") + SECTION("CG Diagonal") { options.precond() = LSPrecond::diagonal; @@ -964,45 +970,75 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("BICGStab ILU") + SECTION("CG ICholesky") { - options.precond() = LSPrecond::incomplete_LU; + options.precond() = LSPrecond::incomplete_cholesky; SmallVector<double> x{5}; x = zero; LinearSolver solver{options}; - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + REQUIRE_THROWS_WITH(solver.solveLocalSystem(A, x, b), + "error: incomplete cholesky is not available for dense matrices in Eigen3"); + } + + SECTION("CG AMG") + { + options.precond() = LSPrecond::amg; + + SmallVector<double> x{5}; + x = zero; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: AMG is not an Eigen3 preconditioner!"); } } - SECTION("BICGStab2") + SECTION("Cholesky") { LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::bicgstab2; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cholesky; options.precond() = LSPrecond::none; - SECTION("BICGStab2 no preconditioner") - { - options.precond() = LSPrecond::none; + SmallVector<double> x{5}; + x = zero; - SmallVector<double> x{5}; - x = zero; + LinearSolver solver{options}; - LinearSolver solver{options}; + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - } +#else // PUGS_HAS_EIGEN3 + SECTION("Eigen3 not linked") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; - SECTION("BICGStab2 Diagonal") + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("PETSc") + { +#ifdef PUGS_HAS_PETSC + + SECTION("CG") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + options.verbose() = true; + + SECTION("CG no preconditioner") { - options.precond() = LSPrecond::diagonal; + options.precond() = LSPrecond::none; SmallVector<double> x{5}; x = zero; @@ -1013,18 +1049,10 @@ TEST_CASE("LinearSolver", "[algebra]") SmallVector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - } - SECTION("GMRES") - { - LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::gmres; - options.precond() = LSPrecond::none; - - SECTION("GMRES no preconditioner") + SECTION("CG Diagonal") { - options.precond() = LSPrecond::none; + options.precond() = LSPrecond::diagonal; SmallVector<double> x{5}; x = zero; @@ -1036,9 +1064,9 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("GMRES Diagonal") + SECTION("CG ICholesky") { - options.precond() = LSPrecond::diagonal; + options.precond() = LSPrecond::incomplete_cholesky; SmallVector<double> x{5}; x = zero; @@ -1050,9 +1078,9 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - SECTION("GMRES ILU") + SECTION("CG AMG") { - options.precond() = LSPrecond::incomplete_LU; + options.precond() = LSPrecond::amg; SmallVector<double> x{5}; x = zero; @@ -1065,11 +1093,11 @@ TEST_CASE("LinearSolver", "[algebra]") } } - SECTION("LU") + SECTION("Cholesky") { LinearSolverOptions options; options.library() = LSLibrary::petsc; - options.method() = LSMethod::lu; + options.method() = LSMethod::cholesky; options.precond() = LSPrecond::none; SmallVector<double> x{5}; @@ -1082,33 +1110,64 @@ TEST_CASE("LinearSolver", "[algebra]") REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } -#else // PUGS_HAS_EIGEN3 - SECTION("Eigen3 not linked") +#else // PUGS_HAS_PETSC + SECTION("PETSc not linked") { LinearSolverOptions options; options.library() = LSLibrary::petsc; options.method() = LSMethod::cg; options.precond() = LSPrecond::none; - REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!"); } -#endif // PUGS_HAS_EIGEN3 +#endif // PUGS_HAS_PETSC } + } - SECTION("PETSc") + SECTION("none symmetric system") + { + SECTION("Dense matrix") { -#ifdef PUGS_HAS_PETSC + SmallMatrix<double> A{5}; + A = zero; - SECTION("BICGStab") - { - LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::bicgstab; - options.precond() = LSPrecond::none; - options.verbose() = true; + A(0, 0) = 2; + A(0, 1) = -1; + + A(1, 0) = -0.2; + A(1, 1) = 2; + A(1, 2) = -1; + + A(2, 1) = -1; + A(2, 2) = 4; + A(2, 3) = -2; + + A(3, 2) = -1; + A(3, 3) = 2; + A(3, 4) = -0.1; + + A(4, 3) = 1; + A(4, 4) = 3; + SmallVector<const double> x_exact = [] { + SmallVector<double> y{5}; + y[0] = 1; + y[1] = 3; + y[2] = 2; + y[3] = 4; + y[4] = 5; + return y; + }(); + + SmallVector<double> b = A * x_exact; + + SECTION("builtin") + { SECTION("BICGStab no preconditioner") { + LinearSolverOptions options; + options.library() = LSLibrary::builtin; + options.method() = LSMethod::bicgstab; options.precond() = LSPrecond::none; SmallVector<double> x{5}; @@ -1120,60 +1179,135 @@ TEST_CASE("LinearSolver", "[algebra]") SmallVector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } + } - SECTION("BICGStab Diagonal") + SECTION("Eigen3") + { +#ifdef PUGS_HAS_EIGEN3 + + SECTION("BICGStab") { - options.precond() = LSPrecond::diagonal; + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::bicgstab; + options.precond() = LSPrecond::none; + options.verbose() = true; - SmallVector<double> x{5}; - x = zero; + SECTION("BICGStab no preconditioner") + { + options.precond() = LSPrecond::none; - LinearSolver solver{options}; + SmallVector<double> x{5}; + x = zero; - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + REQUIRE_THROWS_WITH(solver.solveLocalSystem(A, x, b), + "error: incomplete LU is not available for dense matrices in Eigen3"); + } } - SECTION("BICGStab ILU") + SECTION("BICGStab2") { - options.precond() = LSPrecond::incomplete_LU; + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::bicgstab2; + options.precond() = LSPrecond::none; - SmallVector<double> x{5}; - x = zero; + SECTION("BICGStab2 no preconditioner") + { + options.precond() = LSPrecond::none; - LinearSolver solver{options}; + SmallVector<double> x{5}; + x = zero; - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: BICGStab2 is not an Eigen3 linear solver!"); + } } - } - - SECTION("BICGStab2") - { - LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::bicgstab2; - options.precond() = LSPrecond::none; - SECTION("BICGStab2 no preconditioner") + SECTION("GMRES") { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::gmres; options.precond() = LSPrecond::none; - SmallVector<double> x{5}; - x = zero; + SECTION("GMRES no preconditioner") + { + options.precond() = LSPrecond::none; - LinearSolver solver{options}; + SmallVector<double> x{5}; + x = zero; - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + REQUIRE_THROWS_WITH(solver.solveLocalSystem(A, x, b), + "error: incomplete LU is not available for dense matrices in Eigen3"); + } } - SECTION("BICGStab2 Diagonal") + SECTION("LU") { - options.precond() = LSPrecond::diagonal; + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::lu; + options.precond() = LSPrecond::none; SmallVector<double> x{5}; x = zero; @@ -1184,46 +1318,167 @@ TEST_CASE("LinearSolver", "[algebra]") SmallVector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } + +#else // PUGS_HAS_EIGEN3 + SECTION("Eigen3 not linked") + { + LinearSolverOptions options; + options.library() = LSLibrary::eigen3; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; + + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: Eigen3 is not linked to pugs. Cannot use it!"); + } +#endif // PUGS_HAS_EIGEN3 } - SECTION("GMRES") + SECTION("PETSc") { - LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::gmres; - options.precond() = LSPrecond::none; +#ifdef PUGS_HAS_PETSC - SECTION("GMRES no preconditioner") + SECTION("BICGStab") { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::bicgstab; options.precond() = LSPrecond::none; + options.verbose() = true; - SmallVector<double> x{5}; - x = zero; + SECTION("BICGStab no preconditioner") + { + options.precond() = LSPrecond::none; - LinearSolver solver{options}; + SmallVector<double> x{5}; + x = zero; - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } } - SECTION("GMRES Diagonal") + SECTION("BICGStab2") { - options.precond() = LSPrecond::diagonal; + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::bicgstab2; + options.precond() = LSPrecond::none; - SmallVector<double> x{5}; - x = zero; + SECTION("BICGStab2 no preconditioner") + { + options.precond() = LSPrecond::none; - LinearSolver solver{options}; + SmallVector<double> x{5}; + x = zero; - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("BICGStab2 Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } } - SECTION("GMRES ILU") + SECTION("GMRES") { - options.precond() = LSPrecond::incomplete_LU; + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::gmres; + options.precond() = LSPrecond::none; + + SECTION("GMRES no preconditioner") + { + options.precond() = LSPrecond::none; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES Diagonal") + { + options.precond() = LSPrecond::diagonal; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + + SECTION("GMRES ILU") + { + options.precond() = LSPrecond::incomplete_LU; + + SmallVector<double> x{5}; + x = zero; + + LinearSolver solver{options}; + + solver.solveLocalSystem(A, x, b); + SmallVector error = x - x_exact; + REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); + } + } + + SECTION("LU") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::lu; + options.precond() = LSPrecond::none; SmallVector<double> x{5}; x = zero; @@ -1234,36 +1489,19 @@ TEST_CASE("LinearSolver", "[algebra]") SmallVector error = x - x_exact; REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); } - } - - SECTION("LU") - { - LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::lu; - options.precond() = LSPrecond::none; - - SmallVector<double> x{5}; - x = zero; - - LinearSolver solver{options}; - - solver.solveLocalSystem(A, x, b); - SmallVector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - } #else // PUGS_HAS_PETSC - SECTION("PETSc not linked") - { - LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::cg; - options.precond() = LSPrecond::none; + SECTION("PETSc not linked") + { + LinearSolverOptions options; + options.library() = LSLibrary::petsc; + options.method() = LSMethod::cg; + options.precond() = LSPrecond::none; - REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!"); - } + REQUIRE_THROWS_WITH(LinearSolver{options}, "error: PETSc is not linked to pugs. Cannot use it!"); + } #endif // PUGS_HAS_PETSC + } } } } -- GitLab From a9e3c1ffd2b64b58655f2d7220f08aaac362e81f Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Wed, 29 Jan 2025 02:05:16 +0100 Subject: [PATCH 3/8] Refactor linear solver (and improve slightly performances) --- src/algebra/DenseMatrixWrapper.hpp | 59 ++++++++++ src/algebra/Eigen3Utils.hpp | 43 ++++++-- src/algebra/LinearSolver.cpp | 170 ++++++++++++----------------- src/algebra/LinearSolver.hpp | 131 ++++++++++++++++++++-- src/algebra/PETScUtils.hpp | 9 +- src/algebra/VectorWrapper.hpp | 47 ++++++++ src/utils/PugsTraits.hpp | 22 ++++ tests/test_LinearSolver.cpp | 42 +------ 8 files changed, 362 insertions(+), 161 deletions(-) create mode 100644 src/algebra/DenseMatrixWrapper.hpp create mode 100644 src/algebra/VectorWrapper.hpp diff --git a/src/algebra/DenseMatrixWrapper.hpp b/src/algebra/DenseMatrixWrapper.hpp new file mode 100644 index 000000000..dee57f27e --- /dev/null +++ b/src/algebra/DenseMatrixWrapper.hpp @@ -0,0 +1,59 @@ +#ifndef DENSE_MATRIX_WRAPPER_HPP +#define DENSE_MATRIX_WRAPPER_HPP + +#include <algebra/SmallMatrix.hpp> +#include <algebra/TinyMatrix.hpp> + +template <typename DataType> +class DenseMatrixWrapper +{ + private: + const size_t m_number_of_rows; + const size_t m_number_of_columns; + const DataType* const m_matrix_ptr; + + public: + PUGS_INLINE + bool + isSquare() const + { + return (m_number_of_rows == m_number_of_columns); + } + + PUGS_INLINE + size_t + numberOfRows() const + { + return m_number_of_rows; + } + + PUGS_INLINE + size_t + numberOfColumns() const + { + return m_number_of_columns; + } + + PUGS_INLINE + const DataType* const& + ptr() const + { + return m_matrix_ptr; + } + + DenseMatrixWrapper(const SmallMatrix<DataType>& A) + : m_number_of_rows{A.numberOfRows()}, + m_number_of_columns{A.numberOfColumns()}, + m_matrix_ptr{(m_number_of_columns * m_number_of_rows > 0) ? (&A(0, 0)) : nullptr} + {} + + template <size_t M, size_t N> + DenseMatrixWrapper(const TinyMatrix<M, N, DataType>& A) + : m_number_of_rows{M}, m_number_of_columns{N}, m_matrix_ptr{(M * N > 0) ? (&A(0, 0)) : nullptr} + {} + + DenseMatrixWrapper(const DenseMatrixWrapper&) = delete; + DenseMatrixWrapper(DenseMatrixWrapper&&) = delete; +}; + +#endif // DENSE_MATRIX_WRAPPER_HPP diff --git a/src/algebra/Eigen3Utils.hpp b/src/algebra/Eigen3Utils.hpp index 5224a4b0b..d9d61e027 100644 --- a/src/algebra/Eigen3Utils.hpp +++ b/src/algebra/Eigen3Utils.hpp @@ -6,8 +6,7 @@ #ifdef PUGS_HAS_EIGEN3 #include <algebra/CRSMatrix.hpp> -#include <algebra/SmallMatrix.hpp> -#include <algebra/TinyMatrix.hpp> +#include <algebra/DenseMatrixWrapper.hpp> #include <eigen3/Eigen/Eigen> @@ -25,6 +24,13 @@ class Eigen3DenseMatrixEmbedder {} public: + PUGS_INLINE + size_t + isSquare() const + { + return (m_eigen_matrix.rows() == m_eigen_matrix.cols()); + } + PUGS_INLINE size_t numberOfRows() const @@ -53,12 +59,8 @@ class Eigen3DenseMatrixEmbedder return m_eigen_matrix; } - template <size_t N> - Eigen3DenseMatrixEmbedder(const TinyMatrix<N>& A) : Eigen3DenseMatrixEmbedder{N, N, &A(0, 0)} - {} - - Eigen3DenseMatrixEmbedder(const SmallMatrix<double>& A) - : Eigen3DenseMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), &A(0, 0)} + Eigen3DenseMatrixEmbedder(const DenseMatrixWrapper<double>& A) + : Eigen3DenseMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), A.ptr()} {} ~Eigen3DenseMatrixEmbedder() = default; @@ -88,6 +90,13 @@ class Eigen3SparseMatrixEmbedder return m_eigen_matrix.cols(); } + PUGS_INLINE + size_t + isSquare() const + { + return (m_eigen_matrix.rows() == m_eigen_matrix.cols()); + } + PUGS_INLINE Eigen3MapMatrixType& matrix() @@ -113,6 +122,24 @@ class Eigen3SparseMatrixEmbedder ~Eigen3SparseMatrixEmbedder() = default; }; +#else // PUGS_HAS_EIGEN3 + +class Eigen3DenseMatrixEmbedder +{ + public: + Eigen3DenseMatrixEmbedder(const DenseMatrixWrapper<double>&) {} + + ~Eigen3DenseMatrixEmbedder() = default; +}; + +class Eigen3SparseMatrixEmbedder +{ + public: + Eigen3SparseMatrixEmbedder(const CRSMatrix<double>&) {} + + ~Eigen3SparseMatrixEmbedder() = default; +}; + #endif // PUGS_HAS_EIGEN3 #endif // EIGEN3_UTILS_HPP diff --git a/src/algebra/LinearSolver.cpp b/src/algebra/LinearSolver.cpp index 1ebb59663..52bf8fc39 100644 --- a/src/algebra/LinearSolver.cpp +++ b/src/algebra/LinearSolver.cpp @@ -187,10 +187,10 @@ struct LinearSolver::Internals template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> static void - builtinSolveLocalSystem(const MatrixType& A, - SolutionVectorType& x, - const RHSVectorType& b, - const LinearSolverOptions& options) + _builtinSolveLocalSystem(const MatrixType& A, + SolutionVectorType& x, + const RHSVectorType& b, + const LinearSolverOptions& options) { if (options.precond() != LSPrecond::none) { // LCOV_EXCL_START @@ -215,18 +215,15 @@ struct LinearSolver::Internals } #ifdef PUGS_HAS_EIGEN3 - template <typename PreconditionerType, - typename Eigen3MatrixEmbedderType, - typename SolutionVectorType, - typename RHSVectorType> + template <typename PreconditionerType, typename Eigen3MatrixEmbedderType> static void _preconditionedEigen3SolveLocalSystem(const Eigen3MatrixEmbedderType& eigen3_A, - SolutionVectorType& x, - const RHSVectorType& b, + VectorWrapper<double>& x, + const VectorWrapper<const double>& b, const LinearSolverOptions& options) { - Eigen::Map<Eigen::VectorX<double>> eigen3_x{(x.size() > 0) ? (&x[0]) : nullptr, static_cast<int>(x.size())}; - Eigen::Map<const Eigen::VectorX<double>> eigen3_b{(b.size() > 0) ? (&b[0]) : nullptr, static_cast<int>(b.size())}; + Eigen::Map<Eigen::VectorX<double>> eigen3_x{x.ptr(), static_cast<int>(x.size())}; + Eigen::Map<const Eigen::VectorX<double>> eigen3_b{b.ptr(), static_cast<int>(b.size())}; using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType; @@ -287,28 +284,29 @@ struct LinearSolver::Internals } } - template <typename Eigen3MatrixEmbedderType, typename SolutionVectorType, typename RHSVectorType> + template <typename Eigen3MatrixEmbedderType> static void _eigen3SolveLocalSystem(const Eigen3MatrixEmbedderType& eigen3_A, - SolutionVectorType& x, - const RHSVectorType& b, + VectorWrapper<double>& x, + const VectorWrapper<const double>& b, const LinearSolverOptions& options) { switch (options.precond()) { case LSPrecond::none: { - _preconditionedEigen3SolveLocalSystem<Eigen::IdentityPreconditioner, Eigen3MatrixEmbedderType, SolutionVectorType, - RHSVectorType>(eigen3_A, x, b, options); + _preconditionedEigen3SolveLocalSystem<Eigen::IdentityPreconditioner, Eigen3MatrixEmbedderType>(eigen3_A, x, b, + options); break; } case LSPrecond::diagonal: { - _preconditionedEigen3SolveLocalSystem<Eigen::DiagonalPreconditioner<double>, Eigen3MatrixEmbedderType, - SolutionVectorType, RHSVectorType>(eigen3_A, x, b, options); + _preconditionedEigen3SolveLocalSystem<Eigen::DiagonalPreconditioner<double>, Eigen3MatrixEmbedderType>(eigen3_A, + x, b, + options); break; } case LSPrecond::incomplete_cholesky: { if constexpr (std::is_same_v<Eigen3SparseMatrixEmbedder, Eigen3MatrixEmbedderType>) { - _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteCholesky<double>, Eigen3MatrixEmbedderType, - SolutionVectorType, RHSVectorType>(eigen3_A, x, b, options); + _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteCholesky<double>, Eigen3MatrixEmbedderType>(eigen3_A, x, + b, options); } else { throw NormalError("incomplete cholesky is not available for dense matrices in Eigen3"); } @@ -316,8 +314,8 @@ struct LinearSolver::Internals } case LSPrecond::incomplete_LU: { if constexpr (std::is_same_v<Eigen3SparseMatrixEmbedder, Eigen3MatrixEmbedderType>) { - _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteLUT<double>, Eigen3MatrixEmbedderType, - SolutionVectorType, RHSVectorType>(eigen3_A, x, b, options); + _preconditionedEigen3SolveLocalSystem<Eigen::IncompleteLUT<double>, Eigen3MatrixEmbedderType>(eigen3_A, x, b, + options); } else { throw NormalError("incomplete LU is not available for dense matrices in Eigen3"); } @@ -330,39 +328,12 @@ struct LinearSolver::Internals // LCOV_EXCL_STOP } } - - template <typename DataType, typename SolutionVectorType, typename RHSVectorType> - static void - eigen3SolveLocalSystem(const CRSMatrix<DataType>& A, - SolutionVectorType& x, - const RHSVectorType& b, - const LinearSolverOptions& options) - { - Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); - - Eigen3SparseMatrixEmbedder eigen3_A{A}; - _eigen3SolveLocalSystem(eigen3_A, x, b, options); - } - - template <typename DataType, typename SolutionVectorType, typename RHSVectorType> - static void - eigen3SolveLocalSystem(const SmallMatrix<DataType>& A, - SolutionVectorType& x, - const RHSVectorType& b, - const LinearSolverOptions& options) - { - Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); - - Eigen3DenseMatrixEmbedder eigen3_A{A}; - _eigen3SolveLocalSystem(eigen3_A, x, b, options); - } - #else // PUGS_HAS_EIGEN3 // LCOV_EXCL_START template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> static void - eigen3SolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&) + _eigen3SolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&) { checkHasLibrary(LSLibrary::eigen3); throw UnexpectedError("unexpected situation should not reach this point!"); @@ -379,19 +350,17 @@ struct LinearSolver::Internals return 0; } - template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> + template <typename MatrixType> static void - petscSolveLocalSystem(const MatrixType& A, - SolutionVectorType& x, - const RHSVectorType& b, - const LinearSolverOptions& options) + _petscSolveLocalSystem(const MatrixType& A, + VectorWrapper<double>& x, + const VectorWrapper<const double>& b, + const LinearSolverOptions& options) { - Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); - Vec petscB; - VecCreateMPIWithArray(PETSC_COMM_SELF, 1, b.size(), b.size(), &b[0], &petscB); + VecCreateMPIWithArray(PETSC_COMM_SELF, 1, b.size(), b.size(), b.ptr(), &petscB); Vec petscX; - VecCreateMPIWithArray(PETSC_COMM_SELF, 1, x.size(), x.size(), &x[0], &petscX); + VecCreateMPIWithArray(PETSC_COMM_SELF, 1, x.size(), x.size(), x.ptr(), &petscX); PETScAijMatrixEmbedder petscA(A); @@ -491,7 +460,7 @@ struct LinearSolver::Internals // LCOV_EXCL_START template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> static void - petscSolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&) + _petscSolveLocalSystem(const MatrixType&, SolutionVectorType&, const RHSVectorType&, const LinearSolverOptions&) { checkHasLibrary(LSLibrary::petsc); throw UnexpectedError("unexpected situation should not reach this point!"); @@ -499,40 +468,6 @@ struct LinearSolver::Internals // LCOV_EXCL_STOP #endif // PUGS_HAS_PETSC - - template <typename MatrixType, typename SolutionVectorType, typename RHSVectorType> - static void - solveLocalSystem(const MatrixType& A, - SolutionVectorType& x, - const RHSVectorType& b, - const LinearSolverOptions& options) - { - switch (options.library()) { - case LSLibrary::builtin: { - builtinSolveLocalSystem(A, x, b, options); - break; - } - // LCOV_EXCL_START - case LSLibrary::eigen3: { - // not covered since if Eigen3 is not linked this point is - // unreachable: LinearSolver throws an exception at construction - // in this case. - eigen3SolveLocalSystem(A, x, b, options); - break; - } - case LSLibrary::petsc: { - // not covered since if PETSc is not linked this point is - // unreachable: LinearSolver throws an exception at construction - // in this case. - petscSolveLocalSystem(A, x, b, options); - break; - } - default: { - throw UnexpectedError(::name(options.library()) + " cannot solve local systems"); - } - // LCOV_EXCL_STOP - } - } }; bool @@ -548,15 +483,52 @@ LinearSolver::checkOptions(const LinearSolverOptions& options) const } void -LinearSolver::solveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b) +LinearSolver::_builtinSolveLocalSystem(const CRSMatrix<double, int>& A, + Vector<double>& x, + const Vector<const double>& b) +{ + Internals::_builtinSolveLocalSystem(A, x, b, m_options); +} + +void +LinearSolver::_builtinSolveLocalSystem(const SmallMatrix<double>& A, + SmallVector<double>& x, + const SmallVector<const double>& b) +{ + Internals::_builtinSolveLocalSystem(A, x, b, m_options); +} + +void +LinearSolver::_eigen3SolveLocalSystem(const Eigen3SparseMatrixEmbedder& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b) +{ + Internals::_eigen3SolveLocalSystem(A, x, b, m_options); +} + +void +LinearSolver::_eigen3SolveLocalSystem(const DenseMatrixWrapper<double>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b) +{ + Eigen3DenseMatrixEmbedder A_wrapper{A}; + Internals::_eigen3SolveLocalSystem(A_wrapper, x, b, m_options); +} + +void +LinearSolver::_petscSolveLocalSystem(const CRSMatrix<double, int>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b) { - Internals::solveLocalSystem(A, x, b, m_options); + Internals::_petscSolveLocalSystem(A, x, b, m_options); } void -LinearSolver::solveLocalSystem(const SmallMatrix<double>& A, SmallVector<double>& x, const SmallVector<const double>& b) +LinearSolver::_petscSolveLocalSystem(const DenseMatrixWrapper<double>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b) { - Internals::solveLocalSystem(A, x, b, m_options); + Internals::_petscSolveLocalSystem(A, x, b, m_options); } LinearSolver::LinearSolver(const LinearSolverOptions& options) : m_options{options} diff --git a/src/algebra/LinearSolver.hpp b/src/algebra/LinearSolver.hpp index e90402c8d..2d03a9cf5 100644 --- a/src/algebra/LinearSolver.hpp +++ b/src/algebra/LinearSolver.hpp @@ -2,11 +2,14 @@ #define LINEAR_SOLVER_HPP #include <algebra/CRSMatrix.hpp> +#include <algebra/DenseMatrixWrapper.hpp> +#include <algebra/Eigen3Utils.hpp> #include <algebra/LinearSolverOptions.hpp> #include <algebra/SmallMatrix.hpp> #include <algebra/TinyMatrix.hpp> #include <algebra/TinyVector.hpp> #include <algebra/Vector.hpp> +#include <algebra/VectorWrapper.hpp> #include <utils/Exceptions.hpp> class LinearSolver @@ -16,29 +19,139 @@ class LinearSolver const LinearSolverOptions m_options; + void _builtinSolveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b); + + void _builtinSolveLocalSystem(const SmallMatrix<double>& A, + SmallVector<double>& x, + const SmallVector<const double>& b); + + void _eigen3SolveLocalSystem(const Eigen3SparseMatrixEmbedder& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b); + + void _eigen3SolveLocalSystem(const DenseMatrixWrapper<double>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b); + + void _petscSolveLocalSystem(const CRSMatrix<double, int>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b); + + void _petscSolveLocalSystem(const DenseMatrixWrapper<double>& A, + VectorWrapper<double> x, + const VectorWrapper<const double>& b); + public: bool hasLibrary(LSLibrary library) const; void checkOptions(const LinearSolverOptions& options) const; + void + solveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b) + { + Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); + switch (m_options.library()) { + case LSLibrary::builtin: { + _builtinSolveLocalSystem(A, x, b); + break; + } + // LCOV_EXCL_START + case LSLibrary::eigen3: { + // not covered since if Eigen3 is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _eigen3SolveLocalSystem(A, x, b); + break; + } + case LSLibrary::petsc: { + // not covered since if PETSc is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _petscSolveLocalSystem(A, x, b); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems"); + } + // LCOV_EXCL_STOP + } + } + + void + solveLocalSystem(const SmallMatrix<double>& A, SmallVector<double>& x, const SmallVector<const double>& b) + { + Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); + switch (m_options.library()) { + case LSLibrary::builtin: { + _builtinSolveLocalSystem(A, x, b); + break; + } + // LCOV_EXCL_START + case LSLibrary::eigen3: { + // not covered since if Eigen3 is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _eigen3SolveLocalSystem(A, x, b); + break; + } + case LSLibrary::petsc: { + // not covered since if PETSc is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _petscSolveLocalSystem(A, x, b); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems"); + } + // LCOV_EXCL_STOP + } + } + template <size_t N> void solveLocalSystem(const TinyMatrix<N>& A, TinyVector<N>& x, const TinyVector<N>& b) { - SmallMatrix A_dense{A}; + Assert((x.size() == b.size()) and (x.size() - A.numberOfColumns() == 0) and A.isSquare()); + switch (m_options.library()) { + case LSLibrary::builtin: { + // Probably expensive but builtin is not the preferred way to + // solve linear systems + SmallMatrix A_dense{A}; + + SmallVector x_vector{x}; + SmallVector<const double> b_vector{b}; - SmallVector x_vector{x}; - SmallVector b_vector{b}; + this->solveLocalSystem(A_dense, x_vector, b_vector); - this->solveLocalSystem(A_dense, x_vector, b_vector); + for (size_t i = 0; i < N; ++i) { + x[i] = x_vector[i]; + } - for (size_t i = 0; i < N; ++i) { - x[i] = x_vector[i]; + _builtinSolveLocalSystem(A, x, b); + break; + } + // LCOV_EXCL_START + case LSLibrary::eigen3: { + // not covered since if Eigen3 is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _eigen3SolveLocalSystem(A, x, b); + break; + } + case LSLibrary::petsc: { + // not covered since if PETSc is not linked this point is + // unreachable: LinearSolver throws an exception at construction + // in this case. + _petscSolveLocalSystem(A, x, b); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems"); + } + // LCOV_EXCL_STOP } } - void solveLocalSystem(const CRSMatrix<double, int>& A, Vector<double>& x, const Vector<const double>& b); - void solveLocalSystem(const SmallMatrix<double>& A, SmallVector<double>& x, const SmallVector<const double>& b); - LinearSolver(); LinearSolver(const LinearSolverOptions& options); }; diff --git a/src/algebra/PETScUtils.hpp b/src/algebra/PETScUtils.hpp index c4db9ee59..c6d2e8be8 100644 --- a/src/algebra/PETScUtils.hpp +++ b/src/algebra/PETScUtils.hpp @@ -6,6 +6,7 @@ #ifdef PUGS_HAS_PETSC #include <algebra/CRSMatrix.hpp> +#include <algebra/DenseMatrixWrapper.hpp> #include <algebra/SmallMatrix.hpp> #include <algebra/TinyMatrix.hpp> @@ -52,12 +53,8 @@ class PETScAijMatrixEmbedder return m_petscMat; } - template <size_t N> - PETScAijMatrixEmbedder(const TinyMatrix<N>& A) : PETScAijMatrixEmbedder{N, N, &A(0, 0)} - {} - - PETScAijMatrixEmbedder(const SmallMatrix<double>& A) - : PETScAijMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), &A(0, 0)} + PETScAijMatrixEmbedder(const DenseMatrixWrapper<double>& A) + : PETScAijMatrixEmbedder{A.numberOfRows(), A.numberOfColumns(), A.ptr()} {} PETScAijMatrixEmbedder(const CRSMatrix<double, int>& A); diff --git a/src/algebra/VectorWrapper.hpp b/src/algebra/VectorWrapper.hpp new file mode 100644 index 000000000..0bdeefbfd --- /dev/null +++ b/src/algebra/VectorWrapper.hpp @@ -0,0 +1,47 @@ +#ifndef VECTOR_WRAPPER_HPP +#define VECTOR_WRAPPER_HPP + +#include <algebra/SmallVector.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> + +template <typename DataType> +class VectorWrapper +{ + private: + const size_t m_size; + DataType* const m_vector_ptr; + + public: + PUGS_INLINE + size_t + size() const + { + return m_size; + } + + PUGS_INLINE + DataType* const& + ptr() const + { + return m_vector_ptr; + } + + VectorWrapper(const Vector<DataType>& x) : m_size{x.size()}, m_vector_ptr{(m_size > 0) ? (&x[0]) : nullptr} {} + VectorWrapper(Vector<DataType>& x) : m_size{x.size()}, m_vector_ptr{(m_size > 0) ? (&x[0]) : nullptr} {} + + VectorWrapper(const SmallVector<DataType>& x) : m_size{x.size()}, m_vector_ptr{(m_size > 0) ? (&x[0]) : nullptr} {} + + template <size_t N> + VectorWrapper(const TinyVector<N, DataType>& x) : m_size{N}, m_vector_ptr{(N > 0) ? (&x[0]) : nullptr} + {} + + template <size_t N> + VectorWrapper(TinyVector<N, DataType>& x) : m_size{N}, m_vector_ptr{(N > 0) ? (&x[0]) : nullptr} + {} + + VectorWrapper(const VectorWrapper&) = delete; + VectorWrapper(VectorWrapper&&) = delete; +}; + +#endif // VECTOR_WRAPPER_HPP diff --git a/src/utils/PugsTraits.hpp b/src/utils/PugsTraits.hpp index 2e1d40957..3c7a4e40a 100644 --- a/src/utils/PugsTraits.hpp +++ b/src/utils/PugsTraits.hpp @@ -15,6 +15,12 @@ class TinyVector; template <size_t M, size_t N, typename T> class TinyMatrix; +template <typename DataType> +class SmallMatrix; + +template <typename DataType, typename IndexType> +class CRSMatrix; + template <typename DataType, ItemType item_type, typename ConnectivityPtr> class ItemValue; template <typename DataType, ItemType item_type, typename ConnectivityPtr> @@ -114,6 +120,22 @@ inline constexpr bool is_tiny_matrix_v = false; template <size_t M, size_t N, typename T> inline constexpr bool is_tiny_matrix_v<TinyMatrix<M, N, T>> = true; +// Traits is_small_matrix + +template <typename T> +inline constexpr bool is_small_matrix_v = false; + +template <typename DataType> +inline constexpr bool is_small_matrix_v<SmallMatrix<DataType>> = true; + +// Traits is_crs_matrix + +template <typename T> +inline constexpr bool is_crs_matrix_v = false; + +template <typename DataType, typename IndexType> +inline constexpr bool is_crs_matrix_v<CRSMatrix<DataType, IndexType>> = true; + // Trais is ItemValue template <typename T> diff --git a/tests/test_LinearSolver.cpp b/tests/test_LinearSolver.cpp index 4d01df2f0..76ef6aa2b 100644 --- a/tests/test_LinearSolver.cpp +++ b/tests/test_LinearSolver.cpp @@ -752,46 +752,10 @@ TEST_CASE("LinearSolver", "[algebra]") } } - SECTION("BICGStab2") - { - LinearSolverOptions options; - options.library() = LSLibrary::petsc; - options.method() = LSMethod::bicgstab2; - options.precond() = LSPrecond::none; - - SECTION("BICGStab2 no preconditioner") - { - options.precond() = LSPrecond::none; - - Vector<double> x{5}; - x = zero; - - LinearSolver solver{options}; - - solver.solveLocalSystem(A, x, b); - Vector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - } - - SECTION("BICGStab2 Diagonal") - { - options.precond() = LSPrecond::diagonal; - - Vector<double> x{5}; - x = zero; - - LinearSolver solver{options}; - - solver.solveLocalSystem(A, x, b); - Vector error = x - x_exact; - REQUIRE(std::sqrt(dot(error, error)) < 1E-10 * std::sqrt(dot(x_exact, x_exact))); - } - } - SECTION("GMRES") { LinearSolverOptions options; - options.library() = LSLibrary::petsc; + options.library() = LSLibrary::eigen3; options.method() = LSMethod::gmres; options.precond() = LSPrecond::none; @@ -841,7 +805,7 @@ TEST_CASE("LinearSolver", "[algebra]") SECTION("LU") { LinearSolverOptions options; - options.library() = LSLibrary::petsc; + options.library() = LSLibrary::eigen3; options.method() = LSMethod::lu; options.precond() = LSPrecond::none; @@ -859,7 +823,7 @@ TEST_CASE("LinearSolver", "[algebra]") SECTION("Eigen3 not linked") { LinearSolverOptions options; - options.library() = LSLibrary::petsc; + options.library() = LSLibrary::eigen3; options.method() = LSMethod::cg; options.precond() = LSPrecond::none; -- GitLab From 1dcf50f0e6d6c8a9dc8a5de1590f5237b05c866b Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Thu, 30 Jan 2025 08:07:22 +0100 Subject: [PATCH 4/8] Prepare EigenvalueSolver to incoming use of Eigen3 --- src/algebra/CMakeLists.txt | 1 + src/algebra/EigenvalueSolver.cpp | 250 +++++++++---- src/algebra/EigenvalueSolver.hpp | 109 ++++-- src/algebra/EigenvalueSolverOptions.cpp | 10 + src/algebra/EigenvalueSolverOptions.hpp | 108 ++++++ src/language/modules/LinearSolverModule.cpp | 40 +- src/utils/checkpointing/Checkpoint.cpp | 11 +- .../EigenvalueSolverOptionsHFType.hpp | 15 + .../LinearSolverOptionsHFType.hpp | 2 +- src/utils/checkpointing/Resume.cpp | 10 + tests/test_EigenvalueSolver.cpp | 344 ++++++++++++++---- tests/test_LinearSolverOptions.cpp | 1 + tests/test_checkpointing_HFTypes.cpp | 3 + 13 files changed, 737 insertions(+), 167 deletions(-) create mode 100644 src/algebra/EigenvalueSolverOptions.cpp create mode 100644 src/algebra/EigenvalueSolverOptions.hpp create mode 100644 src/utils/checkpointing/EigenvalueSolverOptionsHFType.hpp diff --git a/src/algebra/CMakeLists.txt b/src/algebra/CMakeLists.txt index d215b09b5..78c041c5c 100644 --- a/src/algebra/CMakeLists.txt +++ b/src/algebra/CMakeLists.txt @@ -3,6 +3,7 @@ add_library( PugsAlgebra EigenvalueSolver.cpp + EigenvalueSolverOptions.cpp LinearSolver.cpp LinearSolverOptions.cpp PETScUtils.cpp) diff --git a/src/algebra/EigenvalueSolver.cpp b/src/algebra/EigenvalueSolver.cpp index c71be58dd..5888d1c0b 100644 --- a/src/algebra/EigenvalueSolver.cpp +++ b/src/algebra/EigenvalueSolver.cpp @@ -2,10 +2,13 @@ #include <utils/pugs_config.hpp> #ifdef PUGS_HAS_SLEPC +#include <algebra/PETScUtils.hpp> #include <slepc.h> +#endif // PUGS_HAS_SLEPC struct EigenvalueSolver::Internals { +#ifdef PUGS_HAS_SLEPC static PetscReal computeSmallestRealEigenvalueOfSymmetricMatrix(EPS& eps) { @@ -65,93 +68,210 @@ struct EigenvalueSolver::Internals computeAllEigenvaluesOfSymmetricMatrixInInterval(eps, left_bound - 0.01 * std::abs(left_bound), right_bound + 0.01 * std::abs(right_bound)); } -}; -void -EigenvalueSolver::computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, SmallArray<double>& eigenvalues) -{ - EPS eps; + static void + slepscComputeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, SmallArray<double>& eigenvalues) + { + EPS eps; - EPSCreate(PETSC_COMM_SELF, &eps); - EPSSetOperators(eps, A, nullptr); - EPSSetProblemType(eps, EPS_HEP); + EPSCreate(PETSC_COMM_SELF, &eps); + EPSSetOperators(eps, A, nullptr); + EPSSetProblemType(eps, EPS_HEP); - Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); + Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); - PetscInt nb_eigenvalues; - EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); + PetscInt nb_eigenvalues; + EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); - eigenvalues = SmallArray<double>(nb_eigenvalues); - for (PetscInt i = 0; i < nb_eigenvalues; ++i) { - EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, nullptr, nullptr); + eigenvalues = SmallArray<double>(nb_eigenvalues); + for (PetscInt i = 0; i < nb_eigenvalues; ++i) { + EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, nullptr, nullptr); + } + + EPSDestroy(&eps); } - EPSDestroy(&eps); + static void + slepscComputeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvector_list) + { + EPS eps; + + EPSCreate(PETSC_COMM_SELF, &eps); + EPSSetOperators(eps, A, nullptr); + EPSSetProblemType(eps, EPS_HEP); + + Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); + + PetscInt nb_eigenvalues; + EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); + + eigenvalues = SmallArray<double>(nb_eigenvalues); + eigenvector_list.reserve(nb_eigenvalues); + for (PetscInt i = 0; i < nb_eigenvalues; ++i) { + Vec Vr; + SmallVector<double> eigenvector{A.numberOfRows()}; + VecCreateSeqWithArray(PETSC_COMM_SELF, 1, A.numberOfRows(), &(eigenvector[0]), &Vr); + EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, Vr, nullptr); + VecDestroy(&Vr); + eigenvector_list.push_back(eigenvector); + } + + EPSDestroy(&eps); + } + + static void + slepscComputeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) + { + EPS eps; + + EPSCreate(PETSC_COMM_SELF, &eps); + EPSSetOperators(eps, A, nullptr); + EPSSetProblemType(eps, EPS_HEP); + + Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); + + PetscInt nb_eigenvalues; + EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); + + eigenvalues = SmallArray<double>(nb_eigenvalues); + P = SmallMatrix<double>(nb_eigenvalues, nb_eigenvalues); + + Array<double> eigenvector(nb_eigenvalues); + for (PetscInt i = 0; i < nb_eigenvalues; ++i) { + Vec Vr; + VecCreateSeqWithArray(PETSC_COMM_SELF, 1, A.numberOfRows(), &(eigenvector[0]), &Vr); + EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, Vr, nullptr); + VecDestroy(&Vr); + for (size_t j = 0; j < eigenvector.size(); ++j) { + P(j, i) = eigenvector[j]; + } + } + + EPSDestroy(&eps); + } +#endif // PUGS_HAS_SLEPC +}; + +#ifdef PUGS_HAS_SLEPC +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, SmallArray<double>& eigenvalues) +{ + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues); } void -EigenvalueSolver::computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, - SmallArray<double>& eigenvalues, - std::vector<SmallVector<double>>& eigenvector_list) +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, SmallArray<double>& eigenvalues) { - EPS eps; - - EPSCreate(PETSC_COMM_SELF, &eps); - EPSSetOperators(eps, A, nullptr); - EPSSetProblemType(eps, EPS_HEP); - - Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); - - PetscInt nb_eigenvalues; - EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); - - eigenvalues = SmallArray<double>(nb_eigenvalues); - eigenvector_list.reserve(nb_eigenvalues); - for (PetscInt i = 0; i < nb_eigenvalues; ++i) { - Vec Vr; - SmallVector<double> eigenvector{A.numberOfRows()}; - VecCreateSeqWithArray(PETSC_COMM_SELF, 1, A.numberOfRows(), &(eigenvector[0]), &Vr); - EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, Vr, nullptr); - VecDestroy(&Vr); - eigenvector_list.push_back(eigenvector); - } + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues); +} - EPSDestroy(&eps); +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) +{ + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues, eigenvectors); } void -EigenvalueSolver::computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, - SmallArray<double>& eigenvalues, - SmallMatrix<double>& P) +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) { - EPS eps; + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues, eigenvectors); +} - EPSCreate(PETSC_COMM_SELF, &eps); - EPSSetOperators(eps, A, nullptr); - EPSSetProblemType(eps, EPS_HEP); +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) +{ + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues, P); +} - Internals::computeAllEigenvaluesOfSymmetricMatrix(eps); +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) +{ + Internals::slepscComputeForSymmetricMatrix(A, eigenvalues, P); +} - PetscInt nb_eigenvalues; - EPSGetDimensions(eps, &nb_eigenvalues, nullptr, nullptr); +#else // PUGS_HAS_SLEPC - eigenvalues = SmallArray<double>(nb_eigenvalues); - P = SmallMatrix<double>(nb_eigenvalues, nb_eigenvalues); +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>&, SmallArray<double>&) +{} - Array<double> eigenvector(nb_eigenvalues); - for (PetscInt i = 0; i < nb_eigenvalues; ++i) { - Vec Vr; - VecCreateSeqWithArray(PETSC_COMM_SELF, 1, A.numberOfRows(), &(eigenvector[0]), &Vr); - EPSGetEigenpair(eps, i, &(eigenvalues[i]), nullptr, Vr, nullptr); - VecDestroy(&Vr); - for (size_t j = 0; j < eigenvector.size(); ++j) { - P(j, i) = eigenvector[j]; - } - } +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, SmallArray<double>&) +{} - EPSDestroy(&eps); -} +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>&, + SmallArray<double>&, + std::vector<SmallVector<double>>&) +{} + +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, + SmallArray<double>&, + std::vector<SmallVector<double>>&) +{} + +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const CRSMatrix<double>&, SmallArray<double>&, SmallMatrix<double>&) +{} + +void +EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, + SmallArray<double>&, + SmallMatrix<double>&) +{} #endif // PUGS_HAS_SLEPC -EigenvalueSolver::EigenvalueSolver() {} +bool +EigenvalueSolver::hasLibrary(const ESLibrary library) +{ + switch (library) { + case ESLibrary::eigen3: { +#ifdef PUGS_HAS_EIGEN3 + return true; +#else + return false; +#endif + } + case ESLibrary::slepsc: { +#ifdef PUGS_HAS_SLEPC + return true; +#else + return false; +#endif + } + // LCOV_EXCL_START + default: { + throw UnexpectedError("Eigenvalue library (" + ::name(library) + ") was not set!"); + } + // LCOV_EXCL_STOP + } +} + +void +EigenvalueSolver::checkHasLibrary(const ESLibrary library) +{ + if (not hasLibrary(library)) { + // LCOV_EXCL_START + throw NormalError(::name(library) + " is not linked to pugs. Cannot use it!"); + // LCOV_EXCL_STOP + } +} + +EigenvalueSolver::EigenvalueSolver(const EigenvalueSolverOptions& options) : m_options{options} +{ + checkHasLibrary(options.library()); +} diff --git a/src/algebra/EigenvalueSolver.hpp b/src/algebra/EigenvalueSolver.hpp index a9b462509..eebd536e4 100644 --- a/src/algebra/EigenvalueSolver.hpp +++ b/src/algebra/EigenvalueSolver.hpp @@ -1,10 +1,13 @@ #ifndef EIGENVALUE_SOLVER_HPP #define EIGENVALUE_SOLVER_HPP -#include <algebra/PETScUtils.hpp> +#include <algebra/EigenvalueSolverOptions.hpp> +#include <algebra/CRSMatrix.hpp> +#include <algebra/DenseMatrixWrapper.hpp> #include <algebra/SmallMatrix.hpp> #include <algebra/SmallVector.hpp> +#include <algebra/TinyMatrix.hpp> #include <utils/Exceptions.hpp> #include <utils/SmallArray.hpp> @@ -13,57 +16,97 @@ class EigenvalueSolver private: struct Internals; -#ifdef PUGS_HAS_SLEPC - void computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, SmallArray<double>& eigenvalues); + const EigenvalueSolverOptions m_options; - void computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, - SmallArray<double>& eigenvalues, - std::vector<SmallVector<double>>& eigenvectors); + void _slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, SmallArray<double>& eigenvalues); - void computeForSymmetricMatrix(const PETScAijMatrixEmbedder& A, - SmallArray<double>& eigenvalues, - SmallMatrix<double>& P); -#endif // PUGS_HAS_SLEPC + void _slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, SmallArray<double>& eigenvalues); + + void _slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors); + + void _slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors); + + void _slepscComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P); + + void _slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P); public: + static bool hasLibrary(ESLibrary library); + static void checkHasLibrary(const ESLibrary library); + template <typename MatrixType> void - computeForSymmetricMatrix([[maybe_unused]] const MatrixType& A, [[maybe_unused]] SmallArray<double>& eigenvalues) + computeForSymmetricMatrix(const MatrixType& A, SmallArray<double>& eigenvalues) { -#ifdef PUGS_HAS_SLEPC - this->computeForSymmetricMatrix(PETScAijMatrixEmbedder{A}, eigenvalues); -#else // PUGS_HAS_SLEPC - throw NotImplementedError("SLEPc is required to solve eigenvalue problems"); -#endif // PUGS_HAS_SLEPC + Assert((eigenvalues.size() - A.numberOfRows() == 0) and A.isSquare()); + + switch (m_options.library()) { + case ESLibrary::eigen3: { + throw NotImplementedError("Eigen3"); + } + case ESLibrary::slepsc: { + this->_slepscComputeForSymmetricMatrix(A, eigenvalues); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot compute for symmetric matrices"); + } + } } template <typename MatrixType> void - computeForSymmetricMatrix([[maybe_unused]] const MatrixType& A, - [[maybe_unused]] SmallArray<double>& eigenvalues, - [[maybe_unused]] std::vector<SmallVector<double>>& eigenvectors) + computeForSymmetricMatrix(const MatrixType& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) { -#ifdef PUGS_HAS_SLEPC - this->computeForSymmetricMatrix(PETScAijMatrixEmbedder{A}, eigenvalues, eigenvectors); -#else // PUGS_HAS_SLEPC - throw NotImplementedError("SLEPc is required to solve eigenvalue problems"); -#endif // PUGS_HAS_SLEPC + Assert((eigenvalues.size() - A.numberOfRows() == 0) and (A.numberOfRows() - eigenvectors.size() == 0) and + A.isSquare()); + + switch (m_options.library()) { + case ESLibrary::eigen3: { + throw NotImplementedError("Eigen3"); + } + case ESLibrary::slepsc: { + this->_slepscComputeForSymmetricMatrix(A, eigenvalues, eigenvectors); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot compute for symmetric matrices"); + } + } } template <typename MatrixType> void - computeForSymmetricMatrix([[maybe_unused]] const MatrixType& A, - [[maybe_unused]] SmallArray<double>& eigenvalues, - [[maybe_unused]] SmallMatrix<double>& P) + computeForSymmetricMatrix(const MatrixType& A, SmallArray<double>& eigenvalues, SmallMatrix<double>& P) { -#ifdef PUGS_HAS_SLEPC - this->computeForSymmetricMatrix(PETScAijMatrixEmbedder{A}, eigenvalues, P); -#else // PUGS_HAS_SLEPC - throw NotImplementedError("SLEPc is required to solve eigenvalue problems"); -#endif // PUGS_HAS_SLEPC + Assert((eigenvalues.size() - A.numberOfRows() == 0) and (A.numberOfRows() - P.numberOfRows() == 0) and + A.isSquare() and P.isSquare()); + + switch (m_options.library()) { + case ESLibrary::eigen3: { + throw NotImplementedError("Eigen3"); + } + case ESLibrary::slepsc: { + this->_slepscComputeForSymmetricMatrix(A, eigenvalues, P); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot compute for symmetric matrices"); + } + } } - EigenvalueSolver(); + EigenvalueSolver(const EigenvalueSolverOptions& options = EigenvalueSolverOptions::default_options); ~EigenvalueSolver() = default; }; diff --git a/src/algebra/EigenvalueSolverOptions.cpp b/src/algebra/EigenvalueSolverOptions.cpp new file mode 100644 index 000000000..2a4ef9549 --- /dev/null +++ b/src/algebra/EigenvalueSolverOptions.cpp @@ -0,0 +1,10 @@ +#include <algebra/EigenvalueSolverOptions.hpp> + +#include <rang.hpp> + +std::ostream& +operator<<(std::ostream& os, const EigenvalueSolverOptions& options) +{ + os << " library: " << rang::style::bold << name(options.library()) << rang::style::reset << '\n'; + return os; +} diff --git a/src/algebra/EigenvalueSolverOptions.hpp b/src/algebra/EigenvalueSolverOptions.hpp new file mode 100644 index 000000000..d9be86be0 --- /dev/null +++ b/src/algebra/EigenvalueSolverOptions.hpp @@ -0,0 +1,108 @@ +#ifndef EIGENVALUE_SOLVER_OPTIONS_HPP +#define EIGENVALUE_SOLVER_OPTIONS_HPP + +#include <utils/Exceptions.hpp> + +#include <iostream> + +enum class ESLibrary : int8_t +{ + ES__begin = 0, + // + eigen3 = ES__begin, + slepsc, + // + ES__end +}; +; + +inline std::string +name(const ESLibrary library) +{ + switch (library) { + case ESLibrary::eigen3: { + return "Eigen3"; + } + case ESLibrary::slepsc: { + return "SLEPSc"; + } + case ESLibrary::ES__end: { + } + } + throw UnexpectedError("Eigenvalue solver library name is not defined!"); +} + +template <typename ESEnumType> +inline ESEnumType +getESEnumFromName(const std::string& enum_name) +{ + using BaseT = std::underlying_type_t<ESEnumType>; + for (BaseT enum_value = static_cast<BaseT>(ESEnumType::ES__begin); + enum_value < static_cast<BaseT>(ESEnumType::ES__end); ++enum_value) { + if (name(ESEnumType{enum_value}) == enum_name) { + return ESEnumType{enum_value}; + } + } + throw NormalError(std::string{"could not find '"} + enum_name + "' associate type!"); +} + +template <typename ESEnumType> +inline void +printESEnumListNames(std::ostream& os) +{ + using BaseT = std::underlying_type_t<ESEnumType>; + for (BaseT enum_value = static_cast<BaseT>(ESEnumType::ES__begin); + enum_value < static_cast<BaseT>(ESEnumType::ES__end); ++enum_value) { + os << " - " << name(ESEnumType{enum_value}) << '\n'; + } +} + +class EigenvalueSolverOptions +{ + private: + ESLibrary m_library = ESLibrary::slepsc; + + double m_epsilon = 1E-6; + size_t m_maximum_iteration = 200; + + bool m_verbose = false; + + public: + static EigenvalueSolverOptions default_options; + + friend std::ostream& operator<<(std::ostream& os, const EigenvalueSolverOptions& options); + + ESLibrary& + library() + { + return m_library; + } + + ESLibrary + library() const + { + return m_library; + } + + bool& + verbose() + { + return m_verbose; + }; + + bool + verbose() const + { + return m_verbose; + }; + + EigenvalueSolverOptions(const EigenvalueSolverOptions&) = default; + EigenvalueSolverOptions(EigenvalueSolverOptions&&) = default; + + EigenvalueSolverOptions() = default; + ~EigenvalueSolverOptions() = default; +}; + +inline EigenvalueSolverOptions EigenvalueSolverOptions::default_options; + +#endif // EIGENVALUE_SOLVER_OPTIONS_HPP diff --git a/src/language/modules/LinearSolverModule.cpp b/src/language/modules/LinearSolverModule.cpp index d3e2de0fa..1ff8be36d 100644 --- a/src/language/modules/LinearSolverModule.cpp +++ b/src/language/modules/LinearSolverModule.cpp @@ -2,7 +2,8 @@ #include <language/modules/ModuleRepository.hpp> -#include <algebra/LinearSolver.hpp> +#include <algebra/EigenvalueSolverOptions.hpp> +#include <algebra/LinearSolverOptions.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> #include <language/utils/TypeDescriptor.hpp> @@ -90,6 +91,43 @@ LinearSolverModule::LinearSolverModule() return os.str(); } + )); + + this->_addBuiltinFunction("setESLibrary", std::function( + + [](const std::string& library_name) -> void { + EigenvalueSolverOptions::default_options.library() = + getESEnumFromName<ESLibrary>(library_name); + } + + )); + + this->_addBuiltinFunction("getESOptions", std::function( + + []() -> std::string { + std::ostringstream os; + os << rang::fgB::yellow << "Eigenvalue solver options" + << rang::style::reset << '\n'; + os << EigenvalueSolverOptions::default_options; + return os.str(); + } + + )); + + this->_addBuiltinFunction("getESAvailable", std::function( + + []() -> std::string { + std::ostringstream os; + + os << rang::fgB::yellow << "Available eigenvalue solver options" + << rang::style::reset << '\n'; + os << rang::fgB::blue << " libraries" << rang::style::reset << '\n'; + + printESEnumListNames<ESLibrary>(os); + + return os.str(); + } + )); } diff --git a/src/utils/checkpointing/Checkpoint.cpp b/src/utils/checkpointing/Checkpoint.cpp index 5976cb2fb..ed56dcb83 100644 --- a/src/utils/checkpointing/Checkpoint.cpp +++ b/src/utils/checkpointing/Checkpoint.cpp @@ -4,6 +4,7 @@ #ifdef PUGS_HAS_HDF5 +#include <algebra/EigenvalueSolverOptions.hpp> #include <algebra/LinearSolverOptions.hpp> #include <dev/ParallelChecker.hpp> #include <language/ast/ASTExecutionStack.hpp> @@ -21,12 +22,12 @@ #include <utils/HighFivePugsUtils.hpp> #include <utils/RandomEngine.hpp> #include <utils/checkpointing/DualMeshTypeHFType.hpp> +#include <utils/checkpointing/EigenvalueSolverOptionsHFType.hpp> #include <utils/checkpointing/LinearSolverOptionsHFType.hpp> #include <utils/checkpointing/ParallelCheckerHFType.hpp> #include <utils/checkpointing/ResumingManager.hpp> #include <iostream> -#include <map> void checkpoint() @@ -122,6 +123,14 @@ checkpoint() linear_solver_options_default_group.createAttribute("method", default_options.method()); linear_solver_options_default_group.createAttribute("precond", default_options.precond()); } + { + HighFive::Group eigenvalue_solver_options_default_group = + checkpoint.createGroup("singleton/eigenvalue_solver_options_default"); + + const EigenvalueSolverOptions& default_options = EigenvalueSolverOptions::default_options; + + eigenvalue_solver_options_default_group.createAttribute("library", default_options.library()); + } { const auto& primal_to_dual_connectivity_info_map = DualConnectivityManager::instance().primalToDualConnectivityInfoMap(); diff --git a/src/utils/checkpointing/EigenvalueSolverOptionsHFType.hpp b/src/utils/checkpointing/EigenvalueSolverOptionsHFType.hpp new file mode 100644 index 000000000..32995c1f5 --- /dev/null +++ b/src/utils/checkpointing/EigenvalueSolverOptionsHFType.hpp @@ -0,0 +1,15 @@ +#ifndef EIGENVALUE_SOLVER_OPTIONS_HF_TYPE_HPP +#define EIGENVALUE_SOLVER_OPTIONS_HF_TYPE_HPP + +#include <algebra/EigenvalueSolverOptions.hpp> +#include <utils/HighFivePugsUtils.hpp> +#include <utils/PugsMacros.hpp> + +HighFive::EnumType<ESLibrary> PUGS_INLINE +create_enum_ESOptions_library_type() +{ + return {{"eigen3", ESLibrary::eigen3}, {"slepsc", ESLibrary::slepsc}}; +} +HIGHFIVE_REGISTER_TYPE(ESLibrary, create_enum_ESOptions_library_type) + +#endif // EIGENVALUE_SOLVER_OPTIONS_HF_TYPE_HPP diff --git a/src/utils/checkpointing/LinearSolverOptionsHFType.hpp b/src/utils/checkpointing/LinearSolverOptionsHFType.hpp index 94f88fc60..1c94d74a9 100644 --- a/src/utils/checkpointing/LinearSolverOptionsHFType.hpp +++ b/src/utils/checkpointing/LinearSolverOptionsHFType.hpp @@ -8,7 +8,7 @@ HighFive::EnumType<LSLibrary> PUGS_INLINE create_enum_LSOptions_library_type() { - return {{"builtin", LSLibrary::builtin}, {"petsc", LSLibrary::petsc}}; + return {{"builtin", LSLibrary::builtin}, {"eigen3", LSLibrary::eigen3}, {"petsc", LSLibrary::petsc}}; } HIGHFIVE_REGISTER_TYPE(LSLibrary, create_enum_LSOptions_library_type) diff --git a/src/utils/checkpointing/Resume.cpp b/src/utils/checkpointing/Resume.cpp index 9d58e1d9d..effd6b808 100644 --- a/src/utils/checkpointing/Resume.cpp +++ b/src/utils/checkpointing/Resume.cpp @@ -4,6 +4,7 @@ #ifdef PUGS_HAS_HDF5 +#include <algebra/EigenvalueSolverOptions.hpp> #include <algebra/LinearSolverOptions.hpp> #include <language/ast/ASTExecutionStack.hpp> #include <language/utils/ASTCheckpointsInfo.hpp> @@ -14,6 +15,7 @@ #include <utils/ExecutionStatManager.hpp> #include <utils/HighFivePugsUtils.hpp> #include <utils/RandomEngine.hpp> +#include <utils/checkpointing/EigenvalueSolverOptionsHFType.hpp> #include <utils/checkpointing/LinearSolverOptionsHFType.hpp> #include <utils/checkpointing/ParallelCheckerHFType.hpp> #include <utils/checkpointing/ResumingData.hpp> @@ -81,6 +83,14 @@ resume() default_options.method() = linear_solver_options_default_group.getAttribute("method").read<LSMethod>(); default_options.precond() = linear_solver_options_default_group.getAttribute("precond").read<LSPrecond>(); } + { + HighFive::Group eigenvalue_solver_options_default_group = + checkpoint.getGroup("singleton/eigenvalue_solver_options_default"); + + EigenvalueSolverOptions& default_options = EigenvalueSolverOptions::default_options; + + default_options.library() = eigenvalue_solver_options_default_group.getAttribute("library").read<ESLibrary>(); + } checkpointing::ResumingData::instance().readData(checkpoint, p_symbol_table); diff --git a/tests/test_EigenvalueSolver.cpp b/tests/test_EigenvalueSolver.cpp index fb7bf0790..4bb1d339d 100644 --- a/tests/test_EigenvalueSolver.cpp +++ b/tests/test_EigenvalueSolver.cpp @@ -12,109 +12,321 @@ TEST_CASE("EigenvalueSolver", "[algebra]") { - SECTION("Sparse Matrices") + SECTION("SLEPc") { - SECTION("symmetric system") + EigenvalueSolverOptions options; + options.library() = ESLibrary::slepsc; + + SECTION("Sparse Matrices") { - Array<int> non_zeros(3); - non_zeros.fill(3); - CRSMatrixDescriptor<double> S{3, 3, non_zeros}; + SECTION("symmetric system") + { + Array<int> non_zeros(3); + non_zeros.fill(3); + CRSMatrixDescriptor<double> S{3, 3, non_zeros}; - S(0, 0) = 3; - S(0, 1) = 2; - S(0, 2) = 4; + S(0, 0) = 3; + S(0, 1) = 2; + S(0, 2) = 4; - S(1, 0) = 2; - S(1, 1) = 0; - S(1, 2) = 2; + S(1, 0) = 2; + S(1, 1) = 0; + S(1, 2) = 2; - S(2, 0) = 4; - S(2, 1) = 2; - S(2, 2) = 3; + S(2, 0) = 4; + S(2, 1) = 2; + S(2, 2) = 3; - CRSMatrix A{S.getCRSMatrix()}; + CRSMatrix A{S.getCRSMatrix()}; - SECTION("eigenvalues") - { - SmallArray<double> eigenvalues; + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC + } + + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC + } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; #ifdef PUGS_HAS_SLEPC - EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalues); - REQUIRE(eigenvalues[0] == Catch::Approx(-1)); - REQUIRE(eigenvalues[1] == Catch::Approx(-1)); - REQUIRE(eigenvalues[2] == Catch::Approx(8)); + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + } + } #else // PUGS_HAS_SLEPC - REQUIRE_THROWS_WITH(EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalues), - "not implemented yet: SLEPc is required to solve eigenvalue problems"); + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: SLEPSc is not linked to pugs. Cannot use it!"); #endif // PUGS_HAS_SLEPC + } } + } - SECTION("eigenvalues and eigenvectors") + SECTION("SmallMatrix") + { + SECTION("symmetric system") { - SmallArray<double> eigenvalue_list; - std::vector<SmallVector<double>> eigenvector_list; + SmallMatrix<double> A{3, 3}; + A.fill(0); + A(0, 0) = 3; + A(0, 1) = 2; + A(0, 2) = 4; + + A(1, 0) = 2; + A(1, 1) = 0; + A(1, 2) = 2; + + A(2, 0) = 4; + A(2, 1) = 2; + A(2, 2) = 3; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC + } + + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; #ifdef PUGS_HAS_SLEPC - EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); - REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); - REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); - REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); - SmallMatrix<double> P{3}; - SmallMatrix<double> PT{3}; - SmallMatrix<double> D{3}; - D = zero; + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < 3; ++j) { - P(i, j) = eigenvector_list[j][i]; - PT(i, j) = eigenvector_list[i][j]; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; } - D(i, i) = eigenvalue_list[i]; - } - SmallMatrix PDPT = P * D * PT; - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < 3; ++j) { - REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } } +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } #else // PUGS_HAS_SLEPC - REQUIRE_THROWS_WITH(EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), - "not implemented yet: SLEPc is required to solve eigenvalue problems"); + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: SLEPSc is not linked to pugs. Cannot use it!"); #endif // PUGS_HAS_SLEPC + } } + } - SECTION("eigenvalues and passage matrix") + SECTION("TinyMatrix") + { + SECTION("symmetric system") { - SmallArray<double> eigenvalue_list; - SmallMatrix<double> P{}; + TinyMatrix<3> A = zero; -#ifdef PUGS_HAS_SLEPC - EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalue_list, P); + A(0, 0) = 3; + A(0, 1) = 2; + A(0, 2) = 4; - REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); - REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); - REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + A(1, 0) = 2; + A(1, 1) = 0; + A(1, 2) = 2; - SmallMatrix<double> D{3}; - D = zero; - for (size_t i = 0; i < 3; ++i) { - D(i, i) = eigenvalue_list[i]; + A(2, 0) = 4; + A(2, 1) = 2; + A(2, 2) = 3; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC } - SmallMatrix PT = transpose(P); - SmallMatrix PDPT = P * D * PT; - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < 3; ++j) { - REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } } +#else // PUGS_HAS_SLEPC + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: SLEPSc is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_SLEPC } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_SLEPC + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } #else // PUGS_HAS_SLEPC - REQUIRE_THROWS_WITH(EigenvalueSolver{}.computeForSymmetricMatrix(A, eigenvalue_list, P), - "not implemented yet: SLEPc is required to solve eigenvalue problems"); + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: SLEPSc is not linked to pugs. Cannot use it!"); #endif // PUGS_HAS_SLEPC + } } } } diff --git a/tests/test_LinearSolverOptions.cpp b/tests/test_LinearSolverOptions.cpp index 620959714..7d078a7fb 100644 --- a/tests/test_LinearSolverOptions.cpp +++ b/tests/test_LinearSolverOptions.cpp @@ -68,6 +68,7 @@ TEST_CASE("LinearSolverOptions", "[algebra]") SECTION("library name") { REQUIRE(name(LSLibrary::builtin) == "builtin"); + REQUIRE(name(LSLibrary::eigen3) == "Eigen3"); REQUIRE(name(LSLibrary::petsc) == "PETSc"); REQUIRE_THROWS_WITH(name(LSLibrary::LS__end), "unexpected error: Linear system library name is not defined!"); } diff --git a/tests/test_checkpointing_HFTypes.cpp b/tests/test_checkpointing_HFTypes.cpp index 41fc6948c..1a5f02b4a 100644 --- a/tests/test_checkpointing_HFTypes.cpp +++ b/tests/test_checkpointing_HFTypes.cpp @@ -3,6 +3,7 @@ #include <utils/checkpointing/DiscreteFunctionTypeHFType.hpp> #include <utils/checkpointing/DualMeshTypeHFType.hpp> +#include <utils/checkpointing/EigenvalueSolverOptionsHFType.hpp> #include <utils/checkpointing/IBoundaryConditionDescriptorHFType.hpp> #include <utils/checkpointing/IBoundaryDescriptorHFType.hpp> #include <utils/checkpointing/IInterfaceDescriptorHFType.hpp> @@ -160,6 +161,7 @@ TEST_CASE("HFTypes", "[utils/checkpointing]") SECTION("LinearSolverOptionsHFType") { file.createAttribute("builtin", LSLibrary::builtin); + file.createAttribute("eigen3", LSLibrary::eigen3); file.createAttribute("petsc", LSLibrary::petsc); file.createAttribute("cg", LSMethod::cg); @@ -177,6 +179,7 @@ TEST_CASE("HFTypes", "[utils/checkpointing]") REQUIRE(file.getAttribute("builtin").read<LSLibrary>() == LSLibrary::builtin); REQUIRE(file.getAttribute("petsc").read<LSLibrary>() == LSLibrary::petsc); + REQUIRE(file.getAttribute("eigen3").read<LSLibrary>() == LSLibrary::eigen3); REQUIRE(file.getAttribute("cg").read<LSMethod>() == LSMethod::cg); REQUIRE(file.getAttribute("bicgstab").read<LSMethod>() == LSMethod::bicgstab); -- GitLab From 54aed67103b6d89da5111291e9c4f76c53ae77ac Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Thu, 30 Jan 2025 21:31:14 +0100 Subject: [PATCH 5/8] Plug Eigen3 to solve eigenvalue problems for symmetric matrices --- src/algebra/EigenvalueSolver.cpp | 151 ++++++++++++++ src/algebra/EigenvalueSolver.hpp | 37 +++- tests/test_EigenvalueSolver.cpp | 332 +++++++++++++++++++++++++++++-- 3 files changed, 498 insertions(+), 22 deletions(-) diff --git a/src/algebra/EigenvalueSolver.cpp b/src/algebra/EigenvalueSolver.cpp index 5888d1c0b..b17e650af 100644 --- a/src/algebra/EigenvalueSolver.cpp +++ b/src/algebra/EigenvalueSolver.cpp @@ -6,6 +6,10 @@ #include <slepc.h> #endif // PUGS_HAS_SLEPC +#ifdef PUGS_HAS_EIGEN3 +#include <algebra/Eigen3Utils.hpp> +#endif // PUGS_HAS_EIGEN3 + struct EigenvalueSolver::Internals { #ifdef PUGS_HAS_SLEPC @@ -154,6 +158,73 @@ struct EigenvalueSolver::Internals EPSDestroy(&eps); } #endif // PUGS_HAS_SLEPC + +#ifdef PUGS_HAS_EIGEN3 + + template <typename Eigen3MatrixEmbedderType> + static void + eigen3ComputeForSymmetricMatrix(const Eigen3MatrixEmbedderType& A, SmallArray<double>& eigenvalues) + { + using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType; + Eigen::SelfAdjointEigenSolver<Eigen3MatrixType> eigen_solver; + + eigen_solver.compute(A.matrix(), Eigen::EigenvaluesOnly); + + eigenvalues = SmallArray<double>(A.numberOfRows()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + eigenvalues[i] = eigen_solver.eigenvalues()[i]; + } + } + + template <typename Eigen3MatrixEmbedderType> + static void + eigen3ComputeForSymmetricMatrix(const Eigen3MatrixEmbedderType& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) + { + using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType; + Eigen::SelfAdjointEigenSolver<Eigen3MatrixType> eigen_solver; + + eigen_solver.compute(A.matrix(), Eigen::ComputeEigenvectors); + + eigenvalues = SmallArray<double>(A.numberOfRows()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + eigenvalues[i] = eigen_solver.eigenvalues()[i]; + } + + eigenvectors.resize(eigenvalues.size()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + eigenvectors[i] = SmallVector<double>{eigenvalues.size()}; + for (size_t j = 0; j < eigenvalues.size(); ++j) { + eigenvectors[i][j] = eigen_solver.eigenvectors().coeff(j, i); + } + } + } + + template <typename Eigen3MatrixEmbedderType> + static void + eigen3ComputeForSymmetricMatrix(const Eigen3MatrixEmbedderType& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) + { + using Eigen3MatrixType = typename Eigen3MatrixEmbedderType::Eigen3MatrixType; + Eigen::SelfAdjointEigenSolver<Eigen3MatrixType> eigen_solver; + + eigen_solver.compute(A.matrix(), Eigen::ComputeEigenvectors); + + eigenvalues = SmallArray<double>(A.numberOfRows()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + eigenvalues[i] = eigen_solver.eigenvalues()[i]; + } + + P = SmallMatrix<double>(eigenvalues.size(), eigenvalues.size()); + for (size_t i = 0; i < eigenvalues.size(); ++i) { + for (size_t j = 0; j < eigenvalues.size(); ++j) { + P(i, j) = eigen_solver.eigenvectors().coeff(i, j); + } + } + } +#endif // PUGS_HAS_EIGEN3 }; #ifdef PUGS_HAS_SLEPC @@ -235,6 +306,86 @@ EigenvalueSolver::_slepscComputeForSymmetricMatrix(const DenseMatrixWrapper<doub #endif // PUGS_HAS_SLEPC +#ifdef PUGS_HAS_EIGEN3 +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, SmallArray<double>& eigenvalues) +{ + Eigen3SparseMatrixEmbedder embedded_A{A}; + Internals::eigen3ComputeForSymmetricMatrix(embedded_A, eigenvalues); +} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, SmallArray<double>& eigenvalues) +{ + Internals::eigen3ComputeForSymmetricMatrix(Eigen3DenseMatrixEmbedder{A}, eigenvalues); +} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) +{ + Internals::eigen3ComputeForSymmetricMatrix(Eigen3SparseMatrixEmbedder{A}, eigenvalues, eigenvectors); +} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors) +{ + Internals::eigen3ComputeForSymmetricMatrix(Eigen3DenseMatrixEmbedder{A}, eigenvalues, eigenvectors); +} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) +{ + Internals::eigen3ComputeForSymmetricMatrix(Eigen3SparseMatrixEmbedder{A}, eigenvalues, P); +} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P) +{ + Internals::eigen3ComputeForSymmetricMatrix(Eigen3DenseMatrixEmbedder{A}, eigenvalues, P); +} + +#else // PUGS_HAS_EIGEN3 + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>&, SmallArray<double>&) +{} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, SmallArray<double>&) +{} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>&, + SmallArray<double>&, + std::vector<SmallVector<double>>&) +{} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, + SmallArray<double>&, + std::vector<SmallVector<double>>&) +{} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>&, SmallArray<double>&, SmallMatrix<double>&) +{} + +void +EigenvalueSolver::_eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>&, + SmallArray<double>&, + SmallMatrix<double>&) +{} + +#endif // PUGS_HAS_EIGEN3 + bool EigenvalueSolver::hasLibrary(const ESLibrary library) { diff --git a/src/algebra/EigenvalueSolver.hpp b/src/algebra/EigenvalueSolver.hpp index eebd536e4..6567adcdc 100644 --- a/src/algebra/EigenvalueSolver.hpp +++ b/src/algebra/EigenvalueSolver.hpp @@ -38,6 +38,26 @@ class EigenvalueSolver SmallArray<double>& eigenvalues, SmallMatrix<double>& P); + void _eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, SmallArray<double>& eigenvalues); + + void _eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, SmallArray<double>& eigenvalues); + + void _eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors); + + void _eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + std::vector<SmallVector<double>>& eigenvectors); + + void _eigen3ComputeForSymmetricMatrix(const CRSMatrix<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P); + + void _eigen3ComputeForSymmetricMatrix(const DenseMatrixWrapper<double>& A, + SmallArray<double>& eigenvalues, + SmallMatrix<double>& P); + public: static bool hasLibrary(ESLibrary library); static void checkHasLibrary(const ESLibrary library); @@ -46,11 +66,12 @@ class EigenvalueSolver void computeForSymmetricMatrix(const MatrixType& A, SmallArray<double>& eigenvalues) { - Assert((eigenvalues.size() - A.numberOfRows() == 0) and A.isSquare()); + Assert(A.isSquare()); switch (m_options.library()) { case ESLibrary::eigen3: { - throw NotImplementedError("Eigen3"); + this->_eigen3ComputeForSymmetricMatrix(A, eigenvalues); + break; } case ESLibrary::slepsc: { this->_slepscComputeForSymmetricMatrix(A, eigenvalues); @@ -68,12 +89,12 @@ class EigenvalueSolver SmallArray<double>& eigenvalues, std::vector<SmallVector<double>>& eigenvectors) { - Assert((eigenvalues.size() - A.numberOfRows() == 0) and (A.numberOfRows() - eigenvectors.size() == 0) and - A.isSquare()); + Assert(A.isSquare()); switch (m_options.library()) { case ESLibrary::eigen3: { - throw NotImplementedError("Eigen3"); + this->_eigen3ComputeForSymmetricMatrix(A, eigenvalues, eigenvectors); + break; } case ESLibrary::slepsc: { this->_slepscComputeForSymmetricMatrix(A, eigenvalues, eigenvectors); @@ -89,12 +110,12 @@ class EigenvalueSolver void computeForSymmetricMatrix(const MatrixType& A, SmallArray<double>& eigenvalues, SmallMatrix<double>& P) { - Assert((eigenvalues.size() - A.numberOfRows() == 0) and (A.numberOfRows() - P.numberOfRows() == 0) and - A.isSquare() and P.isSquare()); + Assert(A.isSquare()); switch (m_options.library()) { case ESLibrary::eigen3: { - throw NotImplementedError("Eigen3"); + this->_eigen3ComputeForSymmetricMatrix(A, eigenvalues, P); + break; } case ESLibrary::slepsc: { this->_slepscComputeForSymmetricMatrix(A, eigenvalues, P); diff --git a/tests/test_EigenvalueSolver.cpp b/tests/test_EigenvalueSolver.cpp index 4bb1d339d..325f3e39b 100644 --- a/tests/test_EigenvalueSolver.cpp +++ b/tests/test_EigenvalueSolver.cpp @@ -12,14 +12,14 @@ TEST_CASE("EigenvalueSolver", "[algebra]") { - SECTION("SLEPc") + SECTION("symmetric system") { - EigenvalueSolverOptions options; - options.library() = ESLibrary::slepsc; - - SECTION("Sparse Matrices") + SECTION("SLEPc") { - SECTION("symmetric system") + EigenvalueSolverOptions options; + options.library() = ESLibrary::slepsc; + + SECTION("Sparse Matrices") { Array<int> non_zeros(3); non_zeros.fill(3); @@ -122,11 +122,8 @@ TEST_CASE("EigenvalueSolver", "[algebra]") #endif // PUGS_HAS_SLEPC } } - } - SECTION("SmallMatrix") - { - SECTION("symmetric system") + SECTION("SmallMatrix") { SmallMatrix<double> A{3, 3}; A.fill(0); @@ -225,11 +222,8 @@ TEST_CASE("EigenvalueSolver", "[algebra]") #endif // PUGS_HAS_SLEPC } } - } - SECTION("TinyMatrix") - { - SECTION("symmetric system") + SECTION("TinyMatrix") { TinyMatrix<3> A = zero; @@ -329,5 +323,315 @@ TEST_CASE("EigenvalueSolver", "[algebra]") } } } + + SECTION("Eigen3") + { + EigenvalueSolverOptions options; + options.library() = ESLibrary::eigen3; + + SECTION("Sparse Matrices") + { + Array<int> non_zeros(3); + non_zeros.fill(3); + CRSMatrixDescriptor<double> S{3, 3, non_zeros}; + + S(0, 0) = 3; + S(0, 1) = 2; + S(0, 2) = 4; + + S(1, 0) = 2; + S(1, 1) = 0; + S(1, 2) = 2; + + S(2, 0) = 4; + S(2, 1) = 2; + S(2, 2) = 3; + + CRSMatrix A{S.getCRSMatrix()}; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - S(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + } + + SECTION("SmallMatrix") + { + SmallMatrix<double> A{3, 3}; + A.fill(0); + A(0, 0) = 3; + A(0, 1) = 2; + A(0, 2) = 4; + + A(1, 0) = 2; + A(1, 1) = 0; + A(1, 2) = 2; + + A(2, 0) = 4; + A(2, 1) = 2; + A(2, 2) = 3; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + } + + SECTION("TinyMatrix") + { + TinyMatrix<3> A = zero; + + A(0, 0) = 3; + A(0, 1) = 2; + A(0, 2) = 4; + + A(1, 0) = 2; + A(1, 1) = 0; + A(1, 2) = 2; + + A(2, 0) = 4; + A(2, 1) = 2; + A(2, 2) = 3; + + SECTION("eigenvalues") + { + SmallArray<double> eigenvalues; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues); + REQUIRE(eigenvalues[0] == Catch::Approx(-1)); + REQUIRE(eigenvalues[1] == Catch::Approx(-1)); + REQUIRE(eigenvalues[2] == Catch::Approx(8)); +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalues), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and eigenvectors") + { + SmallArray<double> eigenvalue_list; + std::vector<SmallVector<double>> eigenvector_list; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> P{3}; + SmallMatrix<double> PT{3}; + SmallMatrix<double> D{3}; + D = zero; + + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + P(i, j) = eigenvector_list[j][i]; + PT(i, j) = eigenvector_list[i][j]; + } + D(i, i) = eigenvalue_list[i]; + } + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, eigenvector_list), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + + SECTION("eigenvalues and transition matrix") + { + SmallArray<double> eigenvalue_list; + SmallMatrix<double> P{}; + +#ifdef PUGS_HAS_EIGEN3 + EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P); + + REQUIRE(eigenvalue_list[0] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[1] == Catch::Approx(-1)); + REQUIRE(eigenvalue_list[2] == Catch::Approx(8)); + + SmallMatrix<double> D{3}; + D = zero; + for (size_t i = 0; i < 3; ++i) { + D(i, i) = eigenvalue_list[i]; + } + SmallMatrix PT = transpose(P); + + SmallMatrix PDPT = P * D * PT; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + REQUIRE(PDPT(i, j) - A(i, j) == Catch::Approx(0).margin(1E-13)); + } + } +#else // PUGS_HAS_EIGEN3 + REQUIRE_THROWS_WITH(EigenvalueSolver{options}.computeForSymmetricMatrix(A, eigenvalue_list, P), + "error: Eigen3 is not linked to pugs. Cannot use it!"); +#endif // PUGS_HAS_EIGEN3 + } + } + } } } -- GitLab From eca2ebc50372138653ee9af2c945378e64ce85b2 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Fri, 31 Jan 2025 08:29:52 +0100 Subject: [PATCH 6/8] Add few Eigen3 installation instructions --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index c859bc694..473b27b07 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,15 @@ To install `SLEPc` on Debian-like systems apt install slepc-dev ``` +#### `Eigen3` + +`Eigen3` is linear system solver and an eigenvalue problem solver. + +To install `Eigen3` on Debian-like systems +```shell +apt install libeigen3-dev +``` + ## Documentation ### User documentation @@ -234,6 +243,7 @@ the way `pugs` is built. | Description | Variable | Values | |:----------------|:--------------------|:----------------------------:| | MPI support | `PUGS_ENABLE_MPI` | `AUTO`(default), `ON`, `OFF` | +| `Eigen3` support| `PUGS_ENABLE_EIGEN3`| `AUTO`(default), `ON`, `OFF` | | `PETSc` support | `PUGS_ENABLE_PETSC` | `AUTO`(default), `ON`, `OFF` | | `SLEPc` support | `PUGS_ENABLE_SLEPC` | `AUTO`(default), `ON`, `OFF` | -- GitLab From 02dda00424f3a631e1e0e5dcf401bfa67cef3f0b Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Fri, 31 Jan 2025 09:01:44 +0100 Subject: [PATCH 7/8] Fix compilation warning --- src/algebra/EigenvalueSolverOptions.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/algebra/EigenvalueSolverOptions.hpp b/src/algebra/EigenvalueSolverOptions.hpp index d9be86be0..2c74ce41e 100644 --- a/src/algebra/EigenvalueSolverOptions.hpp +++ b/src/algebra/EigenvalueSolverOptions.hpp @@ -14,7 +14,6 @@ enum class ESLibrary : int8_t // ES__end }; -; inline std::string name(const ESLibrary library) -- GitLab From 8827f99e0c6615444ba737cc2ade9c26ea7e8014 Mon Sep 17 00:00:00 2001 From: Stephane Del Pino <stephane.delpino44@gmail.com> Date: Fri, 31 Jan 2025 09:09:13 +0100 Subject: [PATCH 8/8] Fix another bunch of warnings --- src/algebra/EigenvalueSolverOptions.hpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/algebra/EigenvalueSolverOptions.hpp b/src/algebra/EigenvalueSolverOptions.hpp index 2c74ce41e..1c047ef3c 100644 --- a/src/algebra/EigenvalueSolverOptions.hpp +++ b/src/algebra/EigenvalueSolverOptions.hpp @@ -61,11 +61,6 @@ class EigenvalueSolverOptions private: ESLibrary m_library = ESLibrary::slepsc; - double m_epsilon = 1E-6; - size_t m_maximum_iteration = 200; - - bool m_verbose = false; - public: static EigenvalueSolverOptions default_options; @@ -83,18 +78,6 @@ class EigenvalueSolverOptions return m_library; } - bool& - verbose() - { - return m_verbose; - }; - - bool - verbose() const - { - return m_verbose; - }; - EigenvalueSolverOptions(const EigenvalueSolverOptions&) = default; EigenvalueSolverOptions(EigenvalueSolverOptions&&) = default; -- GitLab