FA-70941 / Weather index computation / Open access
Shade apparent temperature: saturation vapour pressure denominator · case 01
Hot humid values are inflated while cold values are slightly low.
ROOT CAUSE
The Tetens exponent drops the temperature from its denominator.
VERIFIED REPAIR
Use 17.27*Ta/(237.7+Ta).
Unsuccessful approach: Using the Kelvin offset 273.15 in the denominator still mis-scales the exponent.
Case contract
Input air temperature degC, RH percent, wind in km/h measured at 10 m. e = RH/100*6.105*exp(17.27*Ta/(237.7+Ta)) hPa; wind in m/s = km/h / 3.6; AT = Ta + 0.33e - 0.70ws - 4.00 rounded to 0.1.
Why this case matters
Feels-like temperature on consumer forecasts uses this non-radiative apparent temperature.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(ta, rh, wind_kmh):
e = rh / 100.0 * 6.105 * math.exp(17.27 * ta / 237.7)
ws = wind_kmh / 3.6
at = ta + 0.33 * e - 0.70 * ws - 4.00
return round(at, 1)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['-5 C 10% RH wind 0 km/h', [-5, 10, 0], -8.9], ['-5 C 10% RH wind 10 km/h', [-5, 10, 10], -10.8], ['-5 C 10% RH wind 36 km/h', [-5, 10, 36], -15.9], ['-5 C 10% RH wind 72 km/h', [-5, 10, 72], -22.9], ['-5 C 50% RH wind 0 km/h', [-5, 50, 0], -8.3], ['-5 C 50% RH wind 10 km/h', [-5, 50, 10], -10.2], ['12 C 10% RH wind 0 km/h', [12, 10, 0], 8.5], ['12 C 50% RH wind 0 km/h', [12, 50, 0], 10.3]], [['-5 C 50% RH wind 72 km/h', [-5, 50, 72], -22.3], ['-5 C 90% RH wind 0 km/h', [-5, 90, 0], -7.7], ['-5 C 90% RH wind 10 km/h', [-5, 90, 10], -9.7], ['-5 C 90% RH wind 36 km/h', [-5, 90, 36], -14.7], ['-5 C 90% RH wind 72 km/h', [-5, 90, 72], -21.7], ['0 C 10% RH wind 0 km/h', [0, 10, 0], -3.8], ['12 C 50% RH wind 36 km/h', [12, 50, 36], 3.3], ['12 C 50% RH wind 72 km/h', [12, 50, 72], -3.7]], [['0 C 10% RH wind 36 km/h', [0, 10, 36], -10.8], ['0 C 10% RH wind 72 km/h', [0, 10, 72], -17.8], ['0 C 50% RH wind 0 km/h', [0, 50, 0], -3.0], ['0 C 50% RH wind 10 km/h', [0, 50, 10], -4.9], ['0 C 50% RH wind 36 km/h', [0, 50, 36], -10.0], ['0 C 50% RH wind 72 km/h', [0, 50, 72], -17.0], ['12 C 90% RH wind 36 km/h', [12, 90, 36], 5.2], ['12 C 90% RH wind 72 km/h', [12, 90, 72], -1.8]], [['0 C 90% RH wind 10 km/h', [0, 90, 10], -4.1], ['0 C 90% RH wind 36 km/h', [0, 90, 36], -9.2], ['0 C 90% RH wind 72 km/h', [0, 90, 72], -16.2], ['12 C 10% RH wind 0 km/h', [12, 10, 0], 8.5], ['12 C 10% RH wind 10 km/h', [12, 10, 10], 6.5], ['12 C 10% RH wind 36 km/h', [12, 10, 36], 1.5], ['20 C 10% RH wind 10 km/h', [20, 10, 10], 14.8], ['20 C 50% RH wind 0 km/h', [20, 50, 0], 19.8]], [['12 C 50% RH wind 0 km/h', [12, 50, 0], 10.3], ['12 C 50% RH wind 10 km/h', [12, 50, 10], 8.4], ['12 C 50% RH wind 36 km/h', [12, 50, 36], 3.3], ['12 C 50% RH wind 72 km/h', [12, 50, 72], -3.7], ['12 C 90% RH wind 0 km/h', [12, 90, 0], 12.2], ['12 C 90% RH wind 10 km/h', [12, 90, 10], 10.2], ['20 C 50% RH wind 0 km/h', [20, 50, 0], 19.8], ['20 C 90% RH wind 10 km/h', [20, 90, 10], 21.0]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| -5 C 10% RH wind 0 km/h | -8.9 | -8.9 | Passed |
| -5 C 10% RH wind 10 km/h | -10.8 | -10.8 | Passed |
| -5 C 10% RH wind 36 km/h | -15.9 | -15.9 | Passed |
| -5 C 10% RH wind 72 km/h | -22.9 | -22.9 | Passed |
| -5 C 50% RH wind 0 km/h | -8.3 | -8.3 | Passed |
| -5 C 50% RH wind 10 km/h | -10.2 | -10.2 | Passed |
| 12 C 10% RH wind 0 km/h | 8.5 | 8.5 | Passed |
| 12 C 50% RH wind 0 km/h | 10.4 | 10.3 | Failed |
SHA-256 / c4b49ee7e058f5ddbe331df26f9ef6d0a7d6d2e7091d0f401e1cd3d60425afba
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(ta, rh, wind_kmh):
e = rh / 100.0 * 6.105 * math.exp(17.27 * ta / (273.15 + ta))
ws = wind_kmh / 3.6
at = ta + 0.33 * e - 0.70 * ws - 4.00
return round(at, 1)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['-5 C 10% RH wind 0 km/h', [-5, 10, 0], -8.9], ['-5 C 10% RH wind 10 km/h', [-5, 10, 10], -10.8], ['-5 C 10% RH wind 36 km/h', [-5, 10, 36], -15.9], ['-5 C 10% RH wind 72 km/h', [-5, 10, 72], -22.9], ['-5 C 50% RH wind 0 km/h', [-5, 50, 0], -8.3], ['-5 C 50% RH wind 10 km/h', [-5, 50, 10], -10.2], ['12 C 10% RH wind 0 km/h', [12, 10, 0], 8.5], ['12 C 50% RH wind 0 km/h', [12, 50, 0], 10.3]], [['-5 C 50% RH wind 72 km/h', [-5, 50, 72], -22.3], ['-5 C 90% RH wind 0 km/h', [-5, 90, 0], -7.7], ['-5 C 90% RH wind 10 km/h', [-5, 90, 10], -9.7], ['-5 C 90% RH wind 36 km/h', [-5, 90, 36], -14.7], ['-5 C 90% RH wind 72 km/h', [-5, 90, 72], -21.7], ['0 C 10% RH wind 0 km/h', [0, 10, 0], -3.8], ['12 C 50% RH wind 36 km/h', [12, 50, 36], 3.3], ['12 C 50% RH wind 72 km/h', [12, 50, 72], -3.7]], [['0 C 10% RH wind 36 km/h', [0, 10, 36], -10.8], ['0 C 10% RH wind 72 km/h', [0, 10, 72], -17.8], ['0 C 50% RH wind 0 km/h', [0, 50, 0], -3.0], ['0 C 50% RH wind 10 km/h', [0, 50, 10], -4.9], ['0 C 50% RH wind 36 km/h', [0, 50, 36], -10.0], ['0 C 50% RH wind 72 km/h', [0, 50, 72], -17.0], ['12 C 90% RH wind 36 km/h', [12, 90, 36], 5.2], ['12 C 90% RH wind 72 km/h', [12, 90, 72], -1.8]], [['0 C 90% RH wind 10 km/h', [0, 90, 10], -4.1], ['0 C 90% RH wind 36 km/h', [0, 90, 36], -9.2], ['0 C 90% RH wind 72 km/h', [0, 90, 72], -16.2], ['12 C 10% RH wind 0 km/h', [12, 10, 0], 8.5], ['12 C 10% RH wind 10 km/h', [12, 10, 10], 6.5], ['12 C 10% RH wind 36 km/h', [12, 10, 36], 1.5], ['20 C 10% RH wind 10 km/h', [20, 10, 10], 14.8], ['20 C 50% RH wind 0 km/h', [20, 50, 0], 19.8]], [['12 C 50% RH wind 0 km/h', [12, 50, 0], 10.3], ['12 C 50% RH wind 10 km/h', [12, 50, 10], 8.4], ['12 C 50% RH wind 36 km/h', [12, 50, 36], 3.3], ['12 C 50% RH wind 72 km/h', [12, 50, 72], -3.7], ['12 C 90% RH wind 0 km/h', [12, 90, 0], 12.2], ['12 C 90% RH wind 10 km/h', [12, 90, 10], 10.2], ['20 C 50% RH wind 0 km/h', [20, 50, 0], 19.8], ['20 C 90% RH wind 10 km/h', [20, 90, 10], 21.0]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| -5 C 10% RH wind 0 km/h | -8.9 | -8.9 | Passed |
| -5 C 10% RH wind 10 km/h | -10.8 | -10.8 | Passed |
| -5 C 10% RH wind 36 km/h | -15.9 | -15.9 | Passed |
| -5 C 10% RH wind 72 km/h | -22.9 | -22.9 | Passed |
| -5 C 50% RH wind 0 km/h | -8.3 | -8.3 | Passed |
| -5 C 50% RH wind 10 km/h | -10.2 | -10.2 | Passed |
| 12 C 10% RH wind 0 km/h | 8.4 | 8.5 | Failed |
| 12 C 50% RH wind 0 km/h | 10.1 | 10.3 | Failed |
SHA-256 / 233bd31561a2efef47c896d0948189e153e34cb229be3732049a0f475f7fa710
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(ta, rh, wind_kmh):
e = rh / 100.0 * 6.105 * math.exp(17.27 * ta / (237.7 + ta))
ws = wind_kmh / 3.6
at = ta + 0.33 * e - 0.70 * ws - 4.00
return round(at, 1)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['-5 C 10% RH wind 0 km/h', [-5, 10, 0], -8.9], ['-5 C 10% RH wind 10 km/h', [-5, 10, 10], -10.8], ['-5 C 10% RH wind 36 km/h', [-5, 10, 36], -15.9], ['-5 C 10% RH wind 72 km/h', [-5, 10, 72], -22.9], ['-5 C 50% RH wind 0 km/h', [-5, 50, 0], -8.3], ['-5 C 50% RH wind 10 km/h', [-5, 50, 10], -10.2], ['12 C 10% RH wind 0 km/h', [12, 10, 0], 8.5], ['12 C 50% RH wind 0 km/h', [12, 50, 0], 10.3]], [['-5 C 50% RH wind 72 km/h', [-5, 50, 72], -22.3], ['-5 C 90% RH wind 0 km/h', [-5, 90, 0], -7.7], ['-5 C 90% RH wind 10 km/h', [-5, 90, 10], -9.7], ['-5 C 90% RH wind 36 km/h', [-5, 90, 36], -14.7], ['-5 C 90% RH wind 72 km/h', [-5, 90, 72], -21.7], ['0 C 10% RH wind 0 km/h', [0, 10, 0], -3.8], ['12 C 50% RH wind 36 km/h', [12, 50, 36], 3.3], ['12 C 50% RH wind 72 km/h', [12, 50, 72], -3.7]], [['0 C 10% RH wind 36 km/h', [0, 10, 36], -10.8], ['0 C 10% RH wind 72 km/h', [0, 10, 72], -17.8], ['0 C 50% RH wind 0 km/h', [0, 50, 0], -3.0], ['0 C 50% RH wind 10 km/h', [0, 50, 10], -4.9], ['0 C 50% RH wind 36 km/h', [0, 50, 36], -10.0], ['0 C 50% RH wind 72 km/h', [0, 50, 72], -17.0], ['12 C 90% RH wind 36 km/h', [12, 90, 36], 5.2], ['12 C 90% RH wind 72 km/h', [12, 90, 72], -1.8]], [['0 C 90% RH wind 10 km/h', [0, 90, 10], -4.1], ['0 C 90% RH wind 36 km/h', [0, 90, 36], -9.2], ['0 C 90% RH wind 72 km/h', [0, 90, 72], -16.2], ['12 C 10% RH wind 0 km/h', [12, 10, 0], 8.5], ['12 C 10% RH wind 10 km/h', [12, 10, 10], 6.5], ['12 C 10% RH wind 36 km/h', [12, 10, 36], 1.5], ['20 C 10% RH wind 10 km/h', [20, 10, 10], 14.8], ['20 C 50% RH wind 0 km/h', [20, 50, 0], 19.8]], [['12 C 50% RH wind 0 km/h', [12, 50, 0], 10.3], ['12 C 50% RH wind 10 km/h', [12, 50, 10], 8.4], ['12 C 50% RH wind 36 km/h', [12, 50, 36], 3.3], ['12 C 50% RH wind 72 km/h', [12, 50, 72], -3.7], ['12 C 90% RH wind 0 km/h', [12, 90, 0], 12.2], ['12 C 90% RH wind 10 km/h', [12, 90, 10], 10.2], ['20 C 50% RH wind 0 km/h', [20, 50, 0], 19.8], ['20 C 90% RH wind 10 km/h', [20, 90, 10], 21.0]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| -5 C 10% RH wind 0 km/h | -8.9 | -8.9 | Passed |
| -5 C 10% RH wind 10 km/h | -10.8 | -10.8 | Passed |
| -5 C 10% RH wind 36 km/h | -15.9 | -15.9 | Passed |
| -5 C 10% RH wind 72 km/h | -22.9 | -22.9 | Passed |
| -5 C 50% RH wind 0 km/h | -8.3 | -8.3 | Passed |
| -5 C 50% RH wind 10 km/h | -10.2 | -10.2 | Passed |
| 12 C 10% RH wind 0 km/h | 8.5 | 8.5 | Passed |
| 12 C 50% RH wind 0 km/h | 10.3 | 10.3 | Passed |
SHA-256 / 845d5e9670934f9c8e0719b0fc442ff38338f7f7f8328704812a523809d9b72e
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:25.317668+00:00.
Case digest / 09848088f671c2eefd9b0738d27d363345a70e1b42ce08a888a2467bad6f3619