feat(linear-static-3d-euler-beam): step 20 - mkl-pardiso-solver

This commit is contained in:
KOKO\Mimi
2026-08-09 20:05:20 +09:00
parent 584c5c8714
commit 80f25e569a
8 changed files with 840 additions and 0 deletions
@@ -0,0 +1,113 @@
#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 <gtest/gtest.h>
#include <type_traits>
#include <utility>
#include <vector>
namespace {
fesa::SparseMatrix makeDenseCsr(
const std::size_t rows,
const std::size_t columns,
const std::vector<double>& values) {
EXPECT_EQ(values.size(), rows * columns);
fesa::SparsePattern pattern;
std::vector<fesa::CooContribution> 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<fesa::LinearSolver, fesa::MklPardisoSolver>);
static_assert(std::has_virtual_destructor_v<fesa::LinearSolver>);
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));
}