FAILURE MAP
← Case archive

FA-70921 / Weather index computation / Open access

Humidex with discomfort band: vapour pressure offset · case 01

Humidex is about 5.5 degrees too high on every reading.

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

ROOT CAUSE

The 10 hPa reference vapour pressure is not subtracted.

VERIFIED REPAIR

Use 0.5555*(e - 10).

Unsuccessful approach: Subtracting the saturation constant 6.11 still leaves a positive bias.

Case contract

Input air temperature and dew point in degC. If dew point exceeds air temperature return None. e = 6.11*exp(5417.7530*(1/273.16 - 1/(273.15+Td))); H = T + 0.5555*(e - 10); report the humidex rounded half up to an integer and a band on that integer: <30 comfortable, 30-39 some discomfort, 40-45 great discomfort, 46-53 dangerous, >=54 heat stroke imminent.

Why this case matters

Summer heat bulletins publish humidex with a discomfort band.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(t_c, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 20 C dew point 28 C', [20, 28], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']]], [['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']], ['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']]], [['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']]], [['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 30 C dew point 20 C', [30, 20], [38, 'some discomfort']]]]
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
air 20 C dew point 5 C[25, 'comfortable'][19, 'comfortable']Failed
air 20 C dew point 12 C[28, 'comfortable'][22, 'comfortable']Failed
air 20 C dew point 18 C[32, 'some discomfort'][26, 'comfortable']Failed
air 20 C dew point 20 C[33, 'some discomfort'][28, 'comfortable']Failed
air 20 C dew point 22 CNoneNonePassed
air 20 C dew point 24 CNoneNonePassed
air 20 C dew point 26 CNoneNonePassed
air 20 C dew point 28 CNoneNonePassed

SHA-256 / 6c43bd6e5314867b97f11a6af6b95ab702a5b1b3279e192c89cb0df26e30ddfb

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, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e - 6.11)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 20 C dew point 28 C', [20, 28], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']]], [['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']], ['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']]], [['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']]], [['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 30 C dew point 20 C', [30, 20], [38, 'some discomfort']]]]
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
air 20 C dew point 5 C[21, 'comfortable'][19, 'comfortable']Failed
air 20 C dew point 12 C[24, 'comfortable'][22, 'comfortable']Failed
air 20 C dew point 18 C[28, 'comfortable'][26, 'comfortable']Failed
air 20 C dew point 20 C[30, 'some discomfort'][28, 'comfortable']Failed
air 20 C dew point 22 CNoneNonePassed
air 20 C dew point 24 CNoneNonePassed
air 20 C dew point 26 CNoneNonePassed
air 20 C dew point 28 CNoneNonePassed

SHA-256 / 93ebfe6c3c867bf96344418418d59e2fc50e04674d21e76e4d5753132f7e4980

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, td_c):
    if td_c > t_c:
        return None
    e = 6.11 * math.exp(5417.7530 * (1 / 273.16 - 1 / (273.15 + td_c)))
    h = t_c + 0.5555 * (e - 10.0)
    hx = int(math.floor(h + 0.5))
    if hx < 30:
        band = "comfortable"
    elif hx <= 39:
        band = "some discomfort"
    elif hx <= 45:
        band = "great discomfort"
    elif hx <= 53:
        band = "dangerous"
    else:
        band = "heat stroke imminent"
    return [hx, band]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['air 20 C dew point 5 C', [20, 5], [19, 'comfortable']], ['air 20 C dew point 12 C', [20, 12], [22, 'comfortable']], ['air 20 C dew point 18 C', [20, 18], [26, 'comfortable']], ['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 22 C', [20, 22], None], ['air 20 C dew point 24 C', [20, 24], None], ['air 20 C dew point 26 C', [20, 26], None], ['air 20 C dew point 28 C', [20, 28], None]], [['air 20 C dew point 20 C', [20, 20], [28, 'comfortable']], ['air 20 C dew point 28 C', [20, 28], None], ['air 20 C dew point 30 C', [20, 30], None], ['air 20 C dew point 36 C', [20, 36], None], ['air 20 C dew point 41 C', [20, 41], None], ['air 25 C dew point 5 C', [25, 5], [24, 'comfortable']], ['air 25 C dew point 12 C', [25, 12], [27, 'comfortable']], ['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']]], [['air 25 C dew point 18 C', [25, 18], [31, 'some discomfort']], ['air 25 C dew point 20 C', [25, 20], [33, 'some discomfort']], ['air 25 C dew point 22 C', [25, 22], [34, 'some discomfort']], ['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 26 C', [25, 26], None], ['air 25 C dew point 28 C', [25, 28], None], ['air 25 C dew point 30 C', [25, 30], None], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']]], [['air 25 C dew point 24 C', [25, 24], [36, 'some discomfort']], ['air 25 C dew point 41 C', [25, 41], None], ['air 28 C dew point 5 C', [28, 5], [27, 'comfortable']], ['air 28 C dew point 12 C', [28, 12], [30, 'some discomfort']], ['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 20 C', [28, 20], [36, 'some discomfort']], ['air 28 C dew point 22 C', [28, 22], [37, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']]], [['air 28 C dew point 18 C', [28, 18], [34, 'some discomfort']], ['air 28 C dew point 26 C', [28, 26], [41, 'great discomfort']], ['air 28 C dew point 28 C', [28, 28], [44, 'great discomfort']], ['air 28 C dew point 30 C', [28, 30], None], ['air 28 C dew point 36 C', [28, 36], None], ['air 28 C dew point 41 C', [28, 41], None], ['air 30 C dew point 5 C', [30, 5], [29, 'comfortable']], ['air 30 C dew point 20 C', [30, 20], [38, 'some discomfort']]]]
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
air 20 C dew point 5 C[19, 'comfortable'][19, 'comfortable']Passed
air 20 C dew point 12 C[22, 'comfortable'][22, 'comfortable']Passed
air 20 C dew point 18 C[26, 'comfortable'][26, 'comfortable']Passed
air 20 C dew point 20 C[28, 'comfortable'][28, 'comfortable']Passed
air 20 C dew point 22 CNoneNonePassed
air 20 C dew point 24 CNoneNonePassed
air 20 C dew point 26 CNoneNonePassed
air 20 C dew point 28 CNoneNonePassed

SHA-256 / 7c092745fe06886276c81493e203473a8be9e66193d4c03e840015e5163bf0e7

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.067723+00:00.

Case digest / f6bee5397c01a8be881b91bfce176e6270ce04d1d2709eb0701f8dca0f4a1f70