Add MITC4 drilling diagnostics output and comparison contract

This commit is contained in:
KOKO\Mimi
2026-10-20 09:45:58 +09:00
parent 3d5c296651
commit a73d3fc9ca
31 changed files with 1175 additions and 45 deletions
@@ -19,6 +19,10 @@
#include "fesa/model/domain.h"
#include "io/hdf5/eigen_hdf5_self_check.h"
#include "io/hdf5/mitc4_drilling_diagnostics.h"
#include "mitc4_fault_test_helpers.h"
namespace {
fesa::ModelDefinition Definition(const std::filesystem::path& source) {
@@ -81,6 +85,10 @@ TEST(EigenHdf5ResultsWriter, WritesModalSchemaAndRejectsTamperedState) {
const hid_t file =
H5Fopen(path.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
ASSERT_GE(file, 0);
const auto drilling = fesa::hdf5_internal::ReadDrillingDiagnostics(file);
ASSERT_TRUE(drilling);
EXPECT_EQ(drilling->formulation, fesa::Mitc4DrillingModeName(*state.drilling_mode));
EXPECT_TRUE(drilling->frames.empty());
EXPECT_GT(H5Lexists(file, "/steps/Step-1/eigen/modes", H5P_DEFAULT), 0);
const hid_t controls =
H5Dopen2(file, "/steps/Step-1/eigen/backend/base_fpm", H5P_DEFAULT);
@@ -105,7 +113,8 @@ TEST(EigenHdf5ResultsWriter, WritesModalSchemaAndRejectsTamperedState) {
[](auto& candidate) { candidate.constraint_mask[0] = 1U; },
[](auto& candidate) { candidate.nodal_reduction[0].rcond_inf = 1e-15; },
[](auto& candidate) { candidate.backend.base_fpm[5U] = 1; },
[](auto& candidate) { candidate.eta_gram = 1e-5; }};
[](auto& candidate) { candidate.eta_gram = 1e-5; },
[](auto& candidate) { candidate.drilling_mode.reset(); }};
for (const auto& mutate : mutations) {
auto candidate = state;
mutate(candidate);
@@ -133,6 +142,27 @@ TEST(EigenHdf5ResultsWriter, WritesModalSchemaAndRejectsTamperedState) {
locked_final.close();
ASSERT_TRUE(
fesa::hdf5_internal::SelfCheckEigenFile(path, domain, state, {}).IsOk());
for (const auto mode : {fesa::Mitc4DrillingMode::kDirectSpring,
fesa::Mitc4DrillingMode::kLegacyCorotatedTwist}) {
state.drilling_mode = mode;
ASSERT_TRUE(writer.Write(path, domain, state, {}).IsOk());
fesa::hdf5_internal::Hdf5Handle stored_file{
H5Fopen(path.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT), H5Fclose};
const auto stored = fesa::hdf5_internal::ReadDrillingDiagnostics(stored_file.Get());
ASSERT_TRUE(stored);
EXPECT_EQ(stored->formulation, fesa::Mitc4DrillingModeName(mode));
EXPECT_TRUE(stored->frames.empty());
}
{
fesa::hdf5_internal::Hdf5Handle stored_file{
H5Fopen(path.string().c_str(), H5F_ACC_RDWR, H5P_DEFAULT), H5Fclose};
fesa::hdf5_internal::Hdf5Handle metadata{
H5Gopen2(stored_file.Get(), "/metadata", H5P_DEFAULT), H5Gclose};
ASSERT_GE(H5Adelete(metadata.Get(), "mitc4_drilling_formulation"), 0);
ASSERT_GE(H5Adelete(metadata.Get(), "drilling_diagnostics_version"), 0);
}
EXPECT_FALSE(fesa::hdf5_internal::SelfCheckEigenFile(path, domain, state, {}).IsOk());
ASSERT_TRUE(writer.Write(path, domain, state, {}).IsOk());
const hid_t corrupt_file =
H5Fopen(path.string().c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
ASSERT_GE(corrupt_file, 0);
@@ -153,3 +183,22 @@ TEST(EigenHdf5ResultsWriter, WritesModalSchemaAndRejectsTamperedState) {
}
} // namespace
TEST(EigenHdf5ResultsWriter, DrillingLifecycleFaultsPreserveFinalBytes) {
const auto path = std::filesystem::temp_directory_path() / "fesa-eigen-drilling-fault.h5";
auto domain = fesa::Domain::Create(Definition(path.parent_path() / "shell.inp"));
ASSERT_TRUE(domain.HasValue());
auto state = fesa::LinearFrequencyProcedureExecutor{}.Execute(domain.Value());
ASSERT_TRUE(state.HasValue());
for (const std::string fault : {"candidate-close", "reader-close", "markers"}) {
SCOPED_TRACE(fault);
std::ofstream(path, std::ios::binary | std::ios::trunc) << "prior";
EXPECT_FALSE(fesa::hdf5_internal::WriteEigenFile(path, domain.Value(), state.Value(), {},
fesa::test::Mitc4LifecycleFault(fault)).IsOk());
std::ifstream stream{path, std::ios::binary};
EXPECT_EQ((std::string{std::istreambuf_iterator<char>{stream}, {}}), "prior");
}
std::error_code ignored;
std::filesystem::remove(path, ignored);
}