fix(elements): reject rounded loss of Hex8 bulk stiffness

This commit is contained in:
KOKO\Mimi
2026-10-09 01:18:20 +09:00
parent 9088af7b32
commit 7fadd48e1b
3 changed files with 234 additions and 0 deletions
@@ -21,6 +21,7 @@
#include "fesa/assembly/parallel_for.h"
#include "fesa/assembly/sparse_assembler.h"
#include "fesa/elements/element.h"
#include "fesa/elements/hex8_element.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/math/vector3.h"
#include "fesa/model/domain.h"
@@ -1149,3 +1150,50 @@ TEST(ResultRecovery, InvalidLaterShellLeavesEntirePriorStateUnchanged) {
EXPECT_EQ(state.Equilibrium(), prior_equilibrium);
EXPECT_EQ(state.VerificationMetrics(), prior_metrics);
}
TEST(ResultRecovery, RealSolidPayloadFailsClosedAndPreservesLegacyState) {
const auto fixture = MakeFixture();
const auto input = MakeAxialEquilibriumState(fixture);
auto state = fesa::AnalysisState::Create(*fixture.dofs, {"prior-step", 9U});
state.Displacement()[0] = 37.0;
state.InternalForce()[0] = 19.0;
const auto prior_bundle = MakeFakeBeamBundle(*fixture.domain, 0U, 11.0);
state.EndpointResults() =
std::get<fesa::BeamElementResultRows>(prior_bundle.payload).endpoint_rows;
fesa::Hex8Element::Coordinates x{{{-1, -1, -1},
{1, -1, -1},
{1, 1, -1},
{-1, 1, -1},
{-1, -1, 1},
{1, -1, 1},
{1, 1, 1},
{-1, 1, 1}}};
const auto identity = fixture.domain->Elements()[0U].SourceId();
fesa::ElementDofLayout layout{
identity,
{0, 1, 2, 3, 4, 5, 6, 7},
{fesa::DofComponent::kUx, fesa::DofComponent::kUy,
fesa::DofComponent::kUz}};
const fesa::LinearElasticMaterial material{"solid", 120.0, .25, {}};
auto solid = fesa::Hex8Element::Create(0U, layout, x, material);
ASSERT_TRUE(solid.HasValue());
auto recovered = solid.Value().Recover(fesa::Vector{24U});
ASSERT_TRUE(recovered.HasValue());
ASSERT_EQ(std::get<fesa::SolidElementResultRows>(recovered.Value().payload)
.rows.size(),
8U);
// A legacy six-DOF beam scatter forwards the real solid payload to the
// generic visitor; it must reject the incompatible payload before commit.
FakeRecoveryElement runtime{MakeRuntimeLayout(*fixture.domain, 0U),
std::move(recovered.Value())};
const fesa::Status status = fesa::ResultRecovery::Recover(
*fixture.model, {std::cref(runtime)}, *fixture.dofs, *fixture.stiffness,
input.Displacement(), input.ExternalForce(), state);
ExpectStatusCode(status, "unsupported-solid-result-recovery");
EXPECT_DOUBLE_EQ(state.Displacement()[0], 37.0);
EXPECT_DOUBLE_EQ(state.InternalForce()[0], 19.0);
ASSERT_EQ(state.EndpointResults().size(), 2U);
EXPECT_DOUBLE_EQ(state.EndpointResults()[0].section_resultant[0], 11.0);
EXPECT_EQ(state.Identity().step_name, "prior-step");
EXPECT_EQ(state.Identity().frame_index, 9U);
}