Add MITC4 drilling diagnostics output and comparison contract
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user