From 2075a167f4db86e8be4990b26e6b584a819d5b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com> Date: Thu, 15 Oct 2020 16:14:21 +0200 Subject: [PATCH] Plug PETSc (and various classic solvers and preconditioners) The 'LinearSolver' class proposes mechanism to choose the used - library, - method, and - preconditioner Also one can adjust the required convergence epsilon, the maximum number of iterations and the verbosity. All of these options can be listed through the 'pgs' script using the 'printLSAvailable()' builtin function. One drive the linear system solver by changing values in the script. For instance: setLSVerbosity(true); setLSEpsilon(1E-9); setLSMaxIter(1000); setLSLibrary("PETSc"); setLSMethod("LU"); setLSPrecond("none"); Finally one can print the current options by using the 'printLSOptions()' function. --- CMakeLists.txt | 24 +- src/algebra/BiCGStab.hpp | 26 +- src/algebra/{PCG.hpp => CG.hpp} | 40 +-- src/algebra/CMakeLists.txt | 3 +- src/algebra/CRSMatrix.hpp | 15 +- src/algebra/LinearSolver.cpp | 320 +++++++++++++++++++ src/algebra/LinearSolver.hpp | 36 +++ src/algebra/LinearSolverOptions.cpp | 15 + src/algebra/LinearSolverOptions.hpp | 233 ++++++++++++++ src/algebra/LinearSystemSolver.cpp | 331 -------------------- src/algebra/LinearSystemSolver.hpp | 79 ----- src/algebra/PETScWrapper.cpp | 1 + src/algebra/SparseMatrixDescriptor.hpp | 7 +- src/language/ast/ASTNodeDataType.cpp | 12 +- src/language/modules/CMakeLists.txt | 1 + src/language/modules/LinearSolverModule.cpp | 85 +++++ src/language/modules/LinearSolverModule.hpp | 19 ++ src/language/modules/ModuleRepository.cpp | 2 + tests/CMakeLists.txt | 6 +- tests/test_BiCGStab.cpp | 4 +- tests/{test_PCG.cpp => test_CG.cpp} | 10 +- tests/test_SparseMatrixDescriptor.cpp | 14 +- 22 files changed, 815 insertions(+), 468 deletions(-) rename src/algebra/{PCG.hpp => CG.hpp} (60%) create mode 100644 src/algebra/LinearSolver.cpp create mode 100644 src/algebra/LinearSolver.hpp create mode 100644 src/algebra/LinearSolverOptions.cpp create mode 100644 src/algebra/LinearSolverOptions.hpp delete mode 100644 src/algebra/LinearSystemSolver.cpp delete mode 100644 src/algebra/LinearSystemSolver.hpp create mode 100644 src/language/modules/LinearSolverModule.cpp create mode 100644 src/language/modules/LinearSolverModule.hpp rename tests/{test_PCG.cpp => test_CG.cpp} (91%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b4f0a54d..dc0f69398 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,8 +139,16 @@ endif() #------------------------------------------------------ # Check for PETSc -find_package(PETSc) -set(PUGS_HAS_PETSC ${PETSC_FOUND}) +# defaults use PETSc +set(PUGS_ENABLE_PETSC AUTO CACHE STRING + "Choose one of: AUTO ON OFF") + +if (PUGS_ENABLE_PETSC MATCHES "^(AUTO|ON)$") + find_package(PETSc) + set(PUGS_HAS_PETSC ${PETSC_FOUND}) +else() + unset(PUGS_HAS_PETSC) +endif() if (${PETSC_FOUND}) include_directories(SYSTEM ${PETSC_INCLUDES}) @@ -474,14 +482,22 @@ else() if(NOT PARMETIS_LIBRARIES) message(" MPI: deactivated: ParMETIS cannot be found!") else() - message(" MPI: not found!") + if (PUGS_ENABLE_MPI MATCHES "^(AUTO|ON)$") + message(" MPI: not found!") + else() + message(" MPI: explicitly deactivated!") + endif() endif() endif() if (PETSC_FOUND) message(" PETSc: ${PETSC_VERSION}") else() - message(" PETSc: not found!") + if (PUGS_ENABLE_PETSC MATCHES "^(AUTO|ON)$") + message(" PETSc: not found!") + else() + message(" PETSc: explicitly deactivated!") + endif() endif() if(CLANG_FORMAT) diff --git a/src/algebra/BiCGStab.hpp b/src/algebra/BiCGStab.hpp index bc822668a..ff34b89e1 100644 --- a/src/algebra/BiCGStab.hpp +++ b/src/algebra/BiCGStab.hpp @@ -7,16 +7,20 @@ #include <rang.hpp> -template <bool verbose = true> struct BiCGStab { template <typename VectorType, typename MatrixType> - BiCGStab(const VectorType& b, const MatrixType& A, VectorType& x, const size_t max_iter, const double epsilon = 1e-6) + BiCGStab(const MatrixType& A, + VectorType& x, + const VectorType& b, + const double epsilon, + const size_t maximum_iteration, + const bool verbose) { - if constexpr (verbose) { + if (verbose) { std::cout << "- bi-conjugate gradient stabilized\n"; std::cout << " epsilon = " << epsilon << '\n'; - std::cout << " maximum number of iterations: " << max_iter << '\n'; + std::cout << " maximum number of iterations: " << maximum_iteration << '\n'; } VectorType r_k_1{b.size()}; @@ -38,13 +42,13 @@ struct BiCGStab VectorType r_k{x.size()}; - if constexpr (verbose) { + if (verbose) { std::cout << " initial residu: " << resid0 << '\n'; } - for (size_t i = 1; i <= max_iter; ++i) { - if constexpr (verbose) { - std::cout << " - iteration: " << std::setw(6) << i << "\tresidu: " << residu / resid0 - << "\tabsolute: " << residu << '\n'; + for (size_t i = 1; i <= maximum_iteration; ++i) { + if (verbose) { + std::cout << " - iteration: " << std::setw(6) << i << " residu: " << std::scientific << residu / resid0 + << " absolute: " << std::scientific << residu << '\n'; } Ap_k = A * p_k; @@ -77,8 +81,8 @@ struct BiCGStab << '\n'; ; std::cout << " - epsilon: " << epsilon << '\n'; - std::cout << " - relative residu : " << residu / resid0 << '\n'; - std::cout << " - absolute residu : " << residu << '\n'; + std::cout << " - relative residu : " << std::scientific << residu / resid0 << '\n'; + std::cout << " - absolute residu : " << std::scientific << residu << '\n'; } } } diff --git a/src/algebra/PCG.hpp b/src/algebra/CG.hpp similarity index 60% rename from src/algebra/PCG.hpp rename to src/algebra/CG.hpp index 36a4635b3..538c77571 100644 --- a/src/algebra/PCG.hpp +++ b/src/algebra/CG.hpp @@ -6,30 +6,30 @@ #include <rang.hpp> -template <bool verbose = true> -struct PCG +struct CG { - template <typename VectorType, typename MatrixType, typename PreconditionerType> - PCG(const VectorType& f, - const MatrixType& A, - [[maybe_unused]] const PreconditionerType& C, - VectorType& x, - const size_t maxiter, - const double epsilon = 1e-6) + template <typename VectorType, typename MatrixType> + CG(const MatrixType& A, + VectorType& x, + const VectorType& f, + const double epsilon, + const size_t maximum_iteration, + const bool verbose = false) { - if constexpr (verbose) { + if (verbose) { std::cout << "- conjugate gradient\n"; std::cout << " epsilon = " << epsilon << '\n'; - std::cout << " maximum number of iterations: " << maxiter << '\n'; + std::cout << " maximum number of iterations: " << maximum_iteration << '\n'; } VectorType h{f.size()}; VectorType b = copy(f); - if constexpr (verbose) { + if (verbose) { h = A * x; h -= f; - std::cout << "- initial *real* residu : " << (h, h) << '\n'; + std::cout << "- initial " << rang::style::bold << "real" << rang::style::reset << " residu : " << (h, h) + << '\n'; } VectorType g{b.size()}; @@ -40,7 +40,7 @@ struct PCG double relativeEpsilon = epsilon; - for (size_t i = 1; i <= maxiter; ++i) { + for (size_t i = 1; i <= maximum_iteration; ++i) { if (i == 1) { h = A * x; @@ -74,13 +74,13 @@ struct PCG if ((i == 1) && (gcg != 0)) { relativeEpsilon = epsilon * gcg; gcg0 = gcg; - if constexpr (verbose) { + if (verbose) { std::cout << " initial residu: " << gcg << '\n'; } } - if constexpr (verbose) { - std::cout << " - iteration " << std::setw(6) << i << "\tresidu: " << gcg / gcg0; - std::cout << "\tabsolute: " << gcg << '\n'; + if (verbose) { + std::cout << " - iteration " << std::setw(6) << i << std::scientific << " residu: " << gcg / gcg0; + std::cout << " absolute: " << std::scientific << gcg << '\n'; } if (gcg < relativeEpsilon) { @@ -96,8 +96,8 @@ struct PCG if (gcg > relativeEpsilon) { std::cout << " conjugate gradient: " << rang::fgB::red << "*NOT CONVERGED*" << rang::style::reset << '\n'; std::cout << " - epsilon: " << epsilon << '\n'; - std::cout << " - relative residu : " << gcg / gcg0 << '\n'; - std::cout << " - absolute residu : " << gcg << '\n'; + std::cout << " - relative residu : " << std::scientific << gcg / gcg0 << '\n'; + std::cout << " - absolute residu : " << std::scientific << gcg << '\n'; } } }; diff --git a/src/algebra/CMakeLists.txt b/src/algebra/CMakeLists.txt index bbd2d01bf..d1f01ec31 100644 --- a/src/algebra/CMakeLists.txt +++ b/src/algebra/CMakeLists.txt @@ -2,5 +2,6 @@ add_library( PugsAlgebra - LinearSystemSolver.cpp + LinearSolver.cpp + LinearSolverOptions.cpp PETScWrapper.cpp) diff --git a/src/algebra/CRSMatrix.hpp b/src/algebra/CRSMatrix.hpp index 97a0a8a8e..d9b03ac93 100644 --- a/src/algebra/CRSMatrix.hpp +++ b/src/algebra/CRSMatrix.hpp @@ -30,8 +30,21 @@ class CRSMatrix return m_host_matrix.numRows(); } + auto + values() const + { + return m_values; + } + + auto + hostMatrix() const + { + return m_host_matrix; + } + template <typename DataType2> - Vector<MutableDataType> operator*(const Vector<DataType2>& x) const + Vector<MutableDataType> + operator*(const Vector<DataType2>& x) const { static_assert(std::is_same<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>(), "Cannot multiply matrix and vector of different type"); diff --git a/src/algebra/LinearSolver.cpp b/src/algebra/LinearSolver.cpp new file mode 100644 index 000000000..a275cd581 --- /dev/null +++ b/src/algebra/LinearSolver.cpp @@ -0,0 +1,320 @@ +#include <algebra/LinearSolver.hpp> +#include <utils/pugs_config.hpp> + +#include <algebra/BiCGStab.hpp> +#include <algebra/CG.hpp> + +#ifdef PUGS_HAS_PETSC +#include <petsc.h> +#endif // PUGS_HAS_PETSC + +struct LinearSolver::Internals +{ + static bool + hasLibrary(const LSLibrary library) + { + switch (library) { + case LSLibrary::builtin: { + return true; + } + case LSLibrary::petsc: { +#ifdef PUGS_HAS_PETSC + return true; +#else + return false; +#endif + } + default: { + throw UnexpectedError("Linear system library (" + ::name(library) + ") was not set!"); + } + } + } + + static void + checkHasLibrary(const LSLibrary library) + { + if (not hasLibrary(library)) { + throw NormalError(::name(library) + " is not linked to pugs. Cannot use it!"); + } + } + + static void + checkBuiltinMethod(const LSMethod method) + { + switch (method) { + case LSMethod::cg: + case LSMethod::bicgstab: { + break; + } + default: { + throw NormalError(name(method) + " is not a builtin linear solver!"); + } + } + } + + static void + checkPETScMethod(const LSMethod method) + { + switch (method) { + case LSMethod::cg: + case LSMethod::bicgstab: + case LSMethod::bicgstab2: + case LSMethod::gmres: + case LSMethod::lu: + case LSMethod::choleski: { + break; + } + default: { + throw NormalError(name(method) + " is not a builtin linear solver!"); + } + } + } + + static void + checkBuiltinPrecond(const LSPrecond precond) + { + switch (precond) { + case LSPrecond::none: { + break; + } + default: { + throw NormalError(name(precond) + " is not a builtin preconditioner!"); + } + } + } + + static void + checkPETScPrecond(const LSPrecond precond) + { + switch (precond) { + case LSPrecond::none: + case LSPrecond::diagonal: + case LSPrecond::incomplete_choleski: + case LSPrecond::incomplete_LU: { + break; + } + default: { + throw NormalError(name(precond) + " is not a PETSc preconditioner!"); + } + } + } + + static void + checkOptions(const LinearSolverOptions& options) + { + switch (options.library()) { + case LSLibrary::builtin: { + checkBuiltinMethod(options.method()); + checkBuiltinPrecond(options.precond()); + break; + } + case LSLibrary::petsc: { + checkPETScMethod(options.method()); + checkPETScPrecond(options.precond()); + break; + } + default: { + throw UnexpectedError("undefined options compatibility for this library (" + ::name(options.library()) + ")!"); + } + } + } + + static void + builtinSolveLocalSystem(const CRSMatrix<double, size_t>& A, + Vector<double>& x, + const Vector<double>& b, + const LinearSolverOptions& options) + { + if (options.precond() != LSPrecond::none) { + throw UnexpectedError("builtin linear solver do not allow any preconditioner!"); + } + switch (options.method()) { + case LSMethod::cg: { + CG{A, x, b, options.epsilon(), options.maximumIteration(), options.verbose()}; + break; + } + case LSMethod::bicgstab: { + BiCGStab{A, x, b, options.epsilon(), options.maximumIteration(), options.verbose()}; + break; + } + default: { + throw NotImplementedError("undefined builtin method: " + name(options.method())); + } + } + } + +#ifdef PUGS_HAS_PETSC + + static int + petscMonitor(KSP, int i, double residu, void*) + { + std::cout << " - iteration: " << std::setw(6) << i << " residu: " << std::scientific << residu << '\n'; + return 0; + } + + static void + petscSolveLocalSystem(const CRSMatrix<double, size_t>& A, + Vector<double>& x, + const Vector<double>& b, + const LinearSolverOptions& options) + { + Assert(x.size() == b.size() and x.size() == A.numberOfRows()); + + Vec petscB; + VecCreateMPIWithArray(PETSC_COMM_WORLD, 1, b.size(), b.size(), &b[0], &petscB); + Vec petscX; + VecCreateMPIWithArray(PETSC_COMM_WORLD, 1, x.size(), x.size(), &x[0], &petscX); + + Mat petscMat; + MatCreate(PETSC_COMM_WORLD, &petscMat); + MatSetSizes(petscMat, PETSC_DECIDE, PETSC_DECIDE, x.size(), x.size()); + + MatSetType(petscMat, MATAIJ); + + Array<PetscScalar> values = copy(A.values()); + auto hm = A.hostMatrix(); + + Array<PetscInt> row_indices{hm.row_map.size()}; + for (size_t i = 0; i < hm.row_map.size(); ++i) { + row_indices[i] = hm.row_map[i]; + } + + Array<PetscInt> column_indices{values.size()}; + size_t l = 0; + for (size_t i = 0; i < A.numberOfRows(); ++i) { + const auto row_i = hm.rowConst(i); + for (size_t j = 0; j < row_i.length; ++j) { + column_indices[l++] = row_i.colidx(j); + } + } + + MatCreateSeqAIJWithArrays(PETSC_COMM_WORLD, x.size(), x.size(), &row_indices[0], &column_indices[0], &values[0], + &petscMat); + + MatAssemblyBegin(petscMat, MAT_FINAL_ASSEMBLY); + MatAssemblyEnd(petscMat, MAT_FINAL_ASSEMBLY); + + KSP ksp; + KSPCreate(PETSC_COMM_WORLD, &ksp); + KSPSetTolerances(ksp, options.epsilon(), 1E-100, 1E5, options.maximumIteration()); + + KSPSetOperators(ksp, petscMat, petscMat); + + PC pc; + KSPGetPC(ksp, &pc); + + bool direct_solver = false; + + switch (options.method()) { + case LSMethod::bicgstab: { + KSPSetType(ksp, KSPBCGS); + break; + } + case LSMethod::bicgstab2: { + KSPSetType(ksp, KSPBCGSL); + KSPBCGSLSetEll(ksp, 2); + break; + } + case LSMethod::cg: { + KSPSetType(ksp, KSPCG); + break; + } + case LSMethod::gmres: { + KSPSetType(ksp, KSPGMRES); + + break; + } + case LSMethod::lu: { + KSPSetType(ksp, KSPPREONLY); + PCSetType(pc, PCLU); + PCFactorSetShiftType(pc, MAT_SHIFT_NONZERO); + direct_solver = true; + break; + } + case LSMethod::choleski: { + KSPSetType(ksp, KSPPREONLY); + PCSetType(pc, PCCHOLESKY); + direct_solver = true; + break; + } + default: { + throw UnexpectedError("unexpected method: " + name(options.method())); + } + } + + if (not direct_solver) { + switch (options.precond()) { + case LSPrecond::diagonal: { + PCSetType(pc, PCJACOBI); + break; + } + case LSPrecond::incomplete_LU: { + PCSetType(pc, PCILU); + break; + } + case LSPrecond::incomplete_choleski: { + PCSetType(pc, PCICC); + break; + } + case LSPrecond::none: { + PCSetType(pc, PCNONE); + break; + } + default: { + throw UnexpectedError("unexpected preconditioner: " + name(options.precond())); + } + } + } + if (options.verbose()) { + KSPMonitorSet(ksp, petscMonitor, 0, 0); + } + + KSPSolve(ksp, petscB, petscX); + MatDestroy(&petscMat); + } + +#else + static void + petscSolveLocalSystem(const CRSMatrix<double, size_t>&, + Vector<double>&, + const Vector<double>&, + const LinearSolverOptions&) + { + checkHasLibrary(LSLibrary::petsc); + throw UnexpectedError("unexpected situation should not reach this point!"); + } + +#endif // PUGS_HAS_PETSC +}; + +bool +LinearSolver::hasLibrary(LSLibrary library) const +{ + return Internals::hasLibrary(library); +} + +void +LinearSolver::solveLocalSystem(const CRSMatrix<double, size_t>& A, Vector<double>& x, const Vector<double>& b) +{ + switch (m_options.library()) { + case LSLibrary::builtin: { + Internals::builtinSolveLocalSystem(A, x, b, m_options); + break; + } + case LSLibrary::petsc: { + Internals::petscSolveLocalSystem(A, x, b, m_options); + break; + } + default: { + throw UnexpectedError(::name(m_options.library()) + " cannot solve local systems for sparse matrices"); + } + } +} + +LinearSolver::LinearSolver(const LinearSolverOptions& options) : m_options{options} +{ + Internals::checkHasLibrary(m_options.library()); + Internals::checkOptions(options); +} + +LinearSolver::LinearSolver() : LinearSolver{LinearSolverOptions::default_options} {} diff --git a/src/algebra/LinearSolver.hpp b/src/algebra/LinearSolver.hpp new file mode 100644 index 000000000..b72b09b0f --- /dev/null +++ b/src/algebra/LinearSolver.hpp @@ -0,0 +1,36 @@ +#ifndef LINEAR_SOLVER_HPP +#define LINEAR_SOLVER_HPP + +#include <algebra/CRSMatrix.hpp> +#include <algebra/LinearSolverOptions.hpp> +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <algebra/Vector.hpp> +#include <utils/Exceptions.hpp> + +class LinearSolver +{ + private: + struct Internals; + + const LinearSolverOptions m_options; + + void _solveLocalDense(size_t N, const double* A, double* x, const double* b); + + public: + bool hasLibrary(LSLibrary library) const; + + void solveLocalSystem(const CRSMatrix<double, size_t>& A, Vector<double>& x, const Vector<double>& b); + + template <size_t N> + void + solveLocalSystem(const TinyMatrix<N>& A, TinyVector<N>& x, const TinyVector<N>& b) + { + this->_solveLocalDense(N, &A(0, 0), &x[0], &b[0]); + } + + LinearSolver(); + LinearSolver(const LinearSolverOptions& options); +}; + +#endif // LINEAR_SOLVER_HPP diff --git a/src/algebra/LinearSolverOptions.cpp b/src/algebra/LinearSolverOptions.cpp new file mode 100644 index 000000000..b40c5fa92 --- /dev/null +++ b/src/algebra/LinearSolverOptions.cpp @@ -0,0 +1,15 @@ +#include <algebra/LinearSolverOptions.hpp> + +#include <rang.hpp> + +std::ostream& +operator<<(std::ostream& os, const LinearSolverOptions& options) +{ + os << " library: " << rang::style::bold << name(options.library()) << rang::style::reset << '\n'; + os << " method : " << rang::style::bold << name(options.method()) << rang::style::reset << '\n'; + os << " precond: " << rang::style::bold << name(options.precond()) << rang::style::reset << '\n'; + os << " epsilon: " << rang::style::bold << options.epsilon() << rang::style::reset << '\n'; + os << " maxiter: " << rang::style::bold << options.maximumIteration() << rang::style::reset << '\n'; + os << " verbose: " << rang::style::bold << std::boolalpha << options.verbose() << rang::style::reset << '\n'; + return os; +} diff --git a/src/algebra/LinearSolverOptions.hpp b/src/algebra/LinearSolverOptions.hpp new file mode 100644 index 000000000..bd7538b53 --- /dev/null +++ b/src/algebra/LinearSolverOptions.hpp @@ -0,0 +1,233 @@ +#ifndef LINEAR_SOLVER_OPTIONS_HPP +#define LINEAR_SOLVER_OPTIONS_HPP + +#include <utils/Exceptions.hpp> + +#include <iostream> + +enum class LSLibrary : int8_t +{ + LS__begin = 0, + // + builtin = LS__begin, + petsc, + // + LS__end +}; + +enum class LSMethod : int8_t +{ + LS__begin = 0, + // + cg = LS__begin, + bicgstab, + bicgstab2, + gmres, + lu, + choleski, + // + LS__end +}; + +enum class LSPrecond : int8_t +{ + LS__begin = 0, + // + none = LS__begin, + diagonal, + incomplete_choleski, + incomplete_LU, + // + LS__end +}; + +inline std::string +name(const LSLibrary library) +{ + switch (library) { + case LSLibrary::builtin: { + return "builtin"; + } + case LSLibrary::petsc: { + return "PETSc"; + } + case LSLibrary::LS__end: { + } + } + throw UnexpectedError("Linear system library name is not defined!"); +} + +inline std::string +name(const LSMethod method) +{ + switch (method) { + case LSMethod::cg: { + return "CG"; + } + case LSMethod::bicgstab: { + return "BICGStab"; + } + case LSMethod::bicgstab2: { + return "BICGStab2"; + } + case LSMethod::gmres: { + return "GMRES"; + } + case LSMethod::lu: { + return "LU"; + } + case LSMethod::choleski: { + return "Choleski"; + } + case LSMethod::LS__end: { + } + } + throw UnexpectedError("Linear system method name is not defined!"); +} + +inline std::string +name(const LSPrecond precond) +{ + switch (precond) { + case LSPrecond::none: { + return "none"; + } + case LSPrecond::diagonal: { + return "diagonal"; + } + case LSPrecond::incomplete_choleski: { + return "ICholeski"; + } + case LSPrecond::incomplete_LU: { + return "ILU"; + } + case LSPrecond::LS__end: { + } + } + throw UnexpectedError("Linear system preconditioner name is not defined!"); +} + +template <typename LSEnumType> +inline LSEnumType +getLSEnumFromName(const std::string& enum_name) +{ + using BaseT = std::underlying_type_t<LSEnumType>; + for (BaseT enum_value = static_cast<BaseT>(LSEnumType::LS__begin); + enum_value < static_cast<BaseT>(LSEnumType::LS__end); ++enum_value) { + if (name(LSEnumType{enum_value}) == enum_name) { + return LSEnumType{enum_value}; + } + } + throw NormalError(std::string{"could not find '"} + enum_name + "' associate type!"); +} + +template <typename LSEnumType> +inline void +printLSEnumListNames(std::ostream& os) +{ + using BaseT = std::underlying_type_t<LSEnumType>; + for (BaseT enum_value = static_cast<BaseT>(LSEnumType::LS__begin); + enum_value < static_cast<BaseT>(LSEnumType::LS__end); ++enum_value) { + os << " - " << name(LSEnumType{enum_value}) << '\n'; + } +} + +class LinearSolverOptions +{ + private: + LSLibrary m_library = LSLibrary::builtin; + LSMethod m_method = LSMethod::bicgstab; + LSPrecond m_precond = LSPrecond::none; + + double m_epsilon = 1E-6; + size_t m_maximum_iteration = 200; + + bool m_verbose = false; + + public: + static LinearSolverOptions default_options; + + friend std::ostream& operator<<(std::ostream& os, const LinearSolverOptions& options); + + LSLibrary& + library() + { + return m_library; + } + + LSLibrary + library() const + { + return m_library; + } + + LSMethod + method() const + { + return m_method; + } + + LSMethod& + method() + { + return m_method; + } + + LSPrecond + precond() const + { + return m_precond; + } + + LSPrecond& + precond() + { + return m_precond; + } + + double + epsilon() const + { + return m_epsilon; + } + + double& + epsilon() + { + return m_epsilon; + } + + size_t& + maximumIteration() + { + return m_maximum_iteration; + } + + size_t + maximumIteration() const + { + return m_maximum_iteration; + } + + bool& + verbose() + { + return m_verbose; + }; + + bool + verbose() const + { + return m_verbose; + }; + + LinearSolverOptions(const LinearSolverOptions&) = default; + LinearSolverOptions(LinearSolverOptions&&) = default; + + LinearSolverOptions() = default; + ~LinearSolverOptions() = default; +}; + +inline LinearSolverOptions LinearSolverOptions::default_options; + +#endif // LINEAR_SOLVER_OPTIONS_HPP diff --git a/src/algebra/LinearSystemSolver.cpp b/src/algebra/LinearSystemSolver.cpp deleted file mode 100644 index 488d6763d..000000000 --- a/src/algebra/LinearSystemSolver.cpp +++ /dev/null @@ -1,331 +0,0 @@ -#include <algebra/LinearSystemSolver.hpp> -#include <utils/pugs_config.hpp> - -#ifdef PUGS_HAS_PETSC -#include <petsc.h> -#endif // PUGS_HAS_PETSC - -struct LinearSystemSolver::Internals -{ - static bool - hasLibrary(LSLibrary library) - { - switch (library) { - case LSLibrary::builtin: { - return true; - } - case LSLibrary::petsc: { -#ifdef PUGS_HAS_PETSC - return true; -#else - return false; -#endif - } - default: { - throw UnexpectedError("Linear system library (" + name(library) + ") was not set!"); - } - } - } - - static void - checkHasLibrary(LSLibrary library) - { - if (not hasLibrary(library)) { - throw NormalError(name(library) + " is not linked to pugs. Cannot use it!"); - } - } - - static std::string - name(LSLibrary library) - { - switch (library) { - case LSLibrary::builtin: { - return "builtin"; - } - case LSLibrary::petsc: { - return "PETSc"; - } - default: { - throw UnexpectedError("Linear system library name is not defined!"); - } - } - } - - static std::string - name(LSMethod method) - { - switch (method) { - case LSMethod::cg: { - return "conjugate gradient"; - } - case LSMethod::bicgstab: { - return "bicg-stab"; - } - case LSMethod::gmres: { - return "gmres"; - } - default: { - throw UnexpectedError("Linear system method name is not defined!"); - } - } - } - - static std::string - name(LSPrecond precond) - { - switch (precond) { - case LSPrecond::none: { - return "none"; - } - case LSPrecond::diagonal: { - return "Jacobi (diagonal)"; - } - case LSPrecond::incomplete_choleski: { - return "incomplete Choleski"; - } - case LSPrecond::incomplete_LU: { - return "incomplete LU"; - } - default: { - throw UnexpectedError("Linear system preconditioner name is not defined!"); - } - } - } - - static void - checkBuiltinMethod(LSMethod method) - { - switch (method) { - case LSMethod::cg: - case LSMethod::bicgstab: { - break; - } - default: { - throw NormalError(name(method) + " is not a builtin linear solver!"); - } - } - } - - static void - checkPETScMethod(LSMethod method) - { - switch (method) { - case LSMethod::cg: - case LSMethod::bicgstab: - case LSMethod::gmres: { - break; - } - default: { - throw NormalError(name(method) + " is not a builtin linear solver!"); - } - } - } - - static void - checkBuiltinPrecond(LSPrecond precond) - { - switch (precond) { - case LSPrecond::none: { - break; - } - default: { - throw NormalError(name(precond) + " is not a builtin preconditioner!"); - } - } - } - - static void - checkPETScPrecond(LSPrecond precond) - { - switch (precond) { - case LSPrecond::none: - case LSPrecond::diagonal: - case LSPrecond::incomplete_choleski: - case LSPrecond::incomplete_LU: { - break; - } - default: { - throw NormalError(name(precond) + " is not a PETSc preconditioner!"); - } - } - } - - static void - checkOptions(LSLibrary library, LSMethod method, LSPrecond precond) - { - switch (library) { - case LSLibrary::builtin: { - checkBuiltinMethod(method); - checkBuiltinPrecond(precond); - break; - } - case LSLibrary::petsc: { - checkPETScMethod(method); - checkPETScPrecond(precond); - break; - } - default: { - throw UnexpectedError("Undefined options compatibility for this library (" + name(library) + ")!"); - } - } - } - -#ifdef PUGS_HAS_PETSC - - static int - petscMonitor(KSP, int it, double norm, void*) - { - std::cout << "iteration: " << it << " residual norm = " << norm << '\n'; - return 0; - } - - static void - petscSolveLocalSystem(const SparseMatrixDescriptor<double, size_t>& A, - Vector<double>& x, - const Vector<double>& b, - LSMethod method, - LSPrecond precond, - double epsilon, - size_t maximum_iteration) - { - Assert(x.size() == b.size() and x.size() == A.numberOfRows()); - - Vec petscB; - VecCreateMPIWithArray(PETSC_COMM_WORLD, 1, b.size(), b.size(), &b[0], &petscB); - Vec petscX; - VecCreateMPIWithArray(PETSC_COMM_WORLD, 1, x.size(), x.size(), &x[0], &petscX); - - Mat petscMat; - MatCreate(PETSC_COMM_WORLD, &petscMat); - MatSetSizes(petscMat, PETSC_DECIDE, PETSC_DECIDE, x.size(), x.size()); - - MatSetType(petscMat, MATAIJ); - MatSetUp(petscMat); - - for (size_t i = 0; i < A.numberOfRows(); ++i) { - const auto& row = A.row(i); - PetscInt I = i; - for (auto [j, value] : row.columnIdToValueMap()) { - PetscInt J = j; - MatSetValues(petscMat, 1, &I, 1, &J, &value, INSERT_VALUES); - } - } - - MatAssemblyBegin(petscMat, MAT_FINAL_ASSEMBLY); - MatAssemblyEnd(petscMat, MAT_FINAL_ASSEMBLY); - - PetscPrintf(PETSC_COMM_WORLD, "Matrix _before_ MatScaleUserImpl() operation\n"); - MatView(petscMat, PETSC_VIEWER_STDOUT_SELF); - - KSP ksp; - - KSPCreate(PETSC_COMM_WORLD, &ksp); - KSPSetTolerances(ksp, epsilon, 1E-100, 1E5, maximum_iteration); - - KSPSetOperators(ksp, petscMat, petscMat); - - switch (method) { - case LSMethod::bicgstab: { - KSPSetType(ksp, KSPBCGS); - break; - } - case LSMethod::cg: { - KSPSetType(ksp, KSPCG); - break; - } - case LSMethod::gmres: { - KSPSetType(ksp, KSPGMRES); - break; - } - default: { - throw UnexpectedError("unexpected method: " + name(method)); - } - } - - PC pc; - KSPGetPC(ksp, &pc); - - switch (precond) { - case LSPrecond::diagonal: { - PCSetType(pc, PCJACOBI); - break; - } - case LSPrecond::incomplete_LU: { - PCSetType(pc, PCILU); - break; - } - case LSPrecond::incomplete_choleski: { - PCSetType(pc, PCICC); - break; - } - case LSPrecond::none: { - PCSetType(pc, PCNONE); - break; - } - default: { - throw UnexpectedError("unexpected preconditioner: " + name(precond)); - } - } - - KSPMonitorSet(ksp, petscMonitor, 0, 0); - - KSPSolve(ksp, petscB, petscX); - - VecView(petscX, PETSC_VIEWER_STDOUT_SELF); - - MatDestroy(&petscMat); - } - -#else - - static void - petscSolveLocalSystem(const SparseMatrixDescriptor<double, size_t>&, - Vector<double>&, - const Vector<double>&, - LSMethod, - LSPrecond, - double, - size_t) - { - checkHasLibrary(LSLibrary::petsc); - } - -#endif // PUGS_HAS_PETSC -}; - -bool -LinearSystemSolver::hasLibrary(LSLibrary library) const -{ - return Internals::hasLibrary(library); -} - -LinearSystemSolver::LinearSystemSolver(LSLibrary library, - LSMethod method, - LSPrecond precond, - double epsilon, - size_t maximum_iteration) - : m_library{library}, m_method{method}, m_precond{precond}, m_epsilon{epsilon}, m_maximum_iteration{maximum_iteration} -{ - Internals::checkHasLibrary(library); - Internals::checkOptions(library, method, precond); -} - -void -LinearSystemSolver::solveLocalSystem(const SparseMatrixDescriptor<double, size_t>& A, - Vector<double>& x, - const Vector<double>& b) -{ - switch (m_library) { - case LSLibrary::builtin: { - throw NotImplementedError("builtin solvers are not plugged yet!"); - break; - } - case LSLibrary::petsc: { - Internals::petscSolveLocalSystem(A, x, b, m_method, m_precond, m_epsilon, m_maximum_iteration); - break; - } - default: { - throw UnexpectedError(Internals::name(m_library) + " cannot solve local systems for sparse matrices"); - } - } -} diff --git a/src/algebra/LinearSystemSolver.hpp b/src/algebra/LinearSystemSolver.hpp deleted file mode 100644 index f689f96dd..000000000 --- a/src/algebra/LinearSystemSolver.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef LINEAR_SYSTEM_SOLVER_HPP -#define LINEAR_SYSTEM_SOLVER_HPP - -#include <algebra/SparseMatrixDescriptor.hpp> -#include <algebra/TinyMatrix.hpp> -#include <algebra/TinyVector.hpp> -#include <algebra/Vector.hpp> - -#include <utils/Exceptions.hpp> - -enum class LSLibrary -{ - builtin, - petsc -}; - -enum class LSMethod -{ - cg, - bicgstab, - gmres -}; - -enum class LSPrecond -{ - none, - diagonal, - incomplete_choleski, - incomplete_LU -}; - -class LinearSystemSolver -{ - private: - struct Internals; - - const LSLibrary m_library; - const LSMethod m_method; - const LSPrecond m_precond; - - const double m_epsilon; - const size_t m_maximum_iteration; - - bool m_is_verbose = false; - - void _solveLocalDense(size_t N, const double* A, double* x, const double* b); - - public: - bool - isVerbose() const - { - return m_is_verbose; - } - - void - setVerbose(bool verbose) - { - m_is_verbose = verbose; - } - - bool hasLibrary(LSLibrary library) const; - - void solveLocalSystem(const SparseMatrixDescriptor<double, size_t>& A, Vector<double>& x, const Vector<double>& b); - - template <size_t N> - void - solveLocalSystem(const TinyMatrix<N>& A, TinyVector<N>& x, const TinyVector<N>& b) - { - this->_solveLocalDense(N, &A(0, 0), &x[0], &b[0]); - } - - LinearSystemSolver(LSLibrary library, - LSMethod method, - LSPrecond precond, - double epsilon = 1e-6, - size_t maximum_iteration = 100); -}; - -#endif // LINEAR_SYSTEM_SOLVER_HPP diff --git a/src/algebra/PETScWrapper.cpp b/src/algebra/PETScWrapper.cpp index 76e56692a..dd11dc977 100644 --- a/src/algebra/PETScWrapper.cpp +++ b/src/algebra/PETScWrapper.cpp @@ -12,6 +12,7 @@ void initialize([[maybe_unused]] int& argc, [[maybe_unused]] char* argv[]) { #ifdef PUGS_HAS_PETSC + PetscOptionsSetValue(NULL, "-no_signal_handler", "true"); PetscInitialize(&argc, &argv, 0, 0); #endif // PUGS_HAS_PETSC } diff --git a/src/algebra/SparseMatrixDescriptor.hpp b/src/algebra/SparseMatrixDescriptor.hpp index 09f13085b..d5c14713b 100644 --- a/src/algebra/SparseMatrixDescriptor.hpp +++ b/src/algebra/SparseMatrixDescriptor.hpp @@ -151,7 +151,12 @@ class SparseMatrixDescriptor return values; } - SparseMatrixDescriptor(size_t nb_row) : m_row_array{nb_row} {} + SparseMatrixDescriptor(size_t nb_row) : m_row_array{nb_row} + { + for (size_t i = 0; i < nb_row; ++i) { + m_row_array[i][i] = 0; + } + } ~SparseMatrixDescriptor() = default; }; diff --git a/src/language/ast/ASTNodeDataType.cpp b/src/language/ast/ASTNodeDataType.cpp index 10ec58b45..ce2bbcc60 100644 --- a/src/language/ast/ASTNodeDataType.cpp +++ b/src/language/ast/ASTNodeDataType.cpp @@ -48,11 +48,15 @@ dataTypeName(const ASTNodeDataType& data_type) case ASTNodeDataType::list_t: { std::ostringstream data_type_name_list; const auto& data_type_list = data_type.contentTypeList(); - data_type_name_list << dataTypeName(*data_type_list[0]); - for (size_t i = 1; i < data_type_list.size(); ++i) { - data_type_name_list << '*' << dataTypeName(*data_type_list[i]); + if (data_type_list.size() > 0) { + data_type_name_list << dataTypeName(*data_type_list[0]); + for (size_t i = 1; i < data_type_list.size(); ++i) { + data_type_name_list << '*' << dataTypeName(*data_type_list[i]); + } + name = "list(" + data_type_name_list.str() + ")"; + } else { + name = "list(void)"; } - name = "list(" + data_type_name_list.str() + ")"; break; } case ASTNodeDataType::string_t: diff --git a/src/language/modules/CMakeLists.txt b/src/language/modules/CMakeLists.txt index f8cf2f0b4..0a996fd67 100644 --- a/src/language/modules/CMakeLists.txt +++ b/src/language/modules/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(PugsLanguageModules BuiltinModule.cpp + LinearSolverModule.cpp MathModule.cpp MeshModule.cpp ModuleRepository.cpp diff --git a/src/language/modules/LinearSolverModule.cpp b/src/language/modules/LinearSolverModule.cpp new file mode 100644 index 000000000..9e626ba2b --- /dev/null +++ b/src/language/modules/LinearSolverModule.cpp @@ -0,0 +1,85 @@ +#include <language/modules/LinearSolverModule.hpp> + +#include <algebra/LinearSolver.hpp> +#include <language/utils/BuiltinFunctionEmbedder.hpp> +#include <language/utils/TypeDescriptor.hpp> + +LinearSolverModule::LinearSolverModule() +{ + this->_addBuiltinFunction("setLSVerbosity", std::make_shared<BuiltinFunctionEmbedder<void(const bool&)>>( + + [](const bool& verbose) -> void { + LinearSolverOptions::default_options.verbose() = verbose; + } + + )); + + this->_addBuiltinFunction("setLSEpsilon", std::make_shared<BuiltinFunctionEmbedder<void(const double&)>>( + + [](const double& epsilon) -> void { + LinearSolverOptions::default_options.epsilon() = epsilon; + } + + )); + + this->_addBuiltinFunction("setLSMaxIter", std::make_shared<BuiltinFunctionEmbedder<void(const uint64_t&)>>( + + [](const uint64_t& max_iter) -> void { + LinearSolverOptions::default_options.maximumIteration() = max_iter; + } + + )); + + this->_addBuiltinFunction("setLSLibrary", std::make_shared<BuiltinFunctionEmbedder<void(const std::string&)>>( + + [](const std::string& library_name) -> void { + LinearSolverOptions::default_options.library() = + getLSEnumFromName<LSLibrary>(library_name); + } + + )); + + this->_addBuiltinFunction("setLSMethod", std::make_shared<BuiltinFunctionEmbedder<void(const std::string&)>>( + + [](const std::string& method_name) -> void { + LinearSolverOptions::default_options.method() = + getLSEnumFromName<LSMethod>(method_name); + } + + )); + + this->_addBuiltinFunction("setLSPrecond", std::make_shared<BuiltinFunctionEmbedder<void(const std::string&)>>( + + [](const std::string& precond_name) -> void { + LinearSolverOptions::default_options.precond() = + getLSEnumFromName<LSPrecond>(precond_name); + } + + )); + + this->_addBuiltinFunction("printLSOptions", std::make_shared<BuiltinFunctionEmbedder<void()>>( + + []() -> void { + std::cout << rang::fgB::yellow << "Linear solver options" + << rang::style::reset << '\n'; + std::cout << LinearSolverOptions::default_options; + } + + )); + + this->_addBuiltinFunction("printLSAvailable", + std::make_shared<BuiltinFunctionEmbedder<void()>>( + + []() -> void { + std::cout << rang::fgB::yellow << "Available linear solver options" + << rang::style::reset << '\n'; + std::cout << rang::fgB::blue << " libraries" << rang::style::reset << '\n'; + printLSEnumListNames<LSLibrary>(std::cout); + std::cout << rang::fgB::blue << " methods" << rang::style::reset << '\n'; + printLSEnumListNames<LSMethod>(std::cout); + std::cout << rang::fgB::blue << " preconditioners" << rang::style::reset << '\n'; + printLSEnumListNames<LSPrecond>(std::cout); + } + + )); +} diff --git a/src/language/modules/LinearSolverModule.hpp b/src/language/modules/LinearSolverModule.hpp new file mode 100644 index 000000000..5dc7ae64e --- /dev/null +++ b/src/language/modules/LinearSolverModule.hpp @@ -0,0 +1,19 @@ +#ifndef LINEAR_SOLVER_MODULE_HPP +#define LINEAR_SOLVER_MODULE_HPP + +#include <language/modules/BuiltinModule.hpp> + +class LinearSolverModule : public BuiltinModule +{ + public: + std::string_view + name() const final + { + return "linear_solver"; + } + + LinearSolverModule(); + ~LinearSolverModule() = default; +}; + +#endif // LINEAR_SOLVER_MODULE_HPP diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp index 497ba083a..df403fa87 100644 --- a/src/language/modules/ModuleRepository.cpp +++ b/src/language/modules/ModuleRepository.cpp @@ -1,6 +1,7 @@ #include <language/modules/ModuleRepository.hpp> #include <language/ast/ASTNode.hpp> +#include <language/modules/LinearSolverModule.hpp> #include <language/modules/MathModule.hpp> #include <language/modules/MeshModule.hpp> #include <language/modules/SchemeModule.hpp> @@ -23,6 +24,7 @@ ModuleRepository::ModuleRepository() this->_subscribe(std::make_unique<MeshModule>()); this->_subscribe(std::make_unique<VTKModule>()); this->_subscribe(std::make_unique<SchemeModule>()); + this->_subscribe(std::make_unique<LinearSolverModule>()); } template <typename NameEmbedderMapT, typename EmbedderTableT> diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8b2a7c551..8d0344a03 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -47,7 +47,7 @@ add_executable (unit_tests test_BuiltinFunctionEmbedder.cpp test_BuiltinFunctionEmbedderTable.cpp test_BuiltinFunctionProcessor.cpp - test_MathModule.cpp + test_CG.cpp test_ContinueProcessor.cpp test_ConcatExpressionProcessor.cpp test_CRSMatrix.cpp @@ -65,9 +65,9 @@ add_executable (unit_tests test_INodeProcessor.cpp test_ItemType.cpp test_ListAffectationProcessor.cpp + test_MathModule.cpp test_NameProcessor.cpp test_OStreamProcessor.cpp - test_PCG.cpp test_PugsFunctionAdapter.cpp test_PugsAssert.cpp test_RevisionInfo.cpp @@ -98,6 +98,7 @@ target_link_libraries (unit_tests kokkos ${PARMETIS_LIBRARIES} ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES} + ${PETSC_LIBRARIES} Catch2 ) @@ -108,6 +109,7 @@ target_link_libraries (mpi_unit_tests kokkos ${PARMETIS_LIBRARIES} ${MPI_CXX_LINK_FLAGS} ${MPI_CXX_LIBRARIES} + ${PETSC_LIBRARIES} Catch2 ) diff --git a/tests/test_BiCGStab.cpp b/tests/test_BiCGStab.cpp index 99b94515b..49a6955be 100644 --- a/tests/test_BiCGStab.cpp +++ b/tests/test_BiCGStab.cpp @@ -45,7 +45,7 @@ TEST_CASE("BiCGStab", "[algebra]") Vector<double> x{5}; x = 0; - BiCGStab<false>{b, A, x, 10, 1e-12}; + BiCGStab{A, x, b, 1e-12, 10, false}; Vector error = x - x_exact; REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x, x))); } @@ -88,7 +88,7 @@ TEST_CASE("BiCGStab", "[algebra]") Vector<double> x{5}; x = 0; - BiCGStab{b, A, x, 1, 1e-12}; + BiCGStab{A, x, b, 1e-12, 1, true}; Vector error = x - x_exact; REQUIRE(std::sqrt((error, error)) > 1E-5 * std::sqrt((x, x))); } diff --git a/tests/test_PCG.cpp b/tests/test_CG.cpp similarity index 91% rename from tests/test_PCG.cpp rename to tests/test_CG.cpp index d4564761d..2ce9febb0 100644 --- a/tests/test_PCG.cpp +++ b/tests/test_CG.cpp @@ -1,11 +1,11 @@ #include <catch2/catch.hpp> +#include <algebra/CG.hpp> #include <algebra/CRSMatrix.hpp> -#include <algebra/PCG.hpp> // clazy:excludeall=non-pod-global-static -TEST_CASE("PCG", "[algebra]") +TEST_CASE("CG", "[algebra]") { SECTION("no preconditionner") { @@ -45,7 +45,7 @@ TEST_CASE("PCG", "[algebra]") Vector<double> x{5}; x = 0; - PCG{b, A, A, x, 10, 1e-12}; + CG{A, x, b, 1e-12, 10}; Vector error = x - x_exact; REQUIRE(std::sqrt((error, error)) < 1E-10 * std::sqrt((x, x))); } @@ -62,7 +62,7 @@ TEST_CASE("PCG", "[algebra]") Vector<double> x{5}; x = 0; - PCG<false>{b, A, A, x, 10, 1e-12}; + CG{A, x, b, 1e-12, 10, false}; REQUIRE(std::sqrt((x, x)) == 0); } @@ -104,7 +104,7 @@ TEST_CASE("PCG", "[algebra]") Vector<double> x{5}; x = 0; - PCG<false>{b, A, A, x, 1, 1e-12}; + CG{A, x, b, 1e-12, 1, false}; Vector error = x - x_exact; REQUIRE(std::sqrt((error, error)) > 1E-10 * std::sqrt((x, x))); } diff --git a/tests/test_SparseMatrixDescriptor.cpp b/tests/test_SparseMatrixDescriptor.cpp index cd66380b1..f90cc83b3 100644 --- a/tests/test_SparseMatrixDescriptor.cpp +++ b/tests/test_SparseMatrixDescriptor.cpp @@ -68,18 +68,18 @@ TEST_CASE("SparseMatrixDescriptor", "[algebra]") S(4, 1) = 1; S(4, 4) = -2; - REQUIRE(S.row(0).numberOfValues() == 1); + REQUIRE(S.row(0).numberOfValues() == 2); REQUIRE(S.row(1).numberOfValues() == 2); REQUIRE(S.row(2).numberOfValues() == 1); - REQUIRE(S.row(3).numberOfValues() == 1); + REQUIRE(S.row(3).numberOfValues() == 2); REQUIRE(S.row(4).numberOfValues() == 2); const auto& const_S = S; - REQUIRE(const_S.row(0).numberOfValues() == 1); + REQUIRE(const_S.row(0).numberOfValues() == 2); REQUIRE(const_S.row(1).numberOfValues() == 2); REQUIRE(const_S.row(2).numberOfValues() == 1); - REQUIRE(const_S.row(3).numberOfValues() == 1); + REQUIRE(const_S.row(3).numberOfValues() == 2); REQUIRE(const_S.row(4).numberOfValues() == 2); #ifndef NDEBUG @@ -126,7 +126,7 @@ TEST_CASE("SparseMatrixDescriptor", "[algebra]") const auto graph = S.graphVector(); REQUIRE(graph.size() == S.numberOfRows()); - REQUIRE(graph[0].size() == 1); + REQUIRE(graph[0].size() == 2); REQUIRE(graph[1].size() == 2); REQUIRE(graph[2].size() == 1); REQUIRE(graph[3].size() == 2); @@ -157,7 +157,7 @@ TEST_CASE("SparseMatrixDescriptor", "[algebra]") const auto value_array = S.valueArray(); - REQUIRE(value_array.size() == 8); + REQUIRE(value_array.size() == 9); REQUIRE(value_array[0] == 5); REQUIRE(value_array[1] == 1); @@ -186,7 +186,7 @@ TEST_CASE("SparseMatrixDescriptor", "[algebra]") output << '\n' << S; std::string expected_output = R"( -0 | 2:5 +0 | 0:0 2:5 1 | 1:1 2:11 2 | 2:4 3 | 1:-3 3:5 -- GitLab