perf(hex8): cache immutable paired linear Hessian

This commit is contained in:
KOKO\Mimi
2026-10-09 23:55:01 +09:00
parent 5435acdde3
commit e878379c30
6 changed files with 76 additions and 3 deletions
@@ -65,6 +65,26 @@ TEST(Hex8HighLow, NonlinearLowGradientProducesSecondOrderForce) {
1e-12 * strain * strain);
}
TEST(Hex8HighLow, VolumetricAndTransverseTermsRetainLowStrain) {
auto brick = UnitBrick(0, 0.25);
fesa::Vector high(24), low(24);
const double strain = std::ldexp(1.0, -60);
for (std::size_t a = 0; a < 8; ++a) high[3*a] = 1;
for (auto a : {1, 2, 5, 6}) low[3*a] = strain;
const auto linear = brick.EvaluateLinearHighLow(high, low);
const auto nonlinear = brick.EvaluateNonlinearHighLow(high, low);
ASSERT_TRUE(linear.HasValue()); ASSERT_TRUE(nonlinear.HasValue());
// Uniform axial strain: lambda=mu=4.8, each nodal face integral is 1/4.
EXPECT_NEAR(linear.Value().high.internal_force[3], 3.6*strain, 1e-13*strain);
EXPECT_NEAR(linear.Value().high.internal_force[7], 1.2*strain, 1e-13*strain);
EXPECT_NEAR(linear.Value().high.strain_energy, 7.2*strain*strain,
1e-13*strain*strain);
const auto quadratic = Subtract(
{nonlinear.Value().high.internal_force[3], nonlinear.Value().internal_force_low[3]},
{linear.Value().high.internal_force[3], linear.Value().internal_force_low[3]});
EXPECT_NEAR(quadratic.high, 5.4*strain*strain, 1e-12*strain*strain);
}
TEST(Hex8HighLow, RejectsInvalidLowShapeAndOverlappingComponents) {
auto brick = UnitBrick();
fesa::Vector high(24), low(24);
@@ -90,6 +110,30 @@ TEST(Hex8HighLow, LinearAndNonlinearZeroStateHaveSameFullHessian) {
}
}
TEST(Hex8HighLow, CachedLinearHessianIsIndependentOfTrialAndPriorFailure) {
auto brick = UnitBrick(0, 0.25);
fesa::Vector zero(24), high(24), low(24);
high[3] = 0.1;
low[3] = std::ldexp(1.0, -60);
const auto reference = brick.EvaluateLinearHighLow(zero, zero);
ASSERT_TRUE(reference.HasValue());
EXPECT_FALSE(brick.EvaluateLinearHighLow(high, fesa::Vector(23)).HasValue());
const auto strained = brick.EvaluateLinearHighLow(high, low);
ASSERT_TRUE(strained.HasValue());
for (std::size_t i = 0; i < 24; ++i)
for (std::size_t j = 0; j < 24; ++j) {
EXPECT_EQ(reference.Value().high.tangent(i,j), strained.Value().high.tangent(i,j));
EXPECT_EQ(reference.Value().tangent_low(i,j), strained.Value().tangent_low(i,j));
}
const auto nonlinear_zero = brick.EvaluateNonlinearHighLow(zero, zero);
ASSERT_TRUE(nonlinear_zero.HasValue());
for (std::size_t i = 0; i < 24; ++i)
for (std::size_t j = 0; j < 24; ++j) {
EXPECT_EQ(reference.Value().high.tangent(i,j), nonlinear_zero.Value().high.tangent(i,j));
EXPECT_EQ(reference.Value().tangent_low(i,j), nonlinear_zero.Value().tangent_low(i,j));
}
}
TEST(Hex8HighLow, CoupledBricksTransmitLowStrainAcrossSharedFace) {
const auto left = UnitBrick(), right = UnitBrick(1);
fesa::Vector high(24), left_low(24), right_low(24);