Files
FESADev/tests/reference/shell_surface_pressure_reference_test.cpp
T

262 lines
10 KiB
C++

#include <gtest/gtest.h>
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <optional>
#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
namespace {
fesa::test::NonlinearReferenceCase MakePressureCase() {
const std::filesystem::path root{FESA_TEST_SOURCE_DIR};
const std::filesystem::path directory =
root / "reference" / "shellNL-meshfine-pressure";
return {"shellnl-meshfine-pressure-s4-follower",
directory / "shellNL-meshfine-pressure.inp",
directory / "shellNL-meshfine-pressure displacements.csv",
directory / "shellNL-meshfine-pressure reactions.csv",
directory / "shellNL-meshfine-pressure stresses.csv",
std::filesystem::path{FESA_TEST_BINARY_DIR} / "reference" /
"shell-surface-pressure-load",
fesa::test::NonlinearReferenceArtifactDisposition::kRequired,
fesa::test::NonlinearReferenceArtifactDisposition::kRequired,
fesa::test::NonlinearReferenceArtifactDisposition::kReviewOnly,
fesa::test::NonlinearReferenceArtifactDisposition::kExcluded};
}
std::optional<std::filesystem::path> FindCachedResults(
const fesa::test::NonlinearReferenceCase& reference_case) {
const std::filesystem::path solver_library =
std::filesystem::path{FESA_TEST_BINARY_DIR} / "src" / "fesa" / "Debug" /
"fesa_solver.lib";
std::error_code error;
const auto solver_time =
std::filesystem::last_write_time(solver_library, error);
if (error ||
!std::filesystem::is_directory(reference_case.execution_directory)) {
return std::nullopt;
}
std::vector<std::filesystem::path> candidates;
for (const auto& entry : std::filesystem::recursive_directory_iterator(
reference_case.execution_directory, error)) {
if (error) {
return std::nullopt;
}
if (entry.path().filename() == "shellNL-meshfine-pressure_results.h5" &&
std::filesystem::is_regular_file(entry.path()) &&
std::filesystem::last_write_time(entry.path(), error) >= solver_time &&
!error) {
candidates.push_back(entry.path());
}
}
std::sort(candidates.begin(), candidates.end());
return candidates.empty()
? std::nullopt
: std::optional<std::filesystem::path>{candidates.back()};
}
const fesa::test::NonlinearReferenceComparisonReport& RunPressureComparison() {
static const auto report = [] {
const auto reference_case = MakePressureCase();
const auto cached = FindCachedResults(reference_case);
if (cached.has_value()) {
const auto comparison =
fesa::test::CompareHdf5ToReferenceCsv(reference_case, *cached);
if (comparison.HasValue()) {
return comparison.Value();
}
}
const auto copied =
fesa::test::CopyInputToExecutionDirectory(reference_case);
if (!copied.HasValue()) {
throw std::runtime_error{"Unable to copy pressure reference input."};
}
const auto generated = fesa::test::RunFesaApplicationOnCopiedInput(
reference_case, copied.Value());
if (!generated.HasValue()) {
throw std::runtime_error{"Unable to run pressure reference input."};
}
const auto comparison = fesa::test::CompareHdf5ToReferenceCsv(
reference_case, generated.Value().generated_results_hdf5);
if (!comparison.HasValue()) {
throw std::runtime_error{"Unable to compare pressure reference output."};
}
return comparison.Value();
}();
return report;
}
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;
});
if (found == report.families.end()) {
throw std::runtime_error{"Required comparison family is absent."};
}
return *found;
}
fesa::test::ReferenceToleranceFamilyDecision MakeTolerance(const bool passed) {
fesa::test::ReferenceToleranceFamilyDecision result{};
result.passed = passed;
return result;
}
} // namespace
TEST(ShellSurfacePressureReference,
AuditsExactRequiredArtifactsAndIgnoresOptionalReviewArtifacts) {
const auto artifacts = fesa::test::ArtifactCheck(MakePressureCase());
ASSERT_TRUE(artifacts.HasValue());
EXPECT_EQ(MakePressureCase().reaction_artifact_disposition,
fesa::test::NonlinearReferenceArtifactDisposition::kReviewOnly);
EXPECT_EQ(MakePressureCase().stress_artifact_disposition,
fesa::test::NonlinearReferenceArtifactDisposition::kExcluded);
ASSERT_EQ(artifacts.Value().size(), 2U);
EXPECT_EQ(artifacts.Value()[0U].byte_size, 56665U);
EXPECT_EQ(artifacts.Value()[0U].sha256,
"53718CCC9B41C23FCC1B509A51D66F1347E87ACA43A74F1D6CEC4D2085F4B90E");
EXPECT_EQ(artifacts.Value()[1U].byte_size, 111182U);
EXPECT_EQ(artifacts.Value()[1U].sha256,
"2FE53F98EAC9F04779100721C3EDF8049EB1DBFE733EBCBD819AEEED03B81198");
}
TEST(ShellSurfacePressureReference,
SelectsUniqueSchemaOneFactorOneAcceptedFrame) {
const auto& report = RunPressureComparison();
EXPECT_TRUE(report.blocking_passed);
EXPECT_EQ(report.case_id, "shellnl-meshfine-pressure-s4-follower");
}
TEST(ShellSurfacePressureReference,
Projects729WideRowsTo2187LogicalRowsPerFamily) {
const auto& report = RunPressureComparison();
EXPECT_EQ(FindFamily(report, "pressure_nodal_translation_blocking").row_count,
2187U);
EXPECT_EQ(FindFamily(report, "pressure_nodal_rotation_warning").row_count,
2187U);
}
TEST(ShellSurfacePressureReference,
MatchesByInstanceNodeLabelAndComponentNotRowOrder) {
const auto& report = RunPressureComparison();
const auto& translation =
FindFamily(report, "pressure_nodal_translation_blocking");
EXPECT_EQ(translation.components,
(std::vector<std::string>{"U1", "U2", "U3"}));
EXPECT_EQ(translation.tolerance.rows.size(), 2187U);
}
TEST(
ShellSurfacePressureReference,
RejectsSchemaIdentityCardinalityMissingExtraDuplicateAndNonfiniteBeforeTolerance) {
const auto& report = RunPressureComparison();
const std::filesystem::path invalid_hdf5 =
report.generated_results_hdf5.parent_path() / "invalid-pressure.h5";
{
std::ofstream stream{invalid_hdf5, std::ios::binary | std::ios::trunc};
stream << "not an HDF5 file";
}
EXPECT_FALSE(
fesa::test::CompareHdf5ToReferenceCsv(MakePressureCase(), invalid_hdf5)
.HasValue());
fesa::test::NonlinearReferenceComparisonReport precheck_report{};
precheck_report.blocking_passed = true;
precheck_report.precheck_findings = {
{"pressure_nodal_translation_blocking",
fesa::test::NonlinearReferenceDisposition::kBlocking,
"missing-extra-duplicate-or-nonfinite",
{}}};
EXPECT_EQ(fesa::test::ClassifyReferenceComparison(precheck_report),
fesa::test::NonlinearReferenceClassification::kFailed);
}
TEST(ShellSurfacePressureReference,
ClassifiesTranslationBlockingAndRotationWarningOnly) {
fesa::test::NonlinearReferenceComparisonReport report{};
report.blocking_passed = true;
report.families = {{"pressure_nodal_translation_blocking",
{"U1", "U2", "U3"},
fesa::test::NonlinearReferenceDisposition::kBlocking,
2187U,
MakeTolerance(true)},
{"pressure_nodal_rotation_warning",
{"UR1", "UR2", "UR3"},
fesa::test::NonlinearReferenceDisposition::kWarningOnly,
2187U,
MakeTolerance(false)}};
EXPECT_EQ(
fesa::test::ClassifyReferenceComparison(report),
fesa::test::NonlinearReferenceClassification::kWarningOnlyDifferences);
}
TEST(ShellSurfacePressureReference,
ExcludesReactionAndStressFamiliesFromVerdict) {
const auto& report = RunPressureComparison();
EXPECT_EQ(report.families.size(), 2U);
EXPECT_EQ(
FindFamily(report, "pressure_nodal_translation_blocking").disposition,
fesa::test::NonlinearReferenceDisposition::kBlocking);
EXPECT_EQ(FindFamily(report, "pressure_nodal_rotation_warning").disposition,
fesa::test::NonlinearReferenceDisposition::kWarningOnly);
}
TEST(ShellSurfacePressureReference,
UsesUnchangedCommonScaleNearZeroRelativeAndRmsPolicy) {
const auto& translation = FindFamily(RunPressureComparison(),
"pressure_nodal_translation_blocking");
EXPECT_EQ(translation.tolerance.near_zero_band,
fesa::test::ReferenceTolerancePolicy::kNearZeroRatio *
translation.tolerance.reference_scale);
EXPECT_LE(translation.tolerance.relative_rms,
fesa::test::ReferenceTolerancePolicy::kRelativeRmsTolerance);
EXPECT_TRUE(translation.tolerance.rms_passed);
}
TEST(ShellSurfacePressureReference, WritesDeterministicComparisonReport) {
const auto& first = RunPressureComparison();
const auto comparison = fesa::test::CompareHdf5ToReferenceCsv(
MakePressureCase(), first.generated_results_hdf5);
ASSERT_TRUE(comparison.HasValue());
ASSERT_EQ(first.families.size(), comparison.Value().families.size());
for (std::size_t index = 0U; index < first.families.size(); ++index) {
const auto& expected = first.families[index];
const auto& actual = comparison.Value().families[index];
EXPECT_EQ(actual.family_identity, expected.family_identity);
EXPECT_EQ(actual.components, expected.components);
EXPECT_EQ(actual.disposition, expected.disposition);
EXPECT_EQ(actual.row_count, expected.row_count);
EXPECT_DOUBLE_EQ(actual.tolerance.reference_scale,
expected.tolerance.reference_scale);
EXPECT_DOUBLE_EQ(actual.tolerance.relative_rms,
expected.tolerance.relative_rms);
EXPECT_EQ(actual.tolerance.worst_row, expected.tolerance.worst_row);
}
}