modify tolerance policy

This commit is contained in:
KOKO\Mimi
2026-08-18 02:29:26 +09:00
parent 123a1b6ccc
commit 624ea4872c
36 changed files with 1909 additions and 672 deletions
+154 -79
View File
@@ -24,6 +24,8 @@
#include <utility>
#include <vector>
#include "reference_tolerance_policy.h"
namespace fesa::test {
namespace {
@@ -32,7 +34,6 @@ constexpr const char* kDisplacementPath =
constexpr const char* kInternalFormulation = "FESA-MITC4";
constexpr const char* kIntegrationRule =
"2x2x2-gauss; mitc4-edge-midpoint-shear";
constexpr double kFixedAbsoluteTolerance = 1.0e-5;
constexpr std::array<const char*, 6> kComponents{"U1", "U2", "U3",
"UR1", "UR2", "UR3"};
const std::vector<std::string> expected_header{"Part Instance Name",
@@ -648,6 +649,18 @@ std::string FiniteText(const double value) {
return stream.str();
}
const char* BranchName(const ReferenceToleranceBranch branch) {
switch (branch) {
case ReferenceToleranceBranch::kNearZero:
return "near-zero";
case ReferenceToleranceBranch::kRelative:
return "relative";
case ReferenceToleranceBranch::kZeroScaleExact:
return "zero-scale-exact";
}
return "unknown";
}
std::string JsonEscape(const std::string& value) {
std::ostringstream stream;
for (const unsigned char character : value) {
@@ -687,6 +700,18 @@ std::string JsonEscape(const std::string& value) {
return stream.str();
}
void WriteJsonStrings(std::ostream& stream,
const std::vector<std::string>& values) {
stream << '[';
for (std::size_t index = 0U; index < values.size(); ++index) {
if (index != 0U) {
stream << ',';
}
stream << '"' << JsonEscape(values[index]) << '"';
}
stream << ']';
}
} // namespace
Result<Mitc4ComparisonReport> Mitc4ReferenceComparison::Compare(
@@ -713,14 +738,6 @@ Result<Mitc4ComparisonReport> Mitc4ReferenceComparison::Compare(
}
}
std::array<double, 6> scales{};
for (const auto& row : reference_rows) {
for (std::size_t component = 0U; component < scales.size(); ++component) {
scales[component] =
(std::max)(scales[component], std::abs(row.values[component]));
}
}
Mitc4ComparisonReport report{};
report.case_id = reference_case.case_id;
report.source_element_type = std::move(hdf5.source_element_type);
@@ -729,80 +746,126 @@ Result<Mitc4ComparisonReport> Mitc4ReferenceComparison::Compare(
report.passed = true;
report.rows.reserve(hdf5.rows.size() * kComponents.size());
report.vector_metrics.reserve(hdf5.rows.size());
std::array<double, 6> error_norms{};
std::array<double, 6> maximum_errors{};
std::array<double, 6> maximum_normalized{};
std::array<std::size_t, 6> worst_rows{};
double global_worst_normalized = -1.0;
std::array<std::vector<std::size_t>, 2> family_rows;
for (const auto& hdf5_row : hdf5.rows) {
const auto reference = reference_by_identity.find(hdf5_row.identity);
if (reference == reference_by_identity.end()) {
Fail("schema-mismatch", "A projected reference row is missing.");
}
std::array<double, 6> errors{};
for (std::size_t component = 0U; component < kComponents.size();
++component) {
const double tolerance = kFixedAbsoluteTolerance;
const double absolute_error = std::abs(
hdf5_row.values[component] - reference->second->values[component]);
const double normalized_error = absolute_error / tolerance;
if (!std::isfinite(tolerance) || !(tolerance > 0.0) ||
!std::isfinite(absolute_error) ||
!std::isfinite(normalized_error)) {
Fail("schema-mismatch",
"A finite row produced a nonfinite comparison metric.");
}
const bool blocking = component < 3U;
const bool within_tolerance = absolute_error <= tolerance;
const std::size_t row_index = report.rows.size();
report.rows.push_back(
{reference_case.case_id, hdf5_row.identity.instance_name,
hdf5_row.identity.source_node_label, kComponents[component],
hdf5_row.values[component], reference->second->values[component],
absolute_error, tolerance, normalized_error, blocking,
within_tolerance});
errors[component] = absolute_error;
error_norms[component] =
std::hypot(error_norms[component], absolute_error);
if (normalized_error > maximum_normalized[component]) {
maximum_normalized[component] = normalized_error;
maximum_errors[component] = absolute_error;
worst_rows[component] = row_index;
}
if (normalized_error > global_worst_normalized) {
global_worst_normalized = normalized_error;
report.worst_row = row_index;
}
if (blocking && !within_tolerance) {
report.passed = false;
} else if (!blocking && !within_tolerance) {
const std::string message =
"case=" + reference_case.case_id +
";instance=" + hdf5_row.identity.instance_name +
";node=" + std::to_string(hdf5_row.identity.source_node_label) +
";component=" + kComponents[component] +
";absolute_error=" + FiniteText(absolute_error) +
";tolerance=" + FiniteText(tolerance);
report.warnings.push_back(
{"rotation-reference-exceedance", row_index, message});
}
report.rows.push_back({reference_case.case_id,
hdf5_row.identity.instance_name,
hdf5_row.identity.source_node_label,
kComponents[component],
hdf5_row.values[component],
reference->second->values[component],
0.0,
0.0,
0.0,
0.0,
0.0,
false,
{},
blocking,
false});
family_rows[blocking ? 0U : 1U].push_back(row_index);
}
report.vector_metrics.push_back(
{hdf5_row.identity.instance_name, hdf5_row.identity.source_node_label,
std::hypot(errors[0U], errors[1U], errors[2U]),
std::hypot(errors[3U], errors[4U], errors[5U])});
}
report.metrics.reserve(kComponents.size());
const double row_count = static_cast<double>(hdf5.rows.size());
for (std::size_t component = 0U; component < kComponents.size();
++component) {
std::vector<ReferenceToleranceFamily> families;
families.reserve(2U);
for (std::size_t family_index = 0U; family_index < family_rows.size();
++family_index) {
ReferenceToleranceFamily family{};
family.identity = family_index == 0U ? "displacement-translation"
: "displacement-rotation";
family.components = family_index == 0U
? std::vector<std::string>{"U1", "U2", "U3"}
: std::vector<std::string>{"UR1", "UR2", "UR3"};
family.blocking = family_index == 0U;
family.rows.reserve(family_rows[family_index].size());
for (const std::size_t row_index : family_rows[family_index]) {
const auto& row = report.rows[row_index];
family.rows.push_back({row.fesa_value, row.reference_value});
}
families.push_back(std::move(family));
}
const auto evaluation = ReferenceTolerancePolicy::Evaluate(families);
if (!evaluation.valid || evaluation.families.size() != families.size()) {
Fail("schema-mismatch",
"A finite row produced a nonfinite comparison metric.");
}
report.metrics.reserve(evaluation.families.size());
double maximum_absolute_error = -1.0;
for (std::size_t family_index = 0U;
family_index < evaluation.families.size(); ++family_index) {
const auto& policy = evaluation.families[family_index];
for (std::size_t local = 0U; local < family_rows[family_index].size();
++local) {
const std::size_t row_index = family_rows[family_index][local];
auto& row = report.rows[row_index];
const auto& decision = policy.rows[local];
row.absolute_error = decision.absolute_error;
row.tolerance = decision.threshold;
row.normalized_error = decision.relative_error;
row.reference_scale = policy.reference_scale;
row.near_zero_band = policy.near_zero_band;
row.relative_error_applicable = decision.relative_error_applicable;
row.tolerance_branch = BranchName(decision.branch);
row.within_tolerance = decision.passed;
if (row.absolute_error > maximum_absolute_error) {
maximum_absolute_error = row.absolute_error;
report.worst_row = row_index;
}
if (!row.within_tolerance) {
if (row.blocking) {
report.passed = false;
} else {
const std::string message =
"case=" + reference_case.case_id +
";instance=" + row.instance_name +
";node=" + std::to_string(row.source_node_label) +
";component=" + row.component +
";absolute_error=" + FiniteText(row.absolute_error) +
";threshold=" + FiniteText(row.tolerance);
report.warnings.push_back(
{"rotation-reference-exceedance", row_index, message});
}
}
}
const std::size_t worst_row = family_rows[family_index][policy.worst_row];
report.metrics.push_back(
{kComponents[component], scales[component], kFixedAbsoluteTolerance,
maximum_errors[component], maximum_normalized[component],
error_norms[component] / std::sqrt(row_count),
error_norms[component], worst_rows[component]});
{policy.identity, policy.components, policy.blocking,
policy.reference_scale, policy.near_zero_band,
policy.near_zero_count, policy.maximum_absolute_error,
policy.relative_rms, worst_row, policy.rms_passed, policy.passed,
policy.diagnostic_code, policy.row_count});
if (policy.blocking && !policy.passed) {
report.passed = false;
}
if (!policy.blocking && !policy.rms_passed) {
report.warnings.push_back(
{"rotation-reference-rms-exceedance", worst_row,
"case=" + reference_case.case_id + ";family=" + policy.identity +
";relative_rms=" + FiniteText(policy.relative_rms)});
}
}
for (std::size_t node = 0U; node < hdf5.rows.size(); ++node) {
const std::size_t row_offset = node * kComponents.size();
report.vector_metrics.push_back(
{report.rows[row_offset].instance_name,
report.rows[row_offset].source_node_label,
std::hypot(report.rows[row_offset].absolute_error,
report.rows[row_offset + 1U].absolute_error,
report.rows[row_offset + 2U].absolute_error),
std::hypot(report.rows[row_offset + 3U].absolute_error,
report.rows[row_offset + 4U].absolute_error,
report.rows[row_offset + 5U].absolute_error)});
}
return Result<Mitc4ComparisonReport>::Success(std::move(report));
} catch (const ComparisonFailure& exception) {
@@ -845,8 +908,14 @@ Status Mitc4ReferenceComparison::WriteDeterministicJson(
<< "\",\"fesa_value\":" << row.fesa_value
<< ",\"reference_value\":" << row.reference_value
<< ",\"absolute_error\":" << row.absolute_error
<< ",\"tolerance\":" << row.tolerance
<< ",\"normalized_error\":" << row.normalized_error
<< ",\"threshold\":" << row.tolerance
<< ",\"relative_error\":" << row.normalized_error
<< ",\"reference_scale\":" << row.reference_scale
<< ",\"near_zero_band\":" << row.near_zero_band
<< ",\"relative_error_applicable\":"
<< (row.relative_error_applicable ? "true" : "false")
<< ",\"tolerance_branch\":\"" << JsonEscape(row.tolerance_branch)
<< "\""
<< ",\"blocking\":" << (row.blocking ? "true" : "false")
<< ",\"within_tolerance\":"
<< (row.within_tolerance ? "true" : "false") << '}';
@@ -857,15 +926,21 @@ Status Mitc4ReferenceComparison::WriteDeterministicJson(
stream << ',';
}
const auto& metric = report.metrics[index];
stream << "{\"component\":\"" << JsonEscape(metric.component)
<< "\",\"reference_scale\":" << metric.reference_scale
<< ",\"tolerance\":" << metric.tolerance
stream << "{\"family_identity\":\"" << JsonEscape(metric.family_identity)
<< "\",\"components\":";
WriteJsonStrings(stream, metric.components);
stream << ",\"blocking\":" << (metric.blocking ? "true" : "false")
<< ",\"reference_scale\":" << metric.reference_scale
<< ",\"near_zero_band\":" << metric.near_zero_band
<< ",\"near_zero_count\":" << metric.near_zero_count
<< ",\"row_count\":" << metric.row_count
<< ",\"maximum_absolute_error\":" << metric.maximum_absolute_error
<< ",\"maximum_normalized_error\":"
<< metric.maximum_normalized_error
<< ",\"rms_error\":" << metric.rms_error
<< ",\"vector_norm_error\":" << metric.vector_norm_error
<< ",\"worst_row\":" << metric.worst_row << '}';
<< ",\"relative_rms\":" << metric.relative_rms
<< ",\"worst_row\":" << metric.worst_row
<< ",\"rms_passed\":" << (metric.rms_passed ? "true" : "false")
<< ",\"passed\":" << (metric.passed ? "true" : "false")
<< ",\"diagnostic_code\":\"" << JsonEscape(metric.diagnostic_code)
<< "\"}";
}
stream << "],\"vector_metrics\":[";
for (std::size_t index = 0U; index < report.vector_metrics.size();