#include "reference_tolerance_policy.h" #include #include #include #include #include #include #include namespace { using fesa::test::ReferenceToleranceBranch; using fesa::test::ReferenceToleranceFamily; using fesa::test::ReferenceTolerancePolicy; using fesa::test::ReferenceToleranceRow; ReferenceToleranceFamily MakeFamily(std::string identity, std::vector components, bool blocking, std::vector rows) { return {std::move(identity), std::move(components), blocking, std::move(rows)}; } TEST(ReferenceTolerancePolicy, AppliesRelativeAndNearZeroThresholdsAtTheirInclusiveBoundaries) { const auto evaluation = ReferenceTolerancePolicy::Evaluate({MakeFamily( "displacement-translation", {"UX", "UY", "UZ"}, true, {{105.0, 100.0}, {105.001, 100.0}, {2.0, 1.0}, {2.001, 1.0}})}); ASSERT_TRUE(evaluation.valid); ASSERT_EQ(evaluation.families.size(), 1U); const auto& family = evaluation.families.front(); EXPECT_EQ(family.row_count, 4U); EXPECT_DOUBLE_EQ(family.reference_scale, 100.0); EXPECT_DOUBLE_EQ(family.near_zero_band, 1.0); EXPECT_EQ(family.near_zero_count, 2U); EXPECT_FALSE(family.passed); ASSERT_EQ(family.rows.size(), 4U); EXPECT_EQ(family.rows[0U].branch, ReferenceToleranceBranch::kRelative); EXPECT_TRUE(family.rows[0U].relative_error_applicable); EXPECT_DOUBLE_EQ(family.rows[0U].relative_error, 0.05); EXPECT_DOUBLE_EQ(family.rows[0U].threshold, 5.0); EXPECT_TRUE(family.rows[0U].passed); EXPECT_FALSE(family.rows[1U].passed); EXPECT_EQ(family.rows[2U].branch, ReferenceToleranceBranch::kNearZero); EXPECT_FALSE(family.rows[2U].relative_error_applicable); EXPECT_DOUBLE_EQ(family.rows[2U].threshold, 1.0); EXPECT_TRUE(family.rows[2U].passed); EXPECT_FALSE(family.rows[3U].passed); } TEST(ReferenceTolerancePolicy, RejectsFamiliesWhoseScaleRelativeRmsExceedsOnePercent) { const auto evaluation = ReferenceTolerancePolicy::Evaluate({MakeFamily( "reaction-force", {"RF1", "RF2", "RF3"}, true, {{101.1, 100.0}, {101.1, 100.0}, {101.1, 100.0}, {101.1, 100.0}})}); ASSERT_TRUE(evaluation.valid); const auto& family = evaluation.families.front(); EXPECT_EQ(family.row_count, 4U); EXPECT_TRUE(std::all_of(family.rows.begin(), family.rows.end(), [](const auto& row) { return row.passed; })); EXPECT_NEAR(family.relative_rms, 0.011, 1.0e-15); EXPECT_FALSE(family.rms_passed); EXPECT_FALSE(family.passed); EXPECT_FALSE(evaluation.passed); } TEST(ReferenceTolerancePolicy, FailsClosedForZeroScaleNonzeroErrorWithoutNonfiniteMetrics) { const auto evaluation = ReferenceTolerancePolicy::Evaluate( {MakeFamily("section-force", {"N"}, true, {{0.0, 0.0}, {1.0, 0.0}})}); ASSERT_TRUE(evaluation.valid); const auto& family = evaluation.families.front(); EXPECT_EQ(family.row_count, 2U); EXPECT_DOUBLE_EQ(family.reference_scale, 0.0); EXPECT_DOUBLE_EQ(family.near_zero_band, 0.0); EXPECT_EQ(family.near_zero_count, 2U); EXPECT_EQ(family.diagnostic_code, "zero-reference-scale-nonzero-error"); EXPECT_EQ(family.rows[0U].branch, ReferenceToleranceBranch::kZeroScaleExact); EXPECT_TRUE(family.rows[0U].passed); EXPECT_FALSE(family.rows[1U].passed); EXPECT_FALSE(family.rows[1U].relative_error_applicable); EXPECT_TRUE(std::isfinite(family.relative_rms)); EXPECT_DOUBLE_EQ(family.relative_rms, 0.0); EXPECT_FALSE(family.rms_passed); EXPECT_FALSE(family.passed); } TEST(ReferenceTolerancePolicy, KeepsWarningOnlyFamilyExceedanceOutOfBlockingVerdict) { const auto evaluation = ReferenceTolerancePolicy::Evaluate( {MakeFamily("translation", {"U1", "U2", "U3"}, true, {{0.0, 0.0}}), MakeFamily("rotation", {"UR1", "UR2", "UR3"}, false, {{1.0, 0.0}})}); ASSERT_TRUE(evaluation.valid); ASSERT_EQ(evaluation.families.size(), 2U); EXPECT_TRUE(evaluation.families[0U].passed); EXPECT_FALSE(evaluation.families[1U].passed); EXPECT_TRUE(evaluation.passed); } TEST(ReferenceTolerancePolicy, RejectsNonfiniteInputsBeforeToleranceEvaluation) { const auto evaluation = ReferenceTolerancePolicy::Evaluate( {MakeFamily("displacement-translation", {"UX", "UY", "UZ"}, true, {{std::numeric_limits::quiet_NaN(), 0.0}})}); EXPECT_FALSE(evaluation.valid); EXPECT_EQ(evaluation.diagnostic_code, "nonfinite-comparison-value"); EXPECT_TRUE(evaluation.families.empty()); } TEST(ReferenceTolerancePolicy, EigenFirstImplementationDoesNotRelaxCommonPolicy) { const std::vector families{ MakeFamily("mode-1", {"Value"}, true, {{105.0, 100.0}})}; const auto common = ReferenceTolerancePolicy::Evaluate(families); const auto eigen = ReferenceTolerancePolicy::EvaluateMitc4EigenFirstImplementation(families); ASSERT_TRUE(common.valid); ASSERT_TRUE(eigen.valid); EXPECT_FALSE(common.passed); EXPECT_TRUE(eigen.passed); EXPECT_DOUBLE_EQ(eigen.families[0].relative_rms, 0.05); EXPECT_DOUBLE_EQ(common.families[0].relative_rms, eigen.families[0].relative_rms); } TEST(ReferenceTolerancePolicy, EigenFirstImplementationPreservesZeroAndFiniteChecks) { for (const auto& row : std::vector{ {0.0, 0.0}, {1.0, 0.0}, {std::numeric_limits::quiet_NaN(), 1.0}}) { const auto result = ReferenceTolerancePolicy::EvaluateMitc4EigenFirstImplementation( {MakeFamily("mode-1", {"Value"}, true, {row})}); if (!std::isfinite(row.fesa_value)) { EXPECT_FALSE(result.valid); EXPECT_FALSE(result.passed); } else { ASSERT_TRUE(result.valid); EXPECT_EQ(result.passed, row.fesa_value == 0.0); if (row.fesa_value != 0.0) { EXPECT_EQ(result.families[0].diagnostic_code, "zero-reference-scale-nonzero-error"); } } } } TEST(ReferenceTolerancePolicy, EigenFirstImplementationRejectsNonmodalFamilies) { for (const auto& families : std::vector>{ {}, {MakeFamily("mode-1", {"U1"}, true, {{100.0, 100.0}})}, {MakeFamily("mode-1", {"Value"}, false, {{100.0, 100.0}})}, {MakeFamily("modes", {"Value"}, true, {{100.0, 100.0}, {100.0, 100.0}})}}) { const auto result = ReferenceTolerancePolicy::EvaluateMitc4EigenFirstImplementation(families); EXPECT_FALSE(result.valid); EXPECT_FALSE(result.passed); EXPECT_EQ(result.diagnostic_code, "invalid-eigen-tolerance-family"); } } }