FAILURE MAP
← Case archive

FA-70901 / Weather index computation / Open access

Magnus dew point with ice branch: supersaturation rejection · case 01

A faulty 104% humidity sensor produces a dew point above the air temperature.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The validity check has no upper bound on relative humidity.

VERIFIED REPAIR

Reject RH <= 0 and RH > 100.

Unsuccessful approach: Rejecting RH below 1% discards valid very dry readings.

Case contract

Input temperature in degC and RH in percent. RH <= 0 or RH > 100 returns None. Use Magnus constants a=17.62, b=243.12 when T >= 0 and a=22.46, b=272.62 below freezing; g = ln(RH/100) + aT/(b+T); dew/frost point = b*g/(a-g) rounded to 0.01.

Why this case matters

Dew and frost point drive fog, icing and frost forecasts.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(t_c, rh):
    if rh <= 0:
        return None
    a, b = (17.62, 243.12) if t_c >= 0 else (22.46, 272.62)
    g = math.log(rh / 100.0) + a * t_c / (b + t_c)
    return round(b * g / (a - g), 2)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['-20 C at 0% RH', [-20, 0], None], ['-20 C at 0.5% RH', [-20, 0.5], -65.32], ['-20 C at 5% RH', [-20, 5], -47.79], ['-20 C at 30% RH', [-20, 30], -31.95], ['-20 C at 50% RH', [-20, 50], -27.02], ['-20 C at 80% RH', [-20, 80], -22.3], ['-20 C at 104% RH', [-20, 104], None], ['-5 C at 0.5% RH', [-5, 0.5], -55.32]], [['-20 C at 104% RH', [-20, 104], None], ['-5 C at 0% RH', [-5, 0], None], ['-5 C at 0.5% RH', [-5, 0.5], -55.32], ['-5 C at 5% RH', [-5, 5], -35.98], ['-5 C at 30% RH', [-5, 30], -18.38], ['-5 C at 50% RH', [-5, 50], -12.87], ['0 C at 104% RH', [0, 104], None], ['25 C at 0.5% RH', [25, 0.5], -41.77]], [['-5 C at 100% RH', [-5, 100], -5.0], ['-5 C at 104% RH', [-5, 104], None], ['-0.5 C at 0% RH', [-0.5, 0], None], ['-0.5 C at 0.5% RH', [-0.5, 0.5], -52.36], ['-0.5 C at 5% RH', [-0.5, 5], -32.47], ['-0.5 C at 30% RH', [-0.5, 30], -14.32], ['0 C at 0.5% RH', [0, 0.5], -56.21], ['25 C at 104% RH', [25, 104], None]], [['-20 C at 0.5% RH', [-20, 0.5], -65.32], ['-5 C at 104% RH', [-5, 104], None], ['-0.5 C at 80% RH', [-0.5, 80], -3.17], ['-0.5 C at 100% RH', [-0.5, 100], -0.5], ['-0.5 C at 104% RH', [-0.5, 104], None], ['0 C at 0% RH', [0, 0], None], ['0 C at 0.5% RH', [0, 0.5], -56.21], ['0 C at 5% RH', [0, 5], -35.33]], [['0 C at 50% RH', [0, 50], -9.2], ['0 C at 80% RH', [0, 80], -3.04], ['0 C at 100% RH', [0, 100], 0.0], ['0 C at 104% RH', [0, 104], None], ['0.5 C at 0% RH', [0.5, 0], None], ['0.5 C at 0.5% RH', [0.5, 0.5], -55.91], ['0.5 C at 104% RH', [0.5, 104], None], ['10 C at 0.5% RH', [10, 0.5], -50.35]]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
-20 C at 0% RHNoneNonePassed
-20 C at 0.5% RH-65.32-65.32Passed
-20 C at 5% RH-47.79-47.79Passed
-20 C at 30% RH-31.95-31.95Passed
-20 C at 50% RH-27.02-27.02Passed
-20 C at 80% RH-22.3-22.3Passed
-20 C at 104% RH-19.59NoneFailed
-5 C at 0.5% RH-55.32-55.32Passed

SHA-256 / 08c53dfb37f003f33e924a213fcf429e3201cc47ed8dae8302d791372870e4f8

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(t_c, rh):
    if rh < 1 or rh > 100:
        return None
    a, b = (17.62, 243.12) if t_c >= 0 else (22.46, 272.62)
    g = math.log(rh / 100.0) + a * t_c / (b + t_c)
    return round(b * g / (a - g), 2)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['-20 C at 0% RH', [-20, 0], None], ['-20 C at 0.5% RH', [-20, 0.5], -65.32], ['-20 C at 5% RH', [-20, 5], -47.79], ['-20 C at 30% RH', [-20, 30], -31.95], ['-20 C at 50% RH', [-20, 50], -27.02], ['-20 C at 80% RH', [-20, 80], -22.3], ['-20 C at 104% RH', [-20, 104], None], ['-5 C at 0.5% RH', [-5, 0.5], -55.32]], [['-20 C at 104% RH', [-20, 104], None], ['-5 C at 0% RH', [-5, 0], None], ['-5 C at 0.5% RH', [-5, 0.5], -55.32], ['-5 C at 5% RH', [-5, 5], -35.98], ['-5 C at 30% RH', [-5, 30], -18.38], ['-5 C at 50% RH', [-5, 50], -12.87], ['0 C at 104% RH', [0, 104], None], ['25 C at 0.5% RH', [25, 0.5], -41.77]], [['-5 C at 100% RH', [-5, 100], -5.0], ['-5 C at 104% RH', [-5, 104], None], ['-0.5 C at 0% RH', [-0.5, 0], None], ['-0.5 C at 0.5% RH', [-0.5, 0.5], -52.36], ['-0.5 C at 5% RH', [-0.5, 5], -32.47], ['-0.5 C at 30% RH', [-0.5, 30], -14.32], ['0 C at 0.5% RH', [0, 0.5], -56.21], ['25 C at 104% RH', [25, 104], None]], [['-20 C at 0.5% RH', [-20, 0.5], -65.32], ['-5 C at 104% RH', [-5, 104], None], ['-0.5 C at 80% RH', [-0.5, 80], -3.17], ['-0.5 C at 100% RH', [-0.5, 100], -0.5], ['-0.5 C at 104% RH', [-0.5, 104], None], ['0 C at 0% RH', [0, 0], None], ['0 C at 0.5% RH', [0, 0.5], -56.21], ['0 C at 5% RH', [0, 5], -35.33]], [['0 C at 50% RH', [0, 50], -9.2], ['0 C at 80% RH', [0, 80], -3.04], ['0 C at 100% RH', [0, 100], 0.0], ['0 C at 104% RH', [0, 104], None], ['0.5 C at 0% RH', [0.5, 0], None], ['0.5 C at 0.5% RH', [0.5, 0.5], -55.91], ['0.5 C at 104% RH', [0.5, 104], None], ['10 C at 0.5% RH', [10, 0.5], -50.35]]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
-20 C at 0% RHNoneNonePassed
-20 C at 0.5% RHNone-65.32Failed
-20 C at 5% RH-47.79-47.79Passed
-20 C at 30% RH-31.95-31.95Passed
-20 C at 50% RH-27.02-27.02Passed
-20 C at 80% RH-22.3-22.3Passed
-20 C at 104% RHNoneNonePassed
-5 C at 0.5% RHNone-55.32Failed

SHA-256 / 9c4dd20587ed55409228613ec007bf1e90ba09976aa83cbc961c86491468eca0

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(t_c, rh):
    if rh <= 0 or rh > 100:
        return None
    a, b = (17.62, 243.12) if t_c >= 0 else (22.46, 272.62)
    g = math.log(rh / 100.0) + a * t_c / (b + t_c)
    return round(b * g / (a - g), 2)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['-20 C at 0% RH', [-20, 0], None], ['-20 C at 0.5% RH', [-20, 0.5], -65.32], ['-20 C at 5% RH', [-20, 5], -47.79], ['-20 C at 30% RH', [-20, 30], -31.95], ['-20 C at 50% RH', [-20, 50], -27.02], ['-20 C at 80% RH', [-20, 80], -22.3], ['-20 C at 104% RH', [-20, 104], None], ['-5 C at 0.5% RH', [-5, 0.5], -55.32]], [['-20 C at 104% RH', [-20, 104], None], ['-5 C at 0% RH', [-5, 0], None], ['-5 C at 0.5% RH', [-5, 0.5], -55.32], ['-5 C at 5% RH', [-5, 5], -35.98], ['-5 C at 30% RH', [-5, 30], -18.38], ['-5 C at 50% RH', [-5, 50], -12.87], ['0 C at 104% RH', [0, 104], None], ['25 C at 0.5% RH', [25, 0.5], -41.77]], [['-5 C at 100% RH', [-5, 100], -5.0], ['-5 C at 104% RH', [-5, 104], None], ['-0.5 C at 0% RH', [-0.5, 0], None], ['-0.5 C at 0.5% RH', [-0.5, 0.5], -52.36], ['-0.5 C at 5% RH', [-0.5, 5], -32.47], ['-0.5 C at 30% RH', [-0.5, 30], -14.32], ['0 C at 0.5% RH', [0, 0.5], -56.21], ['25 C at 104% RH', [25, 104], None]], [['-20 C at 0.5% RH', [-20, 0.5], -65.32], ['-5 C at 104% RH', [-5, 104], None], ['-0.5 C at 80% RH', [-0.5, 80], -3.17], ['-0.5 C at 100% RH', [-0.5, 100], -0.5], ['-0.5 C at 104% RH', [-0.5, 104], None], ['0 C at 0% RH', [0, 0], None], ['0 C at 0.5% RH', [0, 0.5], -56.21], ['0 C at 5% RH', [0, 5], -35.33]], [['0 C at 50% RH', [0, 50], -9.2], ['0 C at 80% RH', [0, 80], -3.04], ['0 C at 100% RH', [0, 100], 0.0], ['0 C at 104% RH', [0, 104], None], ['0.5 C at 0% RH', [0.5, 0], None], ['0.5 C at 0.5% RH', [0.5, 0.5], -55.91], ['0.5 C at 104% RH', [0.5, 104], None], ['10 C at 0.5% RH', [10, 0.5], -50.35]]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
-20 C at 0% RHNoneNonePassed
-20 C at 0.5% RH-65.32-65.32Passed
-20 C at 5% RH-47.79-47.79Passed
-20 C at 30% RH-31.95-31.95Passed
-20 C at 50% RH-27.02-27.02Passed
-20 C at 80% RH-22.3-22.3Passed
-20 C at 104% RHNoneNonePassed
-5 C at 0.5% RH-55.32-55.32Passed

SHA-256 / 46a1d182a846cc68d5404e5c7434900cec3f5e17b006f292a7c9e5a4146630a4

Verification & scope

Stipulated deterministic teaching model of an operational weather index; coefficients and thresholds are fixed by the contract and no claim of standards conformance is made. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:48:24.907448+00:00.

Case digest / 6b73d78d7916200eed2d279e5f9420946c51f225fc3566534021550d5835a4a0