test: onboard quad02 reaction reference
This commit is contained in:
+93
-4
@@ -213,10 +213,14 @@ std::string readTextFile(const std::string& path) {
|
||||
|
||||
void checkComparisonPass(const fesa::ComparisonResult& comparison) {
|
||||
if (!comparison.pass) {
|
||||
throw std::runtime_error("reference comparison failed: max_abs_error=" +
|
||||
std::to_string(comparison.max_abs_error) +
|
||||
", max_rel_error=" + std::to_string(comparison.max_rel_error) +
|
||||
", diagnostics=" + std::to_string(comparison.diagnostics.size()));
|
||||
std::string message = "reference comparison failed: max_abs_error=" +
|
||||
std::to_string(comparison.max_abs_error) +
|
||||
", max_rel_error=" + std::to_string(comparison.max_rel_error) +
|
||||
", diagnostics=" + std::to_string(comparison.diagnostics.size());
|
||||
if (!comparison.diagnostics.empty()) {
|
||||
message += ", first_diagnostic=" + comparison.diagnostics.front().message;
|
||||
}
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1009,6 +1013,20 @@ FESA_TEST(displacement_csv_loader_accepts_quad02_format) {
|
||||
FESA_CHECK(table.rows.at(2).values[2] < 0.0);
|
||||
}
|
||||
|
||||
FESA_TEST(reaction_csv_loader_accepts_quad02_reactionforces_format) {
|
||||
const auto required_columns = fesa::reactionCsvRequiredColumns();
|
||||
FESA_CHECK(required_columns == std::vector<std::string>({"Node Label", "RF-RF1", "RF-RF2", "RF-RF3",
|
||||
"RM-RM1", "RM-RM2", "RM-RM3"}));
|
||||
|
||||
auto table = fesa::loadReactionCsv(sourceRoot() + "/references/quad_02_reactionforces.csv");
|
||||
FESA_CHECK(!fesa::hasError(table.diagnostics));
|
||||
FESA_CHECK(table.rows.size() == 121);
|
||||
FESA_CHECK(table.rows.count(1) == 1);
|
||||
FESA_CHECK(table.rows.count(2) == 1);
|
||||
FESA_CHECK_NEAR(table.rows.at(1).values[2], 6.86e3, 1.0e-9);
|
||||
FESA_CHECK_NEAR(table.rows.at(1).values[4], 2.40e4, 1.0e-8);
|
||||
}
|
||||
|
||||
FESA_TEST(displacement_csv_loader_reports_required_header_errors) {
|
||||
auto table = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2\n"
|
||||
"1,0,0,0,0,0\n",
|
||||
@@ -1016,6 +1034,13 @@ FESA_TEST(displacement_csv_loader_reports_required_header_errors) {
|
||||
FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-MISSING-COLUMN"));
|
||||
}
|
||||
|
||||
FESA_TEST(reaction_csv_loader_reports_required_header_errors) {
|
||||
auto table = fesa::loadReactionCsvFromString("Node Label,RF-RF1,RF-RF2,RF-RF3,RM-RM1,RM-RM2\n"
|
||||
"1,0,0,0,0,0\n",
|
||||
"missing-reaction-header.csv");
|
||||
FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-MISSING-COLUMN"));
|
||||
}
|
||||
|
||||
FESA_TEST(displacement_csv_loader_reports_duplicate_node_rows) {
|
||||
auto table = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2,UR-UR3\n"
|
||||
"1,0,0,0,0,0,0\n"
|
||||
@@ -1024,6 +1049,14 @@ FESA_TEST(displacement_csv_loader_reports_duplicate_node_rows) {
|
||||
FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-DUPLICATE-NODE"));
|
||||
}
|
||||
|
||||
FESA_TEST(reaction_csv_loader_reports_duplicate_node_rows) {
|
||||
auto table = fesa::loadReactionCsvFromString("Node Label,RF-RF1,RF-RF2,RF-RF3,RM-RM1,RM-RM2,RM-RM3\n"
|
||||
"1,0,0,0,0,0,0\n"
|
||||
"1,0,0,0,0,0,0\n",
|
||||
"duplicate-reaction-node.csv");
|
||||
FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-DUPLICATE-NODE"));
|
||||
}
|
||||
|
||||
FESA_TEST(displacement_csv_loader_reports_missing_and_non_numeric_node_rows) {
|
||||
auto table = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2,UR-UR3\n"
|
||||
",0,0,0,0,0,0\n"
|
||||
@@ -1050,6 +1083,22 @@ FESA_TEST(displacement_comparator_matches_by_node_id_not_row_order) {
|
||||
FESA_CHECK(compared.pass);
|
||||
}
|
||||
|
||||
FESA_TEST(reaction_comparator_matches_by_node_id_not_row_order) {
|
||||
fesa::FieldOutput actual;
|
||||
actual.name = "RF";
|
||||
actual.position = "NODAL";
|
||||
actual.entity_type = "node";
|
||||
actual.basis = "GLOBAL";
|
||||
actual.entity_ids = {2, 1};
|
||||
actual.component_labels = fesa::reactionComponentLabels();
|
||||
actual.values = {{{0, 0, 2, 0, 20, 0}}, {{0, 0, 1, 0, 10, 0}}};
|
||||
fesa::CsvReactionTable expected;
|
||||
expected.rows[1] = {1, {0, 0, 1, 0, 10, 0}};
|
||||
expected.rows[2] = {2, {0, 0, 2, 0, 20, 0}};
|
||||
auto compared = fesa::compareReactions(actual, expected, {1.0e-12, 1.0e-12, 1.0});
|
||||
FESA_CHECK(compared.pass);
|
||||
}
|
||||
|
||||
FESA_TEST(displacement_comparator_uses_absolute_and_relative_tolerances) {
|
||||
fesa::FieldOutput actual;
|
||||
actual.name = "U";
|
||||
@@ -1089,6 +1138,24 @@ FESA_TEST(displacement_comparator_rejects_wrong_component_labels_and_missing_nod
|
||||
FESA_CHECK(fesa::containsDiagnostic(compared.diagnostics, "FESA-COMPARE-MISSING-ACTUAL"));
|
||||
}
|
||||
|
||||
FESA_TEST(reaction_comparator_rejects_wrong_component_labels_and_missing_nodes) {
|
||||
fesa::FieldOutput actual;
|
||||
actual.name = "RF";
|
||||
actual.position = "NODAL";
|
||||
actual.entity_type = "node";
|
||||
actual.basis = "GLOBAL";
|
||||
actual.entity_ids = {1};
|
||||
actual.component_labels = fesa::displacementComponentLabels();
|
||||
actual.values = {{{0, 0, 0, 0, 0, 0}}};
|
||||
fesa::CsvReactionTable expected;
|
||||
expected.rows[2] = {2, {0, 0, 0, 0, 0, 0}};
|
||||
|
||||
auto compared = fesa::compareReactions(actual, expected, {1.0e-12, 1.0e-12, 1.0});
|
||||
FESA_CHECK(!compared.pass);
|
||||
FESA_CHECK(fesa::containsDiagnostic(compared.diagnostics, "FESA-COMPARE-COMPONENT-LABELS"));
|
||||
FESA_CHECK(fesa::containsDiagnostic(compared.diagnostics, "FESA-COMPARE-MISSING-ACTUAL"));
|
||||
}
|
||||
|
||||
FESA_TEST(displacement_comparator_reports_duplicate_actual_nodes) {
|
||||
fesa::FieldOutput actual;
|
||||
actual.name = "U";
|
||||
@@ -1122,9 +1189,13 @@ FESA_TEST(quad02_reference_fixture_discovery_is_consistent) {
|
||||
const auto reference = fesa::loadDisplacementCsv(sourceRoot() + "/references/quad_02_displacements.csv");
|
||||
FESA_CHECK(!fesa::hasError(reference.diagnostics));
|
||||
FESA_CHECK(reference.rows.size() == normalized.domain.nodes.size());
|
||||
const auto reactions = fesa::loadReactionCsv(sourceRoot() + "/references/quad_02_reactionforces.csv");
|
||||
FESA_CHECK(!fesa::hasError(reactions.diagnostics));
|
||||
FESA_CHECK(reactions.rows.size() == normalized.domain.nodes.size());
|
||||
for (const auto& [node_id, node] : normalized.domain.nodes) {
|
||||
(void)node;
|
||||
FESA_CHECK(reference.rows.count(node_id) == 1);
|
||||
FESA_CHECK(reactions.rows.count(node_id) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1145,6 +1216,24 @@ FESA_TEST(quad02_phase1_stored_displacement_reference_regression) {
|
||||
FESA_CHECK(comparison.max_rel_error <= 1.0e-5);
|
||||
}
|
||||
|
||||
FESA_TEST(quad02_phase1_stored_reaction_reference_comparison_reports_current_gap) {
|
||||
const auto input_text = readTextFile(sourceRoot() + "/references/quad_02_phase1.inp");
|
||||
const auto analysis = fesa::runLinearStaticInputString(input_text, "quad_02_phase1.inp");
|
||||
FESA_CHECK(analysis.ok());
|
||||
FESA_CHECK(analysis.state.converged);
|
||||
FESA_CHECK(analysis.result_file.steps.size() == 1);
|
||||
const auto& frame = analysis.result_file.steps[0].frames[0];
|
||||
FESA_CHECK(frame.field_outputs.count("RF") == 1);
|
||||
|
||||
const auto expected = fesa::loadReactionCsv(sourceRoot() + "/references/quad_02_reactionforces.csv");
|
||||
FESA_CHECK(!fesa::hasError(expected.diagnostics));
|
||||
const auto comparison = fesa::compareReactions(frame.field_outputs.at("RF"), expected, {1.0e-6, 1.0e-5, 1.0});
|
||||
FESA_CHECK(!comparison.pass);
|
||||
FESA_CHECK(fesa::containsDiagnostic(comparison.diagnostics, "FESA-COMPARE-TOLERANCE"));
|
||||
FESA_CHECK(comparison.max_abs_error > 100.0);
|
||||
FESA_CHECK(comparison.max_rel_error > 0.1);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_shape_functions_node_order_and_tying_points) {
|
||||
auto center = fesa::shapeFunctions(0.0, 0.0);
|
||||
const fesa::Real sum = center.n[0] + center.n[1] + center.n[2] + center.n[3];
|
||||
|
||||
@@ -91,6 +91,11 @@ int main() {
|
||||
check(required_columns.front() == "Node Label", "CSV node label column changed");
|
||||
check(required_columns.back() == "UR-UR3", "CSV rotation column changed");
|
||||
|
||||
const auto required_reaction_columns = fesa::reactionCsvRequiredColumns();
|
||||
check(required_reaction_columns.size() == 7, "required reaction CSV column count changed");
|
||||
check(required_reaction_columns.front() == "Node Label", "reaction CSV node label column changed");
|
||||
check(required_reaction_columns.back() == "RM-RM3", "reaction CSV moment column changed");
|
||||
|
||||
const auto missing_header = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2\n"
|
||||
"1,0,0,0,0,0\n",
|
||||
"missing-header.csv");
|
||||
@@ -112,9 +117,21 @@ int main() {
|
||||
const auto comparison = fesa::compareDisplacements(u_field, expected, {1.0e-12, 1.0e-12, 1.0});
|
||||
check(comparison.pass, "displacement comparator no longer matches by node id");
|
||||
|
||||
fesa::CsvReactionTable expected_reactions;
|
||||
expected_reactions.rows[1] = {1, {0, 0, 3.0, 0, 0, 0}};
|
||||
expected_reactions.rows[2] = {2, {0, 0, 0, 0, 0, 0}};
|
||||
expected_reactions.rows[3] = {3, {0, 0, 0, 0, 0, 0}};
|
||||
expected_reactions.rows[4] = {4, {0, 0, 0, 0, 0, 0}};
|
||||
const auto reaction_comparison = fesa::compareReactions(rf_field, expected_reactions, {1.0e-12, 1.0e-12, 1.0});
|
||||
check(reaction_comparison.pass, "reaction comparator no longer matches by node id");
|
||||
|
||||
const auto quad02 = fesa::loadDisplacementCsv(sourceRoot() + "/references/quad_02_displacements.csv");
|
||||
check(!fesa::hasError(quad02.diagnostics), "quad_02 displacement CSV no longer loads");
|
||||
check(quad02.rows.size() == 121, "quad_02 displacement CSV row count changed");
|
||||
|
||||
const auto quad02_reactions = fesa::loadReactionCsv(sourceRoot() + "/references/quad_02_reactionforces.csv");
|
||||
check(!fesa::hasError(quad02_reactions.diagnostics), "quad_02 reaction CSV no longer loads");
|
||||
check(quad02_reactions.rows.size() == 121, "quad_02 reaction CSV row count changed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user