fix: strengthen validation diagnostics

This commit is contained in:
NINI
2026-05-04 13:10:36 +09:00
parent c0f668754d
commit ac72f4ccd7
5 changed files with 256 additions and 9 deletions
+111
View File
@@ -124,6 +124,21 @@ std::size_t diagnosticCount(const std::vector<fesa::Diagnostic>& diagnostics, co
return count;
}
fesa::Domain singleElementValidationDomain() {
fesa::Domain domain;
domain.nodes[1] = {1, {0, 0, 0}};
domain.nodes[2] = {2, {1, 0, 0}};
domain.nodes[3] = {3, {1, 1, 0}};
domain.nodes[4] = {4, {0, 1, 0}};
domain.elements[1] = {1, fesa::ElementType::MITC4, {1, 2, 3, 4}, "EALL"};
domain.element_sets["eall"] = {"EALL", {1}};
domain.node_sets["all_nodes"] = {"ALL_NODES", {1, 2, 3, 4}};
domain.materials["mat"] = {"MAT", 1000.0, 0.3};
domain.shell_sections.push_back({"EALL", "MAT", 0.1});
domain.loads.push_back({"2", 3, -1.0});
return domain;
}
} // namespace
FESA_TEST(core_types_and_dof_mapping_are_stable) {
@@ -377,6 +392,102 @@ FESA_TEST(domain_validation_reports_missing_property_and_targets) {
}
}
FESA_TEST(domain_validation_reports_missing_sets_and_set_members) {
auto domain = singleElementValidationDomain();
domain.shell_sections.clear();
domain.shell_sections.push_back({"MISSING_ELSET", "MISSING_MAT", 0.1});
domain.node_sets["bad_nodes"] = {"BAD_NODES", {1, 99}};
domain.element_sets["bad_elements"] = {"BAD_ELEMENTS", {1, 77}};
domain.boundary_conditions.push_back({"MISSING_BOUNDARY_SET", 1, 6, 0.0});
domain.boundary_conditions.push_back({"BAD_NODES", 1, 1, 0.0});
domain.loads.push_back({"MISSING_LOAD_SET", 3, 1.0});
domain.loads.push_back({"BAD_NODES", 3, 1.0});
auto diagnostics = fesa::validateDomain(domain);
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-ELSET"));
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-MATERIAL"));
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-NSET"));
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-NSET-MISSING-NODE"));
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-ELSET-MISSING-ELEMENT"));
const auto* missing_set = findDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-NSET");
FESA_CHECK(missing_set != nullptr);
FESA_CHECK(missing_set->message.find("MISSING_BOUNDARY_SET") != std::string::npos ||
missing_set->message.find("MISSING_LOAD_SET") != std::string::npos);
const auto* missing_node = findDiagnostic(diagnostics, "FESA-VALIDATION-NSET-MISSING-NODE");
FESA_CHECK(missing_node != nullptr);
FESA_CHECK(missing_node->message.find("BAD_NODES") != std::string::npos);
FESA_CHECK(missing_node->message.find("99") != std::string::npos);
}
FESA_TEST(domain_validation_reports_nonpositive_thickness_and_invalid_dofs) {
auto domain = singleElementValidationDomain();
domain.shell_sections.front().thickness = 0.0;
domain.boundary_conditions.push_back({"ALL_NODES", 0, 1, 0.0});
domain.loads.push_back({"2", 7, 1.0});
auto diagnostics = fesa::validateDomain(domain);
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-NONPOSITIVE-THICKNESS"));
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-BOUNDARY-DOF"));
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-CLOAD-DOF"));
const auto* thickness = findDiagnostic(diagnostics, "FESA-VALIDATION-NONPOSITIVE-THICKNESS");
FESA_CHECK(thickness != nullptr);
FESA_CHECK(thickness->message.find("EALL") != std::string::npos);
const auto* load_dof = findDiagnostic(diagnostics, "FESA-VALIDATION-CLOAD-DOF");
FESA_CHECK(load_dof != nullptr);
FESA_CHECK(load_dof->message.find("DOF 7") != std::string::npos);
}
FESA_TEST(domain_validation_reports_no_active_elements_and_missing_load) {
fesa::Domain domain;
domain.nodes[1] = {1, {0, 0, 0}};
auto diagnostics = fesa::validateDomain(domain);
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-SINGULAR-NO-ACTIVE-ELEMENTS"));
FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-SINGULAR-NO-NONZERO-LOAD"));
for (const auto& diagnostic : diagnostics) {
FESA_CHECK(diagnostic.code.find("MESH") == std::string::npos);
}
}
FESA_TEST(domain_validation_reports_no_free_dofs) {
auto domain = singleElementValidationDomain();
domain.boundary_conditions.push_back({"ALL_NODES", 1, 6, 0.0});
auto diagnostics = fesa::validateDomain(domain);
const auto* no_free = findDiagnostic(diagnostics, "FESA-SINGULAR-NO-FREE-DOFS");
FESA_CHECK(no_free != nullptr);
FESA_CHECK(no_free->source.keyword == "dof");
FESA_CHECK(no_free->message.find("No free DOFs") != std::string::npos);
}
FESA_TEST(domain_validation_reports_free_untouched_dofs_for_isolated_nodes) {
auto domain = singleElementValidationDomain();
domain.nodes[99] = {99, {10, 0, 0}};
domain.boundary_conditions.push_back({"ALL_NODES", 1, 6, 0.0});
auto diagnostics = fesa::validateDomain(domain);
const auto* untouched = findDiagnostic(diagnostics, "FESA-SINGULAR-DOF-UNTOUCHED");
FESA_CHECK(untouched != nullptr);
FESA_CHECK(untouched->source.keyword == "dof");
FESA_CHECK(untouched->message.find("Node 99") != std::string::npos);
FESA_CHECK(untouched->message.find("UX") != std::string::npos);
}
FESA_TEST(domain_validation_reports_weak_drilling_dof_smoke) {
auto domain = singleElementValidationDomain();
domain.boundary_conditions.push_back({"ALL_NODES", 1, 5, 0.0});
auto diagnostics = fesa::validateDomain(domain);
const auto* weak_drilling = findDiagnostic(diagnostics, "FESA-SINGULAR-WEAK-DRILLING-DOF");
FESA_CHECK(weak_drilling != nullptr);
FESA_CHECK(weak_drilling->severity == fesa::Severity::Warning);
FESA_CHECK(weak_drilling->source.keyword == "dof");
FESA_CHECK(weak_drilling->message.find("RZ") != std::string::npos);
}
FESA_TEST(dof_manager_owns_equation_numbering_and_reconstruction) {
auto domain = parsedPhase1Domain();
fesa::DofManager dofs(domain);