FAILURE MAP
← Case archive

FA-70966 / Weather index computation / Open access

Livestock temperature-humidity index: classify after rounding · case 01

A reported THI of 72.0 is labelled none because the unrounded 71.97 was classified.

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

ROOT CAUSE

Bands are evaluated on the unrounded THI while the rounded value is reported.

VERIFIED REPAIR

Round THI to 0.1 before classification.

Unsuccessful approach: Truncating to 0.1 before classification reports values such as 71.9 for 71.97.

Case contract

Input barn temperature degC and RH percent. THI = (1.8T+32) - (0.55 - 0.0055RH)(1.8T - 26); THI is rounded to 0.1 first and the stress level is taken from the rounded value: <72 none, <79 mild, <89 moderate, <99 severe, else emergency.

Why this case matters

Dairy heat-stress alerts and cooling automation key off THI bands.

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):
    thi = (1.8 * t_c + 32) - (0.55 - 0.0055 * rh) * (1.8 * t_c - 26)
    if thi < 72:
        level = "none"
    elif thi < 79:
        level = "mild"
    elif thi < 89:
        level = "moderate"
    elif thi < 99:
        level = "severe"
    else:
        level = "emergency"
    return [round(thi, 1), level]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['15 C barn at 20% RH', [15, 20], [58.6, 'none']], ['15 C barn at 40% RH', [15, 40], [58.7, 'none']], ['15 C barn at 55% RH', [15, 55], [58.8, 'none']], ['15 C barn at 70% RH', [15, 70], [58.8, 'none']], ['15 C barn at 85% RH', [15, 85], [58.9, 'none']], ['15 C barn at 100% RH', [15, 100], [59.0, 'none']], ['21 C barn at 20% RH', [21, 20], [64.6, 'none']], ['THI just under 72 unrounded 22.2 C 100%', [22.2, 100], [72.0, 'mild']]], [['21 C barn at 40% RH', [21, 40], [65.9, 'none']], ['21 C barn at 55% RH', [21, 55], [66.9, 'none']], ['21 C barn at 70% RH', [21, 70], [67.9, 'none']], ['21 C barn at 85% RH', [21, 85], [68.8, 'none']], ['21 C barn at 100% RH', [21, 100], [69.8, 'none']], ['22 C barn at 20% RH', [22, 20], [65.6, 'none']], ['22 C barn at 85% RH', [22, 85], [70.5, 'none']], ['THI just under 72 unrounded 22.7 C 89%', [22.7, 89], [72.0, 'mild']]], [['22 C barn at 55% RH', [22, 55], [68.2, 'none']], ['22 C barn at 70% RH', [22, 70], [69.4, 'none']], ['22 C barn at 85% RH', [22, 85], [70.5, 'none']], ['22 C barn at 100% RH', [22, 100], [71.6, 'none']], ['24 C barn at 20% RH', [24, 20], [67.6, 'none']], ['24 C barn at 40% RH', [24, 40], [69.5, 'none']], ['26 C barn at 55% RH', [26, 55], [73.7, 'mild']], ['THI on 79 boundary 26.3 C 97%', [26.3, 97], [79.0, 'moderate']]], [['24 C barn at 70% RH', [24, 70], [72.4, 'mild']], ['24 C barn at 85% RH', [24, 85], [73.8, 'mild']], ['24 C barn at 100% RH', [24, 100], [75.2, 'mild']], ['25 C barn at 20% RH', [25, 20], [68.6, 'none']], ['25 C barn at 40% RH', [25, 40], [70.7, 'none']], ['25 C barn at 55% RH', [25, 55], [72.3, 'mild']], ['29 C barn at 20% RH', [29, 20], [72.7, 'mild']], ['THI just under 72 unrounded 22.3 C 98%', [22.3, 98], [72.0, 'mild']]], [['25 C barn at 85% RH', [25, 85], [75.4, 'mild']], ['25 C barn at 100% RH', [25, 100], [77.0, 'mild']], ['26 C barn at 20% RH', [26, 20], [69.6, 'none']], ['26 C barn at 40% RH', [26, 40], [71.9, 'none']], ['26 C barn at 55% RH', [26, 55], [73.7, 'mild']], ['26 C barn at 70% RH', [26, 70], [75.4, 'mild']], ['30 C barn at 55% RH', [30, 55], [79.1, 'moderate']], ['THI just under 72 unrounded 22.8 C 87%', [22.8, 87], [72.0, 'mild']]]]
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
15 C barn at 20% RH[58.6, 'none'][58.6, 'none']Passed
15 C barn at 40% RH[58.7, 'none'][58.7, 'none']Passed
15 C barn at 55% RH[58.8, 'none'][58.8, 'none']Passed
15 C barn at 70% RH[58.8, 'none'][58.8, 'none']Passed
15 C barn at 85% RH[58.9, 'none'][58.9, 'none']Passed
15 C barn at 100% RH[59.0, 'none'][59.0, 'none']Passed
21 C barn at 20% RH[64.6, 'none'][64.6, 'none']Passed
THI just under 72 unrounded 22.2 C 100%[72.0, 'none'][72.0, 'mild']Failed

SHA-256 / a1a3c993b8a193504e5efdcc7b043b50b095c4f5aee6253122f2c921246543f2

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):
    thi = (1.8 * t_c + 32) - (0.55 - 0.0055 * rh) * (1.8 * t_c - 26)
    thi = math.floor(thi * 10) / 10
    if thi < 72:
        level = "none"
    elif thi < 79:
        level = "mild"
    elif thi < 89:
        level = "moderate"
    elif thi < 99:
        level = "severe"
    else:
        level = "emergency"
    return [thi, level]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['15 C barn at 20% RH', [15, 20], [58.6, 'none']], ['15 C barn at 40% RH', [15, 40], [58.7, 'none']], ['15 C barn at 55% RH', [15, 55], [58.8, 'none']], ['15 C barn at 70% RH', [15, 70], [58.8, 'none']], ['15 C barn at 85% RH', [15, 85], [58.9, 'none']], ['15 C barn at 100% RH', [15, 100], [59.0, 'none']], ['21 C barn at 20% RH', [21, 20], [64.6, 'none']], ['THI just under 72 unrounded 22.2 C 100%', [22.2, 100], [72.0, 'mild']]], [['21 C barn at 40% RH', [21, 40], [65.9, 'none']], ['21 C barn at 55% RH', [21, 55], [66.9, 'none']], ['21 C barn at 70% RH', [21, 70], [67.9, 'none']], ['21 C barn at 85% RH', [21, 85], [68.8, 'none']], ['21 C barn at 100% RH', [21, 100], [69.8, 'none']], ['22 C barn at 20% RH', [22, 20], [65.6, 'none']], ['22 C barn at 85% RH', [22, 85], [70.5, 'none']], ['THI just under 72 unrounded 22.7 C 89%', [22.7, 89], [72.0, 'mild']]], [['22 C barn at 55% RH', [22, 55], [68.2, 'none']], ['22 C barn at 70% RH', [22, 70], [69.4, 'none']], ['22 C barn at 85% RH', [22, 85], [70.5, 'none']], ['22 C barn at 100% RH', [22, 100], [71.6, 'none']], ['24 C barn at 20% RH', [24, 20], [67.6, 'none']], ['24 C barn at 40% RH', [24, 40], [69.5, 'none']], ['26 C barn at 55% RH', [26, 55], [73.7, 'mild']], ['THI on 79 boundary 26.3 C 97%', [26.3, 97], [79.0, 'moderate']]], [['24 C barn at 70% RH', [24, 70], [72.4, 'mild']], ['24 C barn at 85% RH', [24, 85], [73.8, 'mild']], ['24 C barn at 100% RH', [24, 100], [75.2, 'mild']], ['25 C barn at 20% RH', [25, 20], [68.6, 'none']], ['25 C barn at 40% RH', [25, 40], [70.7, 'none']], ['25 C barn at 55% RH', [25, 55], [72.3, 'mild']], ['29 C barn at 20% RH', [29, 20], [72.7, 'mild']], ['THI just under 72 unrounded 22.3 C 98%', [22.3, 98], [72.0, 'mild']]], [['25 C barn at 85% RH', [25, 85], [75.4, 'mild']], ['25 C barn at 100% RH', [25, 100], [77.0, 'mild']], ['26 C barn at 20% RH', [26, 20], [69.6, 'none']], ['26 C barn at 40% RH', [26, 40], [71.9, 'none']], ['26 C barn at 55% RH', [26, 55], [73.7, 'mild']], ['26 C barn at 70% RH', [26, 70], [75.4, 'mild']], ['30 C barn at 55% RH', [30, 55], [79.1, 'moderate']], ['THI just under 72 unrounded 22.8 C 87%', [22.8, 87], [72.0, 'mild']]]]
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
15 C barn at 20% RH[58.5, 'none'][58.6, 'none']Failed
15 C barn at 40% RH[58.6, 'none'][58.7, 'none']Failed
15 C barn at 55% RH[58.7, 'none'][58.8, 'none']Failed
15 C barn at 70% RH[58.8, 'none'][58.8, 'none']Passed
15 C barn at 85% RH[58.9, 'none'][58.9, 'none']Passed
15 C barn at 100% RH[59.0, 'none'][59.0, 'none']Passed
21 C barn at 20% RH[64.6, 'none'][64.6, 'none']Passed
THI just under 72 unrounded 22.2 C 100%[71.9, 'none'][72.0, 'mild']Failed

SHA-256 / bb446d3f9fbed697302a4f85e3bb599e03348f135691f5c7fedf444f5613b0e4

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):
    thi = (1.8 * t_c + 32) - (0.55 - 0.0055 * rh) * (1.8 * t_c - 26)
    thi = round(thi, 1)
    if thi < 72:
        level = "none"
    elif thi < 79:
        level = "mild"
    elif thi < 89:
        level = "moderate"
    elif thi < 99:
        level = "severe"
    else:
        level = "emergency"
    return [thi, level]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['15 C barn at 20% RH', [15, 20], [58.6, 'none']], ['15 C barn at 40% RH', [15, 40], [58.7, 'none']], ['15 C barn at 55% RH', [15, 55], [58.8, 'none']], ['15 C barn at 70% RH', [15, 70], [58.8, 'none']], ['15 C barn at 85% RH', [15, 85], [58.9, 'none']], ['15 C barn at 100% RH', [15, 100], [59.0, 'none']], ['21 C barn at 20% RH', [21, 20], [64.6, 'none']], ['THI just under 72 unrounded 22.2 C 100%', [22.2, 100], [72.0, 'mild']]], [['21 C barn at 40% RH', [21, 40], [65.9, 'none']], ['21 C barn at 55% RH', [21, 55], [66.9, 'none']], ['21 C barn at 70% RH', [21, 70], [67.9, 'none']], ['21 C barn at 85% RH', [21, 85], [68.8, 'none']], ['21 C barn at 100% RH', [21, 100], [69.8, 'none']], ['22 C barn at 20% RH', [22, 20], [65.6, 'none']], ['22 C barn at 85% RH', [22, 85], [70.5, 'none']], ['THI just under 72 unrounded 22.7 C 89%', [22.7, 89], [72.0, 'mild']]], [['22 C barn at 55% RH', [22, 55], [68.2, 'none']], ['22 C barn at 70% RH', [22, 70], [69.4, 'none']], ['22 C barn at 85% RH', [22, 85], [70.5, 'none']], ['22 C barn at 100% RH', [22, 100], [71.6, 'none']], ['24 C barn at 20% RH', [24, 20], [67.6, 'none']], ['24 C barn at 40% RH', [24, 40], [69.5, 'none']], ['26 C barn at 55% RH', [26, 55], [73.7, 'mild']], ['THI on 79 boundary 26.3 C 97%', [26.3, 97], [79.0, 'moderate']]], [['24 C barn at 70% RH', [24, 70], [72.4, 'mild']], ['24 C barn at 85% RH', [24, 85], [73.8, 'mild']], ['24 C barn at 100% RH', [24, 100], [75.2, 'mild']], ['25 C barn at 20% RH', [25, 20], [68.6, 'none']], ['25 C barn at 40% RH', [25, 40], [70.7, 'none']], ['25 C barn at 55% RH', [25, 55], [72.3, 'mild']], ['29 C barn at 20% RH', [29, 20], [72.7, 'mild']], ['THI just under 72 unrounded 22.3 C 98%', [22.3, 98], [72.0, 'mild']]], [['25 C barn at 85% RH', [25, 85], [75.4, 'mild']], ['25 C barn at 100% RH', [25, 100], [77.0, 'mild']], ['26 C barn at 20% RH', [26, 20], [69.6, 'none']], ['26 C barn at 40% RH', [26, 40], [71.9, 'none']], ['26 C barn at 55% RH', [26, 55], [73.7, 'mild']], ['26 C barn at 70% RH', [26, 70], [75.4, 'mild']], ['30 C barn at 55% RH', [30, 55], [79.1, 'moderate']], ['THI just under 72 unrounded 22.8 C 87%', [22.8, 87], [72.0, 'mild']]]]
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
15 C barn at 20% RH[58.6, 'none'][58.6, 'none']Passed
15 C barn at 40% RH[58.7, 'none'][58.7, 'none']Passed
15 C barn at 55% RH[58.8, 'none'][58.8, 'none']Passed
15 C barn at 70% RH[58.8, 'none'][58.8, 'none']Passed
15 C barn at 85% RH[58.9, 'none'][58.9, 'none']Passed
15 C barn at 100% RH[59.0, 'none'][59.0, 'none']Passed
21 C barn at 20% RH[64.6, 'none'][64.6, 'none']Passed
THI just under 72 unrounded 22.2 C 100%[72.0, 'mild'][72.0, 'mild']Passed

SHA-256 / 4e438e7f49fa81cce69f326fde5945aaa726ca242cf01818f80ae7828f219897

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

Case digest / a10c1d13431d013cfc568570db1bfd79dec67812e1a2236e5479f8319921109c