modify tolerance policy
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user