feat: strengthen results comparator foundation
This commit is contained in:
+117
-4
@@ -652,11 +652,40 @@ FESA_TEST(results_writer_uses_step_frame_fields_for_u_and_rf) {
|
||||
writer.writeLinearStatic(domain, dofs, u, rf);
|
||||
const auto& result = writer.result();
|
||||
FESA_CHECK(result.schema_name == "FESA_RESULTS");
|
||||
FESA_CHECK(result.schema_version == 1);
|
||||
FESA_CHECK(result.solver_name == "FESA");
|
||||
FESA_CHECK(result.dof_convention == "UX,UY,UZ,RX,RY,RZ");
|
||||
FESA_CHECK(result.sign_convention == "Abaqus-compatible");
|
||||
FESA_CHECK(result.precision == "double");
|
||||
FESA_CHECK(result.index_type == "int64");
|
||||
FESA_CHECK(result.steps.size() == 1);
|
||||
FESA_CHECK(result.steps[0].frames[0].field_outputs.count("U") == 1);
|
||||
FESA_CHECK(result.steps[0].frames[0].field_outputs.count("RF") == 1);
|
||||
FESA_CHECK(result.steps[0].frames[0].field_outputs.at("U").component_labels[2] == "UZ");
|
||||
FESA_CHECK(result.steps[0].frames[0].field_outputs.at("RF").component_labels[2] == "RFZ");
|
||||
FESA_CHECK(result.steps[0].name == "Step-1");
|
||||
FESA_CHECK(result.steps[0].frames.size() == 1);
|
||||
const auto& frame = result.steps[0].frames[0];
|
||||
FESA_CHECK(frame.frame_id == 0);
|
||||
FESA_CHECK(frame.increment == 1);
|
||||
FESA_CHECK(frame.iteration == 0);
|
||||
FESA_CHECK(frame.converged);
|
||||
FESA_CHECK_NEAR(frame.step_time, 1.0, 1.0e-15);
|
||||
FESA_CHECK_NEAR(frame.total_time, 1.0, 1.0e-15);
|
||||
FESA_CHECK(frame.field_outputs.count("U") == 1);
|
||||
FESA_CHECK(frame.field_outputs.count("RF") == 1);
|
||||
const auto& u_field = frame.field_outputs.at("U");
|
||||
const auto& rf_field = frame.field_outputs.at("RF");
|
||||
FESA_CHECK(u_field.name == "U");
|
||||
FESA_CHECK(u_field.position == "NODAL");
|
||||
FESA_CHECK(u_field.entity_type == "node");
|
||||
FESA_CHECK(u_field.basis == "GLOBAL");
|
||||
FESA_CHECK(u_field.component_labels == fesa::displacementComponentLabels());
|
||||
FESA_CHECK(rf_field.name == "RF");
|
||||
FESA_CHECK(rf_field.position == "NODAL");
|
||||
FESA_CHECK(rf_field.entity_type == "node");
|
||||
FESA_CHECK(rf_field.basis == "GLOBAL");
|
||||
FESA_CHECK(rf_field.component_labels == fesa::reactionComponentLabels());
|
||||
FESA_CHECK(u_field.component_labels[2] == "UZ");
|
||||
FESA_CHECK(rf_field.component_labels[2] == "RFZ");
|
||||
FESA_CHECK(u_field.entity_ids.size() == domain.nodes.size());
|
||||
FESA_CHECK(rf_field.entity_ids.size() == domain.nodes.size());
|
||||
}
|
||||
|
||||
FESA_TEST(displacement_csv_loader_accepts_quad01_format) {
|
||||
@@ -674,9 +703,37 @@ FESA_TEST(displacement_csv_loader_accepts_quad02_format) {
|
||||
FESA_CHECK(table.rows.at(2).values[2] < 0.0);
|
||||
}
|
||||
|
||||
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",
|
||||
"missing-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"
|
||||
"1,0,0,0,0,0,0\n",
|
||||
"duplicate-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"
|
||||
"two,0,0,0,0,0,0\n"
|
||||
"3,0,not-a-number,0,0,0,0\n",
|
||||
"invalid-node-row.csv");
|
||||
FESA_CHECK(diagnosticCount(table.diagnostics, "FESA-CSV-NODE") == 2);
|
||||
FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-NUMERIC"));
|
||||
}
|
||||
|
||||
FESA_TEST(displacement_comparator_matches_by_node_id_not_row_order) {
|
||||
fesa::FieldOutput actual;
|
||||
actual.name = "U";
|
||||
actual.position = "NODAL";
|
||||
actual.entity_type = "node";
|
||||
actual.basis = "GLOBAL";
|
||||
actual.entity_ids = {2, 1};
|
||||
actual.component_labels = fesa::displacementComponentLabels();
|
||||
actual.values = {{{2, 0, 0, 0, 0, 0}}, {{1, 0, 0, 0, 0, 0}}};
|
||||
@@ -687,6 +744,62 @@ FESA_TEST(displacement_comparator_matches_by_node_id_not_row_order) {
|
||||
FESA_CHECK(compared.pass);
|
||||
}
|
||||
|
||||
FESA_TEST(displacement_comparator_uses_absolute_and_relative_tolerances) {
|
||||
fesa::FieldOutput actual;
|
||||
actual.name = "U";
|
||||
actual.position = "NODAL";
|
||||
actual.entity_type = "node";
|
||||
actual.basis = "GLOBAL";
|
||||
actual.entity_ids = {10};
|
||||
actual.component_labels = fesa::displacementComponentLabels();
|
||||
actual.values = {{{5.0e-7, 100.0005, 0, 0, 0, 0}}};
|
||||
fesa::CsvDisplacementTable expected;
|
||||
expected.rows[10] = {10, {0.0, 100.0, 0, 0, 0, 0}};
|
||||
|
||||
auto loose = fesa::compareDisplacements(actual, expected, {1.0e-6, 1.0e-5, 1.0});
|
||||
FESA_CHECK(loose.pass);
|
||||
FESA_CHECK_NEAR(loose.max_abs_error, 5.0e-4, 1.0e-12);
|
||||
|
||||
auto strict = fesa::compareDisplacements(actual, expected, {1.0e-8, 1.0e-8, 1.0});
|
||||
FESA_CHECK(!strict.pass);
|
||||
FESA_CHECK(fesa::containsDiagnostic(strict.diagnostics, "FESA-COMPARE-TOLERANCE"));
|
||||
}
|
||||
|
||||
FESA_TEST(displacement_comparator_rejects_wrong_component_labels_and_missing_nodes) {
|
||||
fesa::FieldOutput actual;
|
||||
actual.name = "U";
|
||||
actual.position = "NODAL";
|
||||
actual.entity_type = "node";
|
||||
actual.basis = "GLOBAL";
|
||||
actual.entity_ids = {1};
|
||||
actual.component_labels = fesa::reactionComponentLabels();
|
||||
actual.values = {{{0, 0, 0, 0, 0, 0}}};
|
||||
fesa::CsvDisplacementTable expected;
|
||||
expected.rows[2] = {2, {0, 0, 0, 0, 0, 0}};
|
||||
|
||||
auto compared = fesa::compareDisplacements(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";
|
||||
actual.position = "NODAL";
|
||||
actual.entity_type = "node";
|
||||
actual.basis = "GLOBAL";
|
||||
actual.entity_ids = {1, 1};
|
||||
actual.component_labels = fesa::displacementComponentLabels();
|
||||
actual.values = {{{0, 0, 0, 0, 0, 0}}, {{0, 0, 0, 0, 0, 0}}};
|
||||
fesa::CsvDisplacementTable expected;
|
||||
expected.rows[1] = {1, {0, 0, 0, 0, 0, 0}};
|
||||
|
||||
auto compared = fesa::compareDisplacements(actual, expected, {1.0e-12, 1.0e-12, 1.0});
|
||||
FESA_CHECK(!compared.pass);
|
||||
FESA_CHECK(fesa::containsDiagnostic(compared.diagnostics, "FESA-COMPARE-DUPLICATE-ACTUAL"));
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_shape_functions_and_stiffness_baseline) {
|
||||
auto shape = fesa::shapeFunctions(0.25, -0.5);
|
||||
const fesa::Real sum = shape.n[0] + shape.n[1] + shape.n[2] + shape.n[3];
|
||||
|
||||
Reference in New Issue
Block a user