test: strengthen core harness guardrails

This commit is contained in:
NINI
2026-05-04 12:46:37 +09:00
parent 950ce29df1
commit 99445d43bb
5 changed files with 97 additions and 28 deletions
+28 -13
View File
@@ -53,6 +53,11 @@ inline bool containsDiagnostic(const std::vector<Diagnostic>& diagnostics, const
});
}
inline Diagnostic makeDiagnostic(Severity severity, std::string code, std::string message, std::string keyword,
std::string file = "<domain>", LocalIndex line = 0) {
return {severity, std::move(code), std::move(message), {std::move(file), line, std::move(keyword)}};
}
inline std::string trim(std::string text) {
auto is_space = [](unsigned char c) { return std::isspace(c) != 0; };
text.erase(text.begin(), std::find_if(text.begin(), text.end(), [&](unsigned char c) { return !is_space(c); }));
@@ -624,11 +629,13 @@ inline std::optional<GlobalId> numericTarget(const std::string& target) {
return parseInt64(target);
}
inline std::vector<GlobalId> resolveNodeTarget(const Domain& domain, const std::string& target, std::vector<Diagnostic>* diagnostics = nullptr) {
inline std::vector<GlobalId> resolveNodeTarget(const Domain& domain, const std::string& target, std::vector<Diagnostic>* diagnostics = nullptr,
const std::string& diagnostic_keyword = "node target") {
if (auto node_id = numericTarget(target)) {
if (domain.nodes.count(*node_id) == 0) {
if (diagnostics != nullptr) {
diagnostics->push_back({Severity::Error, "FESA-VALIDATION-MISSING-NODE", "Missing node target: " + target, {}});
diagnostics->push_back(
makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-NODE", "Missing node target: " + target, diagnostic_keyword));
}
return {};
}
@@ -637,7 +644,8 @@ inline std::vector<GlobalId> resolveNodeTarget(const Domain& domain, const std::
auto set_it = domain.node_sets.find(Domain::key(target));
if (set_it == domain.node_sets.end()) {
if (diagnostics != nullptr) {
diagnostics->push_back({Severity::Error, "FESA-VALIDATION-MISSING-NSET", "Missing node set: " + target, {}});
diagnostics->push_back(
makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-NSET", "Missing node set: " + target, diagnostic_keyword));
}
return {};
}
@@ -660,44 +668,51 @@ inline const ShellSection* shellSectionForElement(const Domain& domain, GlobalId
inline std::vector<Diagnostic> validateDomain(const Domain& domain) {
std::vector<Diagnostic> diagnostics;
if (domain.elements.empty()) {
diagnostics.push_back({Severity::Error, "FESA-SINGULAR-NO-ACTIVE-ELEMENTS", "No active elements exist in the current model", {}});
diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-NO-ACTIVE-ELEMENTS",
"No active elements exist in the current model", "analysis model"));
}
if (domain.boundary_conditions.empty()) {
diagnostics.push_back({Severity::Warning, "FESA-SINGULAR-NO-BOUNDARY", "No boundary constraints are defined", {}});
diagnostics.push_back(makeDiagnostic(Severity::Warning, "FESA-SINGULAR-NO-BOUNDARY", "No boundary constraints are defined", "boundary"));
}
for (const auto& [id, element] : domain.elements) {
for (GlobalId node_id : element.node_ids) {
if (domain.nodes.count(node_id) == 0) {
diagnostics.push_back({Severity::Error, "FESA-VALIDATION-ELEMENT-MISSING-NODE", "Element references missing node", {}});
diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-ELEMENT-MISSING-NODE",
"Element " + std::to_string(id) + " references missing node " + std::to_string(node_id),
"element"));
}
}
const ShellSection* section = shellSectionForElement(domain, id);
if (section == nullptr) {
diagnostics.push_back({Severity::Error, "FESA-VALIDATION-MISSING-PROPERTY", "Element has no assigned shell section", {}});
diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-PROPERTY",
"Element " + std::to_string(id) + " has no assigned shell section", "element"));
}
}
for (const ShellSection& section : domain.shell_sections) {
if (domain.element_sets.count(Domain::key(section.element_set)) == 0) {
diagnostics.push_back({Severity::Error, "FESA-VALIDATION-MISSING-ELSET", "Shell section references missing element set: " + section.element_set, {}});
diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-ELSET",
"Shell section references missing element set: " + section.element_set, "shell section"));
}
auto material_it = domain.materials.find(Domain::key(section.material));
if (material_it == domain.materials.end()) {
diagnostics.push_back({Severity::Error, "FESA-VALIDATION-MISSING-MATERIAL", "Shell section references missing material: " + section.material, {}});
diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-MATERIAL",
"Shell section references missing material: " + section.material, "shell section"));
} else if (material_it->second.elastic_modulus <= 0.0) {
diagnostics.push_back({Severity::Error, "FESA-VALIDATION-INCOMPLETE-MATERIAL", "Material has no valid elastic constants: " + section.material, {}});
diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-INCOMPLETE-MATERIAL",
"Material has no valid elastic constants: " + section.material, "material"));
}
}
for (const BoundaryCondition& boundary : domain.boundary_conditions) {
(void)resolveNodeTarget(domain, boundary.target, &diagnostics);
(void)resolveNodeTarget(domain, boundary.target, &diagnostics, "boundary");
}
for (const NodalLoad& load : domain.loads) {
(void)resolveNodeTarget(domain, load.target, &diagnostics);
(void)resolveNodeTarget(domain, load.target, &diagnostics, "cload");
}
const bool any_nonzero_load = std::any_of(domain.loads.begin(), domain.loads.end(), [](const NodalLoad& load) {
return std::fabs(load.magnitude) > 0.0;
});
if (!any_nonzero_load) {
diagnostics.push_back({Severity::Warning, "FESA-SINGULAR-NO-NONZERO-LOAD", "No nonzero load is defined", {}});
diagnostics.push_back(makeDiagnostic(Severity::Warning, "FESA-SINGULAR-NO-NONZERO-LOAD", "No nonzero load is defined", "cload"));
}
return diagnostics;
}