///|
/// Return half the bilateral tolerance width.
pub fn tolerance_half_width(feature : Feature) -> Double {
(feature.upper - feature.lower).abs() / 2.0
}
///|
/// Return the absolute lower measurement limit.
pub fn lower_limit(feature : Feature) -> Double {
feature.nominal + feature.lower
}
///|
/// Return the absolute upper measurement limit.
pub fn upper_limit(feature : Feature) -> Double {
feature.nominal + feature.upper
}
///|
/// Return the normalized absolute deviation from nominal.
pub fn relative_error(feature : Feature, value : Double) -> Double {
let width = tolerance_half_width(feature)
if width == 0.0 {
if value == feature.nominal {
0.0
} else {
1.0
}
} else {
(value - feature.nominal).abs() / width
}
}
///|
/// Return whether a sample count can support an uncertainty calculation.
pub fn sample_count_is_valid(count : Int) -> Bool {
count > 0
}
///|
/// Return the distance of a value from the nearest specification limit.
pub fn limit_margin(feature : Feature, value : Double) -> Double {
let lower_margin = value - lower_limit(feature)
let upper_margin = upper_limit(feature) - value
if lower_margin < upper_margin {
lower_margin
} else {
upper_margin
}
}
///|
/// Return the signed deviation from a feature's nominal value.
pub fn signed_deviation(feature : Feature, value : Double) -> Double {
value - feature.nominal
}
///|
/// Classify an uncertainty ratio into a planning risk level.
pub fn risk_for_uncertainty(
uncertainty : Double,
tolerance : Double,
) -> RiskLevel {
if tolerance <= 0.0 {
RiskLevel::High
} else {
let ratio = uncertainty / tolerance
if ratio <= 0.25 {
RiskLevel::Low
} else if ratio <= 0.5 {
RiskLevel::Medium
} else {
RiskLevel::High
}
}
}
///|
/// Build a per-feature result from an optional observation.
pub fn feature_result(
feature~ : Feature,
observation~ : Observation?,
) -> FeatureResult {
match observation {
None =>
{
feature_id: feature.id,
verdict: Verdict::Unmeasured,
deviation: 0.0,
risk: RiskLevel::High,
sample_count: 0,
}
Some(observation) => {
let verdict = classify(feature, Some(observation))
let deviation = relative_error(feature, observation.value)
let risk = if verdict is Verdict::Pass {
RiskLevel::Low
} else {
RiskLevel::High
}
{
feature_id: feature.id,
verdict,
deviation,
risk,
sample_count: observation.sample_count,
}
}
}
}