486 lines
21 KiB
C++
486 lines
21 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "nonlinear_static_reference_comparison.h"
|
|
|
|
#ifndef FESA_TEST_SOURCE_DIR
|
|
#error FESA_TEST_SOURCE_DIR must identify the repository root.
|
|
#endif
|
|
|
|
#ifndef FESA_TEST_BINARY_DIR
|
|
#error FESA_TEST_BINARY_DIR must identify the CMake binary root.
|
|
#endif
|
|
|
|
#include "io/hdf5/mitc4_drilling_diagnostics.h"
|
|
|
|
namespace {
|
|
|
|
std::string ReadBytes(const std::filesystem::path& path) {
|
|
std::ifstream stream{path, std::ios::binary};
|
|
if (!stream) {
|
|
throw std::runtime_error{"Unable to read reference artifact."};
|
|
}
|
|
return {std::istreambuf_iterator<char>{stream},
|
|
std::istreambuf_iterator<char>{}};
|
|
}
|
|
|
|
fesa::test::NonlinearReferenceCase MakeCase(const std::string& case_id) {
|
|
const std::filesystem::path root{FESA_TEST_SOURCE_DIR};
|
|
const bool nonlinear = case_id == "shellnl-s4-final";
|
|
const std::filesystem::path directory =
|
|
root / "reference" / (nonlinear ? "shellNL" : "shell");
|
|
const std::string stem = nonlinear ? "shellNL" : "shell";
|
|
return {case_id,
|
|
directory / (stem + ".inp"),
|
|
directory / (stem + " displacements.csv"),
|
|
directory / (stem + " reactions.csv"),
|
|
directory / (stem + " stresses.csv"),
|
|
std::filesystem::path{FESA_TEST_BINARY_DIR} / "reference" /
|
|
"nonlinear-static-reference" / stem};
|
|
}
|
|
|
|
void ExpectSnapshotsEqual(
|
|
const std::vector<fesa::test::ArtifactSnapshot>& expected,
|
|
const std::vector<fesa::test::ArtifactSnapshot>& actual) {
|
|
ASSERT_EQ(actual.size(), expected.size());
|
|
for (std::size_t index = 0U; index < expected.size(); ++index) {
|
|
EXPECT_EQ(actual[index].path, expected[index].path);
|
|
EXPECT_EQ(actual[index].sha256, expected[index].sha256);
|
|
EXPECT_EQ(actual[index].byte_size, expected[index].byte_size);
|
|
EXPECT_EQ(actual[index].last_write_time, expected[index].last_write_time);
|
|
}
|
|
}
|
|
|
|
const fesa::test::NonlinearReferenceFamilyReport* FindFamily(
|
|
const fesa::test::NonlinearReferenceComparisonReport& report,
|
|
const std::string& identity) {
|
|
const auto found = std::find_if(
|
|
report.families.begin(), report.families.end(),
|
|
[&identity](const fesa::test::NonlinearReferenceFamilyReport& family) {
|
|
return family.family_identity == identity;
|
|
});
|
|
return found == report.families.end() ? nullptr : &*found;
|
|
}
|
|
|
|
void ExpectFamily(const fesa::test::NonlinearReferenceComparisonReport& report,
|
|
const std::string& identity, const std::size_t row_count,
|
|
const fesa::test::NonlinearReferenceDisposition disposition) {
|
|
const auto* family = FindFamily(report, identity);
|
|
ASSERT_NE(family, nullptr) << identity;
|
|
EXPECT_EQ(family->row_count, row_count);
|
|
EXPECT_EQ(family->disposition, disposition);
|
|
}
|
|
|
|
void ExpectBlockingFamiliesPassed(
|
|
const fesa::test::NonlinearReferenceComparisonReport& report) {
|
|
for (const auto& family : report.families) {
|
|
if (family.disposition !=
|
|
fesa::test::NonlinearReferenceDisposition::kBlocking) {
|
|
continue;
|
|
}
|
|
EXPECT_TRUE(family.tolerance.passed)
|
|
<< family.family_identity << " rows=" << family.row_count
|
|
<< " scale=" << family.tolerance.reference_scale
|
|
<< " max_abs=" << family.tolerance.maximum_absolute_error
|
|
<< " rms=" << family.tolerance.relative_rms
|
|
<< " diagnostic=" << family.tolerance.diagnostic_code;
|
|
}
|
|
}
|
|
|
|
void ExpectInternalFamiliesPassed(
|
|
const fesa::test::NonlinearReferenceComparisonReport& report) {
|
|
for (const auto& family : report.internal_equilibrium) {
|
|
EXPECT_TRUE(family.passed)
|
|
<< family.family_identity << " rows=" << family.row_count
|
|
<< " numerator=" << family.numerator << " scale=" << family.scale
|
|
<< " metric="
|
|
<< (family.normalized_metric.has_value() ? *family.normalized_metric
|
|
: -1.0)
|
|
<< " diagnostic=" << family.diagnostic_code;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// NSNR-REF-001 and NSNR-REF-018.
|
|
TEST(NonlinearStaticReference, AuditsEveryDeclaredArtifactBeforeExecution) {
|
|
const auto nonlinear = MakeCase("shellnl-s4-final");
|
|
const auto linear = MakeCase("shell-s4-linear-limit");
|
|
|
|
const auto nonlinear_artifacts = fesa::test::ArtifactCheck(nonlinear);
|
|
ASSERT_TRUE(nonlinear_artifacts.HasValue());
|
|
ASSERT_EQ(nonlinear_artifacts.Value().size(), 4U);
|
|
EXPECT_EQ(nonlinear_artifacts.Value()[0U].sha256,
|
|
"4BF3ECB0F7F1E8462C573E993DBC97AC5D920C91CF6F23AD4C6891D77FFBADA2");
|
|
EXPECT_EQ(nonlinear_artifacts.Value()[3U].sha256,
|
|
"5500E5114B1C44887528E30CEC054A1EB0F68BE3F4B8CE81FA59FC74A881483F");
|
|
|
|
const auto linear_artifacts = fesa::test::ArtifactCheck(linear);
|
|
ASSERT_TRUE(linear_artifacts.HasValue());
|
|
ASSERT_EQ(linear_artifacts.Value().size(), 4U);
|
|
EXPECT_EQ(linear_artifacts.Value()[0U].sha256,
|
|
"4005851E1AB22FD3A16AC17A8D5DA3E051233F69F37419079F3553AD134ECFCF");
|
|
EXPECT_EQ(linear_artifacts.Value()[3U].sha256,
|
|
"2B75A5B0DEB045A283C9BB7E9B1153B79F7537FF487214BF4A23316B59B660A7");
|
|
}
|
|
|
|
// NSNR-REF-002 through NSNR-REF-018.
|
|
TEST(NonlinearStaticReference,
|
|
CopiesRunsComparesAndPreservesTheReadOnlyReferenceInventory) {
|
|
for (const std::string case_id :
|
|
{"shellnl-s4-final", "shell-s4-linear-limit"}) {
|
|
const auto reference_case = MakeCase(case_id);
|
|
const auto before = fesa::test::ArtifactCheck(reference_case);
|
|
ASSERT_TRUE(before.HasValue());
|
|
|
|
const auto copied =
|
|
fesa::test::CopyInputToExecutionDirectory(reference_case);
|
|
ASSERT_TRUE(copied.HasValue());
|
|
EXPECT_NE(copied.Value().parent_path(),
|
|
reference_case.original_input_path.parent_path());
|
|
EXPECT_EQ(ReadBytes(copied.Value()),
|
|
ReadBytes(reference_case.original_input_path));
|
|
|
|
const auto generated = fesa::test::RunFesaApplicationOnCopiedInput(
|
|
reference_case, copied.Value());
|
|
ASSERT_TRUE(generated.HasValue());
|
|
EXPECT_EQ(generated.Value().copied_input_path, copied.Value());
|
|
EXPECT_TRUE(std::filesystem::is_regular_file(
|
|
generated.Value().generated_results_hdf5));
|
|
EXPECT_TRUE(
|
|
std::filesystem::is_regular_file(generated.Value().generated_log_path));
|
|
EXPECT_EQ(generated.Value().generated_results_hdf5.filename(),
|
|
case_id == "shellnl-s4-final" ? "shellNL_results.h5"
|
|
: "shell_results.h5");
|
|
EXPECT_EQ(
|
|
generated.Value().generated_log_path.filename(),
|
|
case_id == "shellnl-s4-final" ? "shellNL_log.txt" : "shell_log.txt");
|
|
|
|
const auto comparison = fesa::test::CompareHdf5ToReferenceCsv(
|
|
reference_case, generated.Value().generated_results_hdf5);
|
|
ASSERT_TRUE(comparison.HasValue());
|
|
ExpectBlockingFamiliesPassed(comparison.Value());
|
|
if (case_id == "shellnl-s4-final") {
|
|
ExpectInternalFamiliesPassed(comparison.Value());
|
|
}
|
|
EXPECT_TRUE(comparison.Value().blocking_passed);
|
|
EXPECT_EQ(comparison.Value().copied_input_path, copied.Value());
|
|
EXPECT_EQ(comparison.Value().generated_log_path,
|
|
generated.Value().generated_log_path);
|
|
|
|
ExpectFamily(comparison.Value(),
|
|
case_id == "shellnl-s4-final" ? "coarse.translation"
|
|
: "linear.translation",
|
|
147U, fesa::test::NonlinearReferenceDisposition::kBlocking);
|
|
ExpectFamily(
|
|
comparison.Value(),
|
|
case_id == "shellnl-s4-final" ? "coarse.rotation" : "linear.rotation",
|
|
147U, fesa::test::NonlinearReferenceDisposition::kWarningOnly);
|
|
if (case_id == "shellnl-s4-final") {
|
|
ExpectFamily(comparison.Value(), "coarse.local_reaction_inplane_force",
|
|
48U, fesa::test::NonlinearReferenceDisposition::kReviewOnly);
|
|
ExpectFamily(comparison.Value(), "coarse.local_reaction_transverse_force",
|
|
24U, fesa::test::NonlinearReferenceDisposition::kReviewOnly);
|
|
ExpectFamily(comparison.Value(), "coarse.local_reaction_physical_moment",
|
|
48U, fesa::test::NonlinearReferenceDisposition::kReviewOnly);
|
|
EXPECT_EQ(FindFamily(comparison.Value(), "RM3"), nullptr);
|
|
}
|
|
|
|
const auto after = fesa::test::ArtifactCheck(reference_case);
|
|
ASSERT_TRUE(after.HasValue());
|
|
ExpectSnapshotsEqual(before.Value(), after.Value());
|
|
}
|
|
}
|
|
|
|
// NSNR-REF-009 and NSNR-REF-017.
|
|
TEST(NonlinearStaticReference,
|
|
RejectsInvalidHdf5BeforeToleranceAndKeepsWarningOnlyClassification) {
|
|
const auto reference_case = MakeCase("shellnl-s4-final");
|
|
const auto copied = fesa::test::CopyInputToExecutionDirectory(reference_case);
|
|
ASSERT_TRUE(copied.HasValue());
|
|
const auto generated = fesa::test::RunFesaApplicationOnCopiedInput(
|
|
reference_case, copied.Value());
|
|
ASSERT_TRUE(generated.HasValue());
|
|
|
|
const auto invalid_hdf5 = copied.Value().parent_path() / "invalid.h5";
|
|
std::ofstream stream{invalid_hdf5, std::ios::binary | std::ios::trunc};
|
|
stream << "not an HDF5 file";
|
|
stream.close();
|
|
EXPECT_FALSE(
|
|
fesa::test::CompareHdf5ToReferenceCsv(reference_case, invalid_hdf5)
|
|
.HasValue());
|
|
|
|
fesa::test::NonlinearReferenceComparisonReport report{};
|
|
report.blocking_passed = true;
|
|
report.families.push_back(
|
|
{"nonlinear.rotation",
|
|
{"UR1", "UR2", "UR3"},
|
|
fesa::test::NonlinearReferenceDisposition::kWarningOnly,
|
|
147U,
|
|
{"nonlinear.rotation",
|
|
{"UR1", "UR2", "UR3"},
|
|
false,
|
|
1.0,
|
|
0.01,
|
|
0U,
|
|
0.02,
|
|
0.02,
|
|
0U,
|
|
false,
|
|
false,
|
|
"relative-rms-exceeded",
|
|
{},
|
|
147U}});
|
|
EXPECT_EQ(
|
|
fesa::test::ClassifyReferenceComparison(report),
|
|
fesa::test::NonlinearReferenceClassification::kWarningOnlyDifferences);
|
|
}
|
|
|
|
// NSNR-REF-020.
|
|
TEST(NonlinearStaticReference, AuditsRetainedStaticArtifacts) {
|
|
const std::array<std::string, 2> case_ids{"shellnl-s4-final",
|
|
"shell-s4-linear-limit"};
|
|
for (const auto& case_id : case_ids) {
|
|
const auto reference_case = MakeCase(case_id);
|
|
const auto before = fesa::test::ArtifactCheck(reference_case);
|
|
ASSERT_TRUE(before.HasValue()) << case_id;
|
|
ASSERT_EQ(before.Value().size(), 4U) << case_id;
|
|
|
|
const auto copied =
|
|
fesa::test::CopyInputToExecutionDirectory(reference_case);
|
|
ASSERT_TRUE(copied.HasValue()) << case_id;
|
|
EXPECT_NE(copied.Value().parent_path(),
|
|
reference_case.original_input_path.parent_path());
|
|
const auto generated = fesa::test::RunFesaApplicationOnCopiedInput(
|
|
reference_case, copied.Value());
|
|
ASSERT_TRUE(generated.HasValue()) << case_id;
|
|
EXPECT_TRUE(std::filesystem::is_regular_file(
|
|
generated.Value().generated_results_hdf5));
|
|
|
|
const auto after = fesa::test::ArtifactCheck(reference_case);
|
|
ASSERT_TRUE(after.HasValue()) << case_id;
|
|
ExpectSnapshotsEqual(before.Value(), after.Value());
|
|
}
|
|
}
|
|
|
|
// NSNR-REF-021.
|
|
TEST(NonlinearStaticReference,
|
|
ReviewOnlyLocalReactionsCannotChangeBlockingVerdict) {
|
|
const auto reference_case = MakeCase("shellnl-s4-final");
|
|
const auto copied = fesa::test::CopyInputToExecutionDirectory(reference_case);
|
|
ASSERT_TRUE(copied.HasValue());
|
|
const auto generated = fesa::test::RunFesaApplicationOnCopiedInput(
|
|
reference_case, copied.Value());
|
|
ASSERT_TRUE(generated.HasValue());
|
|
const auto comparison = fesa::test::CompareHdf5ToReferenceCsv(
|
|
reference_case, generated.Value().generated_results_hdf5);
|
|
ASSERT_TRUE(comparison.HasValue());
|
|
|
|
ExpectFamily(comparison.Value(), "coarse.local_reaction_inplane_force", 48U,
|
|
fesa::test::NonlinearReferenceDisposition::kReviewOnly);
|
|
ExpectFamily(comparison.Value(), "coarse.local_reaction_transverse_force",
|
|
24U, fesa::test::NonlinearReferenceDisposition::kReviewOnly);
|
|
ExpectFamily(comparison.Value(), "coarse.local_reaction_physical_moment", 48U,
|
|
fesa::test::NonlinearReferenceDisposition::kReviewOnly);
|
|
EXPECT_TRUE(comparison.Value().external_blocking_passed);
|
|
}
|
|
|
|
// NSNR-REF-022.
|
|
TEST(NonlinearStaticReference, BlocksOnGlobalBalanceAndAcceptedFreeResidual) {
|
|
for (const std::string case_id : {"shellnl-s4-final"}) {
|
|
const auto reference_case = MakeCase(case_id);
|
|
const auto copied =
|
|
fesa::test::CopyInputToExecutionDirectory(reference_case);
|
|
ASSERT_TRUE(copied.HasValue()) << case_id;
|
|
const auto generated = fesa::test::RunFesaApplicationOnCopiedInput(
|
|
reference_case, copied.Value());
|
|
ASSERT_TRUE(generated.HasValue()) << case_id;
|
|
const auto comparison = fesa::test::CompareHdf5ToReferenceCsv(
|
|
reference_case, generated.Value().generated_results_hdf5);
|
|
ASSERT_TRUE(comparison.HasValue()) << case_id;
|
|
|
|
const std::string prefix = "coarse.";
|
|
const std::array<std::string, 4> identities{
|
|
prefix + "global_force_balance", prefix + "global_moment_balance",
|
|
prefix + "accepted_free_force_residual",
|
|
prefix + "accepted_free_moment_residual"};
|
|
for (const auto& identity : identities) {
|
|
const auto found = std::find_if(
|
|
comparison.Value().internal_equilibrium.begin(),
|
|
comparison.Value().internal_equilibrium.end(),
|
|
[&identity](
|
|
const fesa::test::NonlinearInternalEquilibriumReport& row) {
|
|
return row.family_identity == identity;
|
|
});
|
|
ASSERT_NE(found, comparison.Value().internal_equilibrium.end())
|
|
<< identity;
|
|
EXPECT_TRUE(found->passed)
|
|
<< identity << " numerator=" << found->numerator
|
|
<< " scale=" << found->scale << " metric="
|
|
<< (found->normalized_metric.has_value() ? *found->normalized_metric
|
|
: -1.0)
|
|
<< " diagnostic=" << found->diagnostic_code;
|
|
}
|
|
EXPECT_TRUE(comparison.Value().internal_blocking_passed);
|
|
const std::array<double, 3> zero_resultant{};
|
|
EXPECT_NE(comparison.Value().constrained_reaction_resultant.force,
|
|
zero_resultant);
|
|
}
|
|
}
|
|
|
|
// NSNR-REF-023.
|
|
TEST(
|
|
NonlinearStaticReference,
|
|
RejectsMissingExtraDuplicateAndNonfiniteRowsBeforeToleranceAndRecordsReviewOnlyFindings) {
|
|
fesa::test::NonlinearReferenceComparisonReport report{};
|
|
report.blocking_passed = true;
|
|
report.external_blocking_passed = true;
|
|
report.internal_blocking_passed = true;
|
|
report.precheck_findings = {
|
|
{"blocking",
|
|
fesa::test::NonlinearReferenceDisposition::kBlocking,
|
|
"missing-row",
|
|
{}},
|
|
{"warning",
|
|
fesa::test::NonlinearReferenceDisposition::kWarningOnly,
|
|
"duplicate-row",
|
|
{}},
|
|
{"review",
|
|
fesa::test::NonlinearReferenceDisposition::kReviewOnly,
|
|
"nonfinite-row",
|
|
{}}};
|
|
|
|
EXPECT_EQ(fesa::test::ClassifyReferenceComparison(report),
|
|
fesa::test::NonlinearReferenceClassification::kFailed);
|
|
EXPECT_EQ(report.precheck_findings[1U].diagnostic_code, "duplicate-row");
|
|
EXPECT_EQ(report.precheck_findings[2U].diagnostic_code, "nonfinite-row");
|
|
}
|
|
|
|
// NSNR-REF-024.
|
|
TEST(NonlinearStaticReference,
|
|
GlobalForceAndMomentBalanceUseApprovedInclusiveOneEighthGate) {
|
|
for (const std::string& identity :
|
|
{"fine.global_force_balance", "fine.global_moment_balance"}) {
|
|
const auto below =
|
|
fesa::test::EvaluateGlobalBalance(identity, 5.0e-9, 1.0, 0.0);
|
|
EXPECT_TRUE(below.passed) << identity;
|
|
ASSERT_TRUE(below.normalized_metric.has_value()) << identity;
|
|
EXPECT_EQ(*below.normalized_metric, 5.0e-9) << identity;
|
|
|
|
const auto boundary =
|
|
fesa::test::EvaluateGlobalBalance(identity, 1.0e-8, 1.0, 0.0);
|
|
EXPECT_TRUE(boundary.passed) << identity;
|
|
ASSERT_TRUE(boundary.normalized_metric.has_value()) << identity;
|
|
EXPECT_EQ(*boundary.normalized_metric, 1.0e-8) << identity;
|
|
|
|
const auto above = fesa::test::EvaluateGlobalBalance(
|
|
identity,
|
|
std::nextafter(1.0e-8, std::numeric_limits<double>::infinity()), 1.0,
|
|
0.0);
|
|
EXPECT_FALSE(above.passed) << identity;
|
|
ASSERT_TRUE(above.normalized_metric.has_value()) << identity;
|
|
EXPECT_GT(*above.normalized_metric, 1.0e-8) << identity;
|
|
}
|
|
}
|
|
|
|
// NSNR-REF-025.
|
|
TEST(NonlinearStaticReference,
|
|
GlobalForceAndMomentExactZeroScaleRequiresExactZeroBalance) {
|
|
for (const std::string& identity :
|
|
{"coarse.global_force_balance", "coarse.global_moment_balance"}) {
|
|
const auto exact_zero =
|
|
fesa::test::EvaluateGlobalBalance(identity, 0.0, 0.0, 0.0);
|
|
EXPECT_TRUE(exact_zero.passed) << identity;
|
|
EXPECT_EQ(exact_zero.scale, 0.0) << identity;
|
|
EXPECT_FALSE(exact_zero.normalized_metric.has_value()) << identity;
|
|
|
|
const auto nonzero =
|
|
fesa::test::EvaluateGlobalBalance(identity, 1.0e-300, 0.0, 0.0);
|
|
EXPECT_FALSE(nonzero.passed) << identity;
|
|
EXPECT_EQ(nonzero.scale, 0.0) << identity;
|
|
EXPECT_FALSE(nonzero.normalized_metric.has_value()) << identity;
|
|
}
|
|
}
|
|
|
|
|
|
TEST(NonlinearStaticReference, DrillingSyntheticMomentSelectsOnlyExistingInternalFamily) {
|
|
const auto reference_case = MakeCase("shellnl-s4-final");
|
|
const auto copied = fesa::test::CopyInputToExecutionDirectory(reference_case);
|
|
ASSERT_TRUE(copied.HasValue());
|
|
const auto generated = fesa::test::RunFesaApplicationOnCopiedInput(reference_case, copied.Value());
|
|
ASSERT_TRUE(generated.HasValue());
|
|
const auto& path = generated.Value().generated_results_hdf5;
|
|
const auto baseline = fesa::test::CompareHdf5ToReferenceCsv(reference_case, path);
|
|
ASSERT_TRUE(baseline.HasValue());
|
|
ASSERT_TRUE(baseline.Value().drilling_diagnostics);
|
|
const auto final_index = baseline.Value().drilling_diagnostics->frames.back().frame_index;
|
|
const std::string root = "/steps/Step-1/frames/" + std::to_string(final_index) + "/global/";
|
|
const auto moment_gate = [](const auto& report) -> const auto& {
|
|
const auto found = std::find_if(report.internal_equilibrium.begin(), report.internal_equilibrium.end(),
|
|
[](const auto& family) { return family.family_identity == "coarse.global_moment_balance"; });
|
|
if (found == report.internal_equilibrium.end()) throw std::runtime_error{"Missing existing moment family."};
|
|
return *found;
|
|
};
|
|
// This mutates only a generated working output into an algebra fixture.
|
|
// Its comparator verdict is not physics reference evidence.
|
|
for (const std::string mode : {"direct-initial-director-spring", "legacy-corotated-twist", "markerless"}) {
|
|
{
|
|
fesa::hdf5_internal::Hdf5Handle file{H5Fopen(path.string().c_str(), H5F_ACC_RDWR, H5P_DEFAULT), H5Fclose};
|
|
fesa::hdf5_internal::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);
|
|
if (mode != "markerless") {
|
|
fesa::hdf5_internal::WriteStringAttribute(metadata.Get(), "mitc4_drilling_formulation", mode);
|
|
fesa::hdf5_internal::WriteUint64Attribute(metadata.Get(), "drilling_diagnostics_version", 1U);
|
|
}
|
|
for (const auto& entry : std::vector<std::pair<std::string, std::vector<double>>>{
|
|
{"equilibrium", {0.,0.,0.,1e6,0.,0.}}, {"drilling_moment", {1e6,0.,0.}},
|
|
{"equilibrium_with_drilling", {0.,0.,0.,0.,0.,0.}}}) {
|
|
fesa::hdf5_internal::Hdf5Handle dataset{H5Dopen2(file.Get(), (root + entry.first).c_str(), H5P_DEFAULT), H5Dclose};
|
|
ASSERT_GE(H5Dwrite(dataset.Get(), H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
|
|
H5P_DEFAULT, entry.second.data()), 0);
|
|
}
|
|
}
|
|
const auto compared = fesa::test::CompareHdf5ToReferenceCsv(reference_case, path);
|
|
ASSERT_TRUE(compared.HasValue());
|
|
EXPECT_TRUE(compared.Value().external_blocking_passed);
|
|
EXPECT_EQ(compared.Value().families.size(), baseline.Value().families.size());
|
|
EXPECT_EQ(compared.Value().internal_equilibrium.size(), baseline.Value().internal_equilibrium.size());
|
|
const auto& gate = moment_gate(compared.Value());
|
|
EXPECT_EQ(gate.scale, moment_gate(baseline.Value()).scale);
|
|
EXPECT_EQ(gate.numerator, mode == "direct-initial-director-spring" ? 0.0 : 1e6);
|
|
EXPECT_EQ(gate.passed, mode == "direct-initial-director-spring");
|
|
EXPECT_EQ(compared.Value().drilling_diagnostics.has_value(), mode != "markerless");
|
|
}
|
|
// Restore new metadata, retain exact raw-minus-M algebra, and expose a real imbalance.
|
|
{
|
|
fesa::hdf5_internal::Hdf5Handle file{H5Fopen(path.string().c_str(), H5F_ACC_RDWR, H5P_DEFAULT), H5Fclose};
|
|
fesa::hdf5_internal::WriteDrillingMetadata(file.Get(), fesa::Mitc4DrillingMode::kDirectSpring);
|
|
for (const auto& entry : std::vector<std::pair<std::string, std::vector<double>>>{
|
|
{"equilibrium", {0.,0.,0.,2e6,0.,0.}}, {"equilibrium_with_drilling", {0.,0.,0.,1e6,0.,0.}}}) {
|
|
fesa::hdf5_internal::Hdf5Handle dataset{H5Dopen2(file.Get(), (root + entry.first).c_str(), H5P_DEFAULT), H5Dclose};
|
|
ASSERT_GE(H5Dwrite(dataset.Get(), H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
|
|
H5P_DEFAULT, entry.second.data()), 0);
|
|
}
|
|
}
|
|
const auto imbalanced = fesa::test::CompareHdf5ToReferenceCsv(reference_case, path);
|
|
ASSERT_TRUE(imbalanced.HasValue());
|
|
EXPECT_FALSE(moment_gate(imbalanced.Value()).passed);
|
|
EXPECT_EQ(moment_gate(imbalanced.Value()).scale, moment_gate(baseline.Value()).scale);
|
|
{
|
|
fesa::hdf5_internal::Hdf5Handle file{H5Fopen(path.string().c_str(), H5F_ACC_RDWR, H5P_DEFAULT), H5Fclose};
|
|
ASSERT_GE(H5Ldelete(file.Get(), "/steps/Step-1/frames/0/global/drilling_moment", H5P_DEFAULT), 0);
|
|
}
|
|
EXPECT_FALSE(fesa::test::CompareHdf5ToReferenceCsv(reference_case, path).HasValue());
|
|
}
|