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.
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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