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
@@ -13,6 +13,7 @@
#include <fstream>
#include <iterator>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <type_traits>
@@ -25,6 +26,11 @@
#include "fesa/model/domain.h"
#include "fesa/results/nonlinear_results_writer.h"
#include "io/hdf5/hdf5_raii.h"
#include "io/hdf5/hdf5_self_check.h"
#include "io/hdf5/mitc4_drilling_diagnostics.h"
#include "mitc4_fault_test_helpers.h"
namespace {
@@ -184,7 +190,8 @@ std::vector<fesa::ShellResultRow> MakeShellRows(const double seed) {
fesa::AcceptedFrameState MakeFrame(const fesa::NonlinearTrialState& committed,
const std::uint32_t frame_index,
const std::uint32_t increment_index,
const double load_factor) {
const double load_factor,
std::optional<fesa::Mitc4DrillingMode> mode = fesa::Mitc4DrillingMode::kDirectSpring) {
fesa::AcceptedFrameState frame{
frame_index,
increment_index,
@@ -223,10 +230,13 @@ fesa::AcceptedFrameState MakeFrame(const fesa::NonlinearTrialState& committed,
frame.reaction[offset + component] = frame.residual[offset + component];
}
}
if (mode) frame.mitc4_drilling = fesa::Mitc4DrillingEvidence{
*mode, {}, frame.global_equilibrium};
return frame;
}
Fixture MakeFixture(const std::filesystem::path& source) {
Fixture MakeFixture(const std::filesystem::path& source,
std::optional<fesa::Mitc4DrillingMode> mode = fesa::Mitc4DrillingMode::kDirectSpring) {
auto domain_result = fesa::Domain::Create(MakeDefinition(source));
if (!domain_result.HasValue()) {
throw std::runtime_error{"Unable to construct nonlinear HDF5 Domain."};
@@ -248,10 +258,10 @@ Fixture MakeFixture(const std::filesystem::path& source) {
}
fesa::NonlinearAnalysisState state = std::move(state_result.Value());
if (!state.CommitAcceptedFrame(
MakeFrame(state.CommittedState(), 0U, 1U, 0.5))
MakeFrame(state.CommittedState(), 0U, 1U, 0.5, mode))
.IsOk() ||
!state.CommitAcceptedFrame(
MakeFrame(state.CommittedState(), 1U, 2U, 1.0))
MakeFrame(state.CommittedState(), 1U, 2U, 1.0, mode))
.IsOk()) {
throw std::runtime_error{"Unable to commit nonlinear HDF5 test frames."};
}
@@ -615,3 +625,55 @@ TEST(NonlinearHdf5ResultsWriter, CandidateFinalizationFailurePreservesPriorFinal
EXPECT_EQ(status.Diagnostics()[0U].code, "hdf5-finalization-failure");
EXPECT_EQ(ReadBytes(output), sentinel);
}
TEST(NonlinearHdf5ResultsWriter, DrillingModesAndEveryAcceptedFrameRoundTrip) {
for (const auto mode : {fesa::Mitc4DrillingMode::kDirectSpring,
fesa::Mitc4DrillingMode::kLegacyCorotatedTwist}) {
TempDirectory directory{"drilling"};
auto fixture = MakeFixture(directory.Path() / "shell.inp", mode);
const auto path = directory.Path() / "results.h5";
ASSERT_TRUE(fesa::NonlinearHdf5ResultsWriter{}.Write(path, *fixture.domain, fixture.state, {}).IsOk());
{
const auto file = OpenFile(path);
const auto stored = fesa::hdf5_internal::ReadDrillingDiagnostics(file.Get());
ASSERT_TRUE(stored);
EXPECT_EQ(stored->formulation, fesa::Mitc4DrillingModeName(mode));
ASSERT_EQ(stored->frames.size(), fixture.state.AcceptedFrames().size());
EXPECT_EQ(stored->frames.front().frame_index, 0U);
EXPECT_EQ(stored->frames.back().raw_equilibrium, fixture.state.AcceptedFrames().back().global_equilibrium);
}
{
Hdf5Handle file{H5Fopen(path.string().c_str(), H5F_ACC_RDWR, H5P_DEFAULT), H5Fclose};
Hdf5Handle metadata{H5Gopen2(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::NonlinearSelfCheck(path, *fixture.domain, fixture.state, 0U).IsOk());
}
}
TEST(NonlinearHdf5ResultsWriter, DrillingMissingActualStatePreservesPriorFinal) {
TempDirectory directory{"drilling-missing"};
auto fixture = MakeFixture(directory.Path() / "shell.inp", std::nullopt);
const auto path = directory.Path() / "results.h5";
std::ofstream(path, std::ios::binary) << "prior";
EXPECT_FALSE(fesa::NonlinearHdf5ResultsWriter{}.Write(path, *fixture.domain, fixture.state, {}).IsOk());
const auto bytes = ReadBytes(path);
EXPECT_EQ(std::string(bytes.begin(), bytes.end()), "prior");
}
TEST(NonlinearHdf5ResultsWriter, DrillingLifecycleFaultsPreserveFinalBytes) {
TempDirectory directory{"drilling-faults"};
auto fixture = MakeFixture(directory.Path() / "shell.inp");
const auto path = directory.Path() / "results.h5";
for (const std::string fault : {"candidate-close", "reader-close", "markers", "datasets"}) {
SCOPED_TRACE(fault);
std::ofstream(path, std::ios::binary | std::ios::trunc) << "prior";
EXPECT_FALSE(fesa::hdf5_internal::WriteNonlinearFile(path, *fixture.domain, fixture.state, {},
fesa::test::Mitc4LifecycleFault(fault)).IsOk());
const auto bytes = ReadBytes(path);
EXPECT_EQ(std::string(bytes.begin(), bytes.end()), "prior");
}
}