#include "fesa/solvers/linear/linear_solver.hpp" #include "fesa/solvers/linear/mkl_pardiso_solver.hpp" #include "fesa/fem/dof_manager.hpp" #include "fesa/math/sparse_matrix.hpp" #include #include #include #include namespace { fesa::SparseMatrix makeDenseCsr( const std::size_t rows, const std::size_t columns, const std::vector& values) { EXPECT_EQ(values.size(), rows * columns); fesa::SparsePattern pattern; std::vector contributions; pattern.rowOffsets.reserve(rows + 1U); pattern.rowOffsets.push_back(0U); for (std::size_t row = 0U; row < rows; ++row) { for (std::size_t column = 0U; column < columns; ++column) { pattern.columnIndices.push_back(column); contributions.push_back({ row, column, values[row * columns + column], row, column}); } pattern.rowOffsets.push_back(pattern.columnIndices.size()); } auto matrix = fesa::SparseMatrix::fromCoo( rows, columns, std::move(contributions), pattern); EXPECT_TRUE(matrix.hasValue()); return std::move(matrix.value()); } void expectSolverFailure(const fesa::Status& status) { EXPECT_FALSE(status.isOk()); EXPECT_EQ(status.failureCategory(), fesa::FailureCategory::solver); ASSERT_FALSE(status.diagnostics().empty()); EXPECT_EQ(status.diagnostics().front().severity, fesa::Severity::error); } } // namespace TEST(MklPardisoSolver, RejectsInvalidCsrStateAndDimensions) { static_assert(std::is_base_of_v); static_assert(std::has_virtual_destructor_v); fesa::MklPardisoSolver solver; fesa::Vector solution{2U}; expectSolverFailure(solver.solve(fesa::Vector{2U, 1.0}, solution)); EXPECT_EQ( solver.solve(fesa::Vector{2U, 1.0}, solution) .diagnostics() .front() .code, "solver-not-factorized"); const auto rectangular = makeDenseCsr( 2U, 3U, {2.0, 0.0, 0.0, 0.0, 3.0, 0.0}); const auto rectangularStatus = solver.factorize(rectangular); expectSolverFailure(rectangularStatus); EXPECT_EQ( rectangularStatus.diagnostics().front().code, "solver-matrix-not-square"); const auto empty = makeDenseCsr(0U, 0U, {}); const auto emptyStatus = solver.factorize(empty); expectSolverFailure(emptyStatus); EXPECT_EQ(emptyStatus.diagnostics().front().code, "solver-empty-matrix"); fesa::SparsePattern invalidPattern{{0U, 2U}, {0U}}; auto invalidCsr = fesa::SparseMatrix::fromCoo( 1U, 1U, {{0U, 0U, 1.0, 0U, 0U}}, invalidPattern); EXPECT_FALSE(invalidCsr.hasValue()); const auto nonsymmetric = makeDenseCsr(2U, 2U, {2.0, 1.0, 0.0, 3.0}); const auto nonsymmetricStatus = solver.factorize(nonsymmetric); expectSolverFailure(nonsymmetricStatus); EXPECT_EQ( nonsymmetricStatus.diagnostics().front().code, "solver-matrix-not-symmetric"); fesa::SparsePattern noDiagonalPattern{{0U, 1U, 2U}, {1U, 0U}}; auto noDiagonal = fesa::SparseMatrix::fromCoo( 2U, 2U, {{0U, 1U, 1.0, 0U, 0U}, {1U, 0U, 1.0, 1U, 0U}}, noDiagonalPattern); ASSERT_TRUE(noDiagonal.hasValue()); const auto noDiagonalStatus = solver.factorize(noDiagonal.value()); expectSolverFailure(noDiagonalStatus); EXPECT_EQ( noDiagonalStatus.diagnostics().front().code, "solver-missing-diagonal"); const auto spd = makeDenseCsr(2U, 2U, {4.0, 1.0, 1.0, 3.0}); ASSERT_TRUE(solver.factorize(spd).isOk()); expectSolverFailure(solver.solve(fesa::Vector{1U, 1.0}, solution)); fesa::Vector wrongSolution{1U}; expectSolverFailure(solver.solve(fesa::Vector{2U, 1.0}, wrongSolution)); }