Integrate Refactor solver sources and dual MSVC CMake builds

This commit is contained in:
KOKO\Mimi
2026-10-18 02:10:51 +09:00
parent 7ee32e5837
commit 12d51480de
218 changed files with 17842 additions and 9689 deletions
+67 -33
View File
@@ -6,7 +6,6 @@
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <utility>
#include <vector>
@@ -93,38 +92,6 @@ TEST(EigenReduction, RemovesOnlyActualFreeDirectorNull) {
.HasValue());
}
TEST(EigenReduction, RejectsNonfiniteUsedBasisAndPreservesPriorCandidate) {
const fesa::ShellNodeInitialFrame frame{0U, {0, 0, 1}, {1, 0, 0}, {0, 1, 0}};
const auto mass = NodalMass(frame.director);
fesa::Matrix stiffness(6U, 6U);
for (std::size_t i = 0; i < 5U; ++i) stiffness(i, i) = mass(i, i) * 7.0;
stiffness(5U, 5U) = .25;
const auto matrix = Sparse(stiffness);
const std::vector<std::uint8_t> mask(6U, 0U);
const auto prior = fesa::eigen_internal::BuildEigenReduction(
matrix, {mass}, {frame}, mask, {.25}, 2.0);
ASSERT_TRUE(prior.HasValue());
const auto prior_values = prior.Value().whitened_operator.Values();
for (const double invalid : {std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::infinity()}) {
for (const bool first_tangent : {false, true}) {
auto bad_frame = frame;
(first_tangent ? bad_frame.tangent_a : bad_frame.tangent_b)[0] = invalid;
const auto rejected = fesa::eigen_internal::BuildEigenReduction(
matrix, {mass}, {bad_frame}, mask, {.25}, 2.0);
ASSERT_FALSE(rejected.HasValue());
EXPECT_EQ(rejected.GetStatus().Category(), fesa::FailureCategory::kSolver);
EXPECT_EQ(rejected.GetStatus().Diagnostics()[0].code,
"invalid-eigen-mass-space");
EXPECT_EQ(prior.Value().whitened_operator.Values(), prior_values);
}
}
const auto rerun = fesa::eigen_internal::BuildEigenReduction(
matrix, {mass}, {frame}, mask, {.25}, 2.0);
ASSERT_TRUE(rerun.HasValue());
EXPECT_EQ(rerun.Value().whitened_operator.Values(), prior_values);
}
TEST(EigenReduction, RejectsPositiveButUnresolvableMassWithoutDeletingDofs) {
const double p = 1e-8;
const std::array<double, 3> d{2 * p / (1 + p * p), 0,
@@ -215,3 +182,70 @@ TEST(EigenReduction, HandlesTwoFreeRotationsAndRejectsEmptyFiniteSpace) {
EXPECT_EQ(empty.GetStatus().Diagnostics()[0].code, "empty-eigenproblem");
}
}
TEST(EigenReduction, PureMassWhiteningRetainsInclusiveInfinityConditionGate) {
const fesa::ShellNodeInitialFrame frame{0U, {0, 0, 1}, {1, 0, 0}, {0, 1, 0}};
for (const double diagonal : {0x1p-31, 0x1p-32, 0x1p-33}) {
auto mass = NodalMass(frame.director);
mass(0, 0) = 1.0;
mass(1, 1) = diagonal;
fesa::Matrix stiffness{6U, 6U};
stiffness(0, 0) = 3.0;
stiffness(1, 1) = 3.0 * diagonal;
const auto reduced = fesa::eigen_internal::BuildEigenReduction(
Sparse(stiffness), {mass}, {frame}, {0, 0, 1, 1, 1, 1}, {0.25}, 1.0);
EXPECT_EQ(reduced.HasValue(), diagonal >= 0x1p-32);
if (reduced.HasValue()) {
EXPECT_DOUBLE_EQ(reduced.Value().nodes[0].record.rcond_inf, diagonal);
fesa::Vector one{2U, 1.0};
const auto applied = reduced.Value().whitened_operator.Multiply(one);
EXPECT_NEAR(applied[0], 3.0, 1e-12);
EXPECT_NEAR(applied[1], 3.0, 1e-12);
} else {
EXPECT_EQ(reduced.GetStatus().Diagnostics()[0].code,
"eigen-mass-conditioning");
}
}
}
TEST(EigenReduction, PureOffDiagonalWhiteningMatchesAnalyticGramAndPencil) {
const fesa::ShellNodeInitialFrame frame{0U, {0, 0, 1}, {1, 0, 0}, {0, 1, 0}};
auto mass = NodalMass(frame.director);
mass(0, 1) = mass(1, 0) = 1.0;
fesa::Matrix stiffness{6U, 6U};
stiffness(0, 0) = stiffness(1, 1) = 6.0;
stiffness(0, 1) = stiffness(1, 0) = 4.0;
// For M=[[2,1],[1,2]], physical whitening columns are analytically
// (1/sqrt(2),0) and (-1/sqrt(6),2/sqrt(6)). No eigensolver is invoked.
for (const double length : {0.25, 3.0, 16.0}) {
const auto reduced = fesa::eigen_internal::BuildEigenReduction(
Sparse(stiffness), {mass}, {frame}, {0, 0, 1, 1, 1, 1}, {0.25}, length);
ASSERT_TRUE(reduced.HasValue());
fesa::Matrix canonical{2U, 2U};
canonical(0, 0) = canonical(1, 1) = 1.0;
std::array<fesa::Vector, 2> physical{fesa::Vector{6U}, fesa::Vector{6U}};
for (std::size_t column = 0; column < 2U; ++column) {
physical[column] = fesa::eigen_internal::RestoreScaledMode(
reduced.Value(), canonical, column);
for (std::size_t i = 0; i < 3U; ++i) physical[column][i] *= length;
}
EXPECT_NEAR(physical[0][0], 1.0 / std::sqrt(2.0), 1e-12);
EXPECT_NEAR(physical[0][1], 0.0, 1e-12);
EXPECT_NEAR(physical[1][0], -1.0 / std::sqrt(6.0), 1e-12);
EXPECT_NEAR(physical[1][1], 2.0 / std::sqrt(6.0), 1e-12);
const double expected[2][2]{{3.0, 1.0 / std::sqrt(3.0)},
{1.0 / std::sqrt(3.0), 7.0 / 3.0}};
for (std::size_t column = 0; column < 2U; ++column) {
fesa::Vector coordinate{2U};
coordinate[column] = 1.0;
const auto action = reduced.Value().whitened_operator.Multiply(coordinate);
const auto mq = mass.Multiply(physical[column]);
const auto kq = stiffness.Multiply(physical[column]);
for (std::size_t row = 0; row < 2U; ++row) {
EXPECT_NEAR(physical[row].Dot(mq), row == column ? 1.0 : 0.0, 1e-12);
EXPECT_NEAR(action[row], expected[row][column], 1e-12);
EXPECT_NEAR(action[row], physical[row].Dot(kq), 1e-12);
}
}
}
}