feat(beam-reference-qualification): correlate Abaqus beam results

This commit is contained in:
KOKO\Mimi
2026-08-03 01:45:30 +09:00
parent b68f6ee143
commit 675379779f
21 changed files with 962 additions and 119 deletions
+60
View File
@@ -240,6 +240,66 @@ TEST(ComparisonMetric, RejectsComponentCountMismatch) {
report.failures, "validation.component_count_mismatch"));
}
TEST(CorrelationMetric, ComputesComponentWiseRmseAndRelativeL2) {
const std::vector<fesa::ComparisonSample> inputs{
sample(
fesa::ReferenceQuantity::displacement,
{"Part-1-1", 101, std::nullopt},
{3.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{0.0, 1.0e-9}),
sample(
fesa::ReferenceQuantity::displacement,
{"Part-1-1", 102, std::nullopt},
{4.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{0.0, 1.0e-9}),
};
const auto report = fesa::correlate_samples(inputs);
ASSERT_TRUE(report.evaluable);
ASSERT_TRUE(report.failures.empty());
ASSERT_EQ(report.metrics.size(), 6U);
EXPECT_EQ(report.metrics[0].quantity, fesa::ReferenceQuantity::displacement);
EXPECT_EQ(report.metrics[0].component_index, 0U);
EXPECT_EQ(report.metrics[0].value_count, 2U);
EXPECT_DOUBLE_EQ(
report.metrics[0].root_mean_square_error,
std::sqrt(12.5));
EXPECT_DOUBLE_EQ(report.metrics[0].relative_l2_error, 1.0);
EXPECT_DOUBLE_EQ(report.metrics[1].root_mean_square_error, 0.0);
EXPECT_DOUBLE_EQ(report.metrics[1].relative_l2_error, 0.0);
}
TEST(CorrelationMetric, UsesAbsoluteScaleNormForNearZeroReference) {
const std::vector<fesa::ComparisonSample> inputs{
sample(
fesa::ReferenceQuantity::reaction,
{"Part-1-1", 101, std::nullopt},
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{3.0e-9, 0.0, 0.0, 0.0, 0.0, 0.0},
{0.0, 1.0e-9}),
sample(
fesa::ReferenceQuantity::reaction,
{"Part-1-1", 102, std::nullopt},
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
{4.0e-9, 0.0, 0.0, 0.0, 0.0, 0.0},
{0.0, 1.0e-9}),
};
const auto report = fesa::correlate_samples(inputs);
ASSERT_TRUE(report.evaluable);
ASSERT_EQ(report.metrics.size(), 6U);
EXPECT_DOUBLE_EQ(
report.metrics[0].root_mean_square_error,
std::sqrt(12.5) * 1.0e-9);
EXPECT_DOUBLE_EQ(
report.metrics[0].relative_l2_error,
5.0 / std::sqrt(2.0));
}
TEST(EntityMatching, MatchesNodalResultByInstanceAndExternalLabel) {
const auto frame = result_frame();
const std::array reference{0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
+25 -6
View File
@@ -72,14 +72,14 @@ TEST(ReferenceCsv, ReadsSuppliedDisplacementsWithWhitespaceHeader) {
EXPECT_EQ(
result.rows.front().quantity,
fesa::ReferenceQuantity::displacement);
EXPECT_EQ(result.rows.front().position.instance_name, "Part-1-1");
EXPECT_EQ(result.rows.front().position.instance_name, "PART-1_1-1");
EXPECT_EQ(result.rows.front().position.entity_label, 1);
EXPECT_FALSE(result.rows.front().position.end_node_label.has_value());
EXPECT_EQ(
result.rows.front().values,
(std::vector<double>{0.0, 0.0, -1.0e-32, 0.0, 1.0e-31, 0.0}));
(std::vector<double>{0.0, 0.0, -1.0e-30, 0.0, 1.0e-29, 0.0}));
EXPECT_EQ(result.rows.back().position.entity_label, 11);
EXPECT_EQ(result.rows.back().values[2], -1.92e-4);
EXPECT_EQ(result.rows.back().values[2], -1.91857e-2);
}
TEST(ReferenceCsv, ReadsSuppliedReactionsWithWhitespaceHeader) {
@@ -90,12 +90,12 @@ TEST(ReferenceCsv, ReadsSuppliedReactionsWithWhitespaceHeader) {
ASSERT_TRUE(result.diagnostics.empty());
ASSERT_EQ(result.rows.size(), 11U);
EXPECT_EQ(result.rows.front().position.instance_name, "Part-1-1");
EXPECT_EQ(result.rows.front().position.instance_name, "PART-1_1-1");
EXPECT_EQ(result.rows.front().position.entity_label, 1);
EXPECT_FALSE(result.rows.front().position.end_node_label.has_value());
EXPECT_EQ(
result.rows.front().values,
(std::vector<double>{0.0, 0.0, 1.0e4, 0.0, -1.0e5, 0.0}));
(std::vector<double>{0.0, 0.0, 1.0e6, 0.0, -1.0e7, 0.0}));
}
TEST(InternalForceCsv, MapsAllSixComponentsAndElementEndPosition) {
@@ -115,7 +115,26 @@ TEST(InternalForceCsv, MapsAllSixComponentsAndElementEndPosition) {
EXPECT_EQ(*result.rows.front().position.end_node_label, 101);
EXPECT_EQ(
result.rows.front().values,
(std::vector<double>{1.25, -2.5, 3.75, -4.0, 5.5, -6.25}));
(std::vector<double>{1.25, 3.75, -2.5, -6.25, -4.0, 5.5}));
}
TEST(InternalForceCsv, AcceptsTrailingEmptyAbaqusExportColumns) {
const TemporaryCsv input{
"fesa-reference-trailing-empty-columns.csv",
"Part Instance Name,Element Label,Node Label,SF-SF1,SF-SF2,"
"SF-SF3,SM-SM1,SM-SM2,SM-SM3,,,\n"
"PART-1_1-1,1,1,1,2,3,4,5,6,,,\n"};
const auto result = fesa::read_reference_csv(
fesa::ReferenceQuantity::internal_force,
input.path(),
"unused-request-name");
ASSERT_TRUE(result.diagnostics.empty());
ASSERT_EQ(result.rows.size(), 1U);
EXPECT_EQ(
result.rows.front().values,
(std::vector<double>{1.0, 3.0, 2.0, 6.0, 4.0, 5.0}));
}
TEST(StressCsv, FillsOmittedInstanceAndReadsCentroidStress) {