feat(linear-static-3d-euler-beam): step 20 - mkl-pardiso-solver-review-fix
This commit is contained in:
@@ -55,14 +55,45 @@ TEST(MklPardisoSolver, RejectsInvalidCsrStateAndDimensions) {
|
||||
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));
|
||||
fesa::Vector untouched{2U};
|
||||
untouched[0U] = 17.0;
|
||||
untouched[1U] = -4.0;
|
||||
const auto beforeFactorize =
|
||||
solver.solve(fesa::Vector{2U, 1.0}, untouched);
|
||||
expectSolverFailure(beforeFactorize);
|
||||
EXPECT_EQ(
|
||||
solver.solve(fesa::Vector{2U, 1.0}, solution)
|
||||
.diagnostics()
|
||||
.front()
|
||||
.code,
|
||||
beforeFactorize.diagnostics().front().code,
|
||||
"solver-not-factorized");
|
||||
EXPECT_DOUBLE_EQ(untouched[0U], 17.0);
|
||||
EXPECT_DOUBLE_EQ(untouched[1U], -4.0);
|
||||
|
||||
// A fully constrained model has a valid 0x0 Kff. It still observes the
|
||||
// factorize-then-solve lifecycle without invoking a numerical backend.
|
||||
const auto empty = makeDenseCsr(0U, 0U, {});
|
||||
ASSERT_TRUE(solver.factorize(empty).isOk());
|
||||
fesa::Vector emptySolution{0U};
|
||||
EXPECT_TRUE(solver.solve(fesa::Vector{0U}, emptySolution).isOk());
|
||||
EXPECT_EQ(emptySolution.size(), 0U);
|
||||
|
||||
// Refactorization from the trivial state must establish ordinary PARDISO
|
||||
// state rather than retaining a zero-equation shortcut.
|
||||
const auto spd = makeDenseCsr(2U, 2U, {4.0, 1.0, 1.0, 3.0});
|
||||
ASSERT_TRUE(solver.factorize(spd).isOk());
|
||||
fesa::Vector solution{2U};
|
||||
ASSERT_TRUE(solver.solve(fesa::Vector{2U, 1.0}, solution).isOk());
|
||||
EXPECT_NEAR(solution[0U], 2.0 / 11.0, 1.0e-14);
|
||||
EXPECT_NEAR(solution[1U], 3.0 / 11.0, 1.0e-14);
|
||||
|
||||
const double solvedFirst = solution[0U];
|
||||
const double solvedSecond = solution[1U];
|
||||
expectSolverFailure(solver.solve(fesa::Vector{1U, 1.0}, solution));
|
||||
EXPECT_DOUBLE_EQ(solution[0U], solvedFirst);
|
||||
EXPECT_DOUBLE_EQ(solution[1U], solvedSecond);
|
||||
|
||||
fesa::Vector wrongSolution{1U};
|
||||
wrongSolution[0U] = 41.0;
|
||||
expectSolverFailure(solver.solve(fesa::Vector{2U, 1.0}, wrongSolution));
|
||||
EXPECT_DOUBLE_EQ(wrongSolution[0U], 41.0);
|
||||
|
||||
const auto rectangular = makeDenseCsr(
|
||||
2U, 3U, {2.0, 0.0, 0.0, 0.0, 3.0, 0.0});
|
||||
@@ -72,11 +103,6 @@ TEST(MklPardisoSolver, RejectsInvalidCsrStateAndDimensions) {
|
||||
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,
|
||||
@@ -92,6 +118,18 @@ TEST(MklPardisoSolver, RejectsInvalidCsrStateAndDimensions) {
|
||||
nonsymmetricStatus.diagnostics().front().code,
|
||||
"solver-matrix-not-symmetric");
|
||||
|
||||
const auto scaledNonsymmetric = makeDenseCsr(
|
||||
2U, 2U, {2.0e-20, 1.0e-20, 1.1e-20, 3.0e-20});
|
||||
const auto scaledNonsymmetricStatus =
|
||||
solver.factorize(scaledNonsymmetric);
|
||||
// Stop this case before inspecting diagnostics when the production code
|
||||
// incorrectly accepts the matrix; this keeps the RED failure deterministic.
|
||||
ASSERT_FALSE(scaledNonsymmetricStatus.isOk());
|
||||
expectSolverFailure(scaledNonsymmetricStatus);
|
||||
EXPECT_EQ(
|
||||
scaledNonsymmetricStatus.diagnostics().front().code,
|
||||
"solver-matrix-not-symmetric");
|
||||
|
||||
fesa::SparsePattern noDiagonalPattern{{0U, 1U, 2U}, {1U, 0U}};
|
||||
auto noDiagonal = fesa::SparseMatrix::fromCoo(
|
||||
2U,
|
||||
@@ -105,9 +143,4 @@ TEST(MklPardisoSolver, RejectsInvalidCsrStateAndDimensions) {
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <initializer_list>
|
||||
#include <limits>
|
||||
@@ -58,7 +57,16 @@ double normalizedResidual(
|
||||
const fesa::Vector& rhs) {
|
||||
auto residual = matrix.multiply(solution);
|
||||
residual.axpy(-1.0, rhs);
|
||||
return residual.norm() / (std::max)(1.0, rhs.norm());
|
||||
const double numerator = residual.norm();
|
||||
const double denominator = rhs.norm();
|
||||
if (!std::isfinite(numerator) || !std::isfinite(denominator)) {
|
||||
return (std::numeric_limits<double>::infinity)();
|
||||
}
|
||||
if (denominator == 0.0) {
|
||||
return numerator == 0.0 ? 0.0 :
|
||||
(std::numeric_limits<double>::infinity)();
|
||||
}
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
double relativeError(
|
||||
@@ -66,7 +74,16 @@ double relativeError(
|
||||
const fesa::Vector& expected) {
|
||||
auto difference = actual;
|
||||
difference.axpy(-1.0, expected);
|
||||
return difference.norm() / (std::max)(1.0, expected.norm());
|
||||
const double numerator = difference.norm();
|
||||
const double denominator = expected.norm();
|
||||
if (!std::isfinite(numerator) || !std::isfinite(denominator)) {
|
||||
return (std::numeric_limits<double>::infinity)();
|
||||
}
|
||||
if (denominator == 0.0) {
|
||||
return numerator == 0.0 ? 0.0 :
|
||||
(std::numeric_limits<double>::infinity)();
|
||||
}
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
void expectStructuredSolverFailure(const fesa::Status& status) {
|
||||
@@ -174,9 +191,13 @@ TEST(MklPardisoSolver, ClassifiesSingularIndefiniteAndNonfiniteFailures) {
|
||||
auto rhs = makeVector({1.0, 2.0});
|
||||
rhs[1U] = (std::numeric_limits<double>::infinity)();
|
||||
fesa::Vector solution{2U};
|
||||
solution[0U] = 23.0;
|
||||
solution[1U] = -9.0;
|
||||
const auto rhsStatus = solver.solve(rhs, solution);
|
||||
expectStructuredSolverFailure(rhsStatus);
|
||||
EXPECT_EQ(rhsStatus.diagnostics()[0U].code, "nonfinite-solver-rhs");
|
||||
EXPECT_DOUBLE_EQ(solution[0U], 23.0);
|
||||
EXPECT_DOUBLE_EQ(solution[1U], -9.0);
|
||||
|
||||
fesa::SparsePattern pattern{{0U, 1U}, {0U}};
|
||||
auto nonfiniteMatrix = fesa::SparseMatrix::fromCoo(
|
||||
|
||||
Reference in New Issue
Block a user