mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
fix(rating): floor the continuous SAP score at 1 (SAP 10.2 §13 / RdSAP 10 §13)
The SAP rating is spec-floored at 1 ("if the result of the calculation is
less than 1, the rating is 1"). `sap_rating_integer` already clamps, but the
continuous `sap_score_continuous` did not — so a degenerate dwelling could
emit a physically impossible negative SAP. Apply the same max(1, …) floor to
the continuous value (the un-rounded part is for sensitivity near real
ratings, not for negative ratings).
Removes a -12.3 accuracy outlier on the committed corpus (cert 422000111926,
lodged at the floor of 1, was computing -11.3): within-0.5 70.2% -> 70.3%,
MAE 0.845 -> 0.833. Ratcheted the corpus MAE ceiling to 0.84. Unit-pinned in
test_calculator.
pyright not installed in this codespace (strict gate not run locally).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2e416a0221
commit
fc5f10ea92
3 changed files with 36 additions and 2 deletions
|
|
@ -617,7 +617,13 @@ def calculate_sap_from_inputs(inputs: CalculatorInputs) -> SapResult:
|
|||
)
|
||||
ecf = energy_cost_factor(total_cost_gbp=total_cost, total_floor_area_m2=tfa)
|
||||
sap_int = sap_rating_integer(ecf=ecf)
|
||||
sap_cont = sap_rating(ecf=ecf)
|
||||
# SAP 10.2 §13 / RdSAP 10 §13: the SAP rating is floored at 1 ("if the
|
||||
# result of the calculation is less than 1, the rating is 1"). Apply the
|
||||
# same floor to the continuous value so it stays a valid rating — the
|
||||
# un-rounded part is for sensitivity NEAR real ratings, not for emitting
|
||||
# a physically impossible negative SAP on a degenerate dwelling (e.g. a
|
||||
# cert lodged at the floor of 1). Mirrors `sap_rating_integer`'s max(1,…).
|
||||
sap_cont = max(1.0, sap_rating(ecf=ecf))
|
||||
co2_factor = inputs.co2_factor_kg_per_kwh
|
||||
# Per-end-use effective CO2 factors (Table 12d monthly cascade for
|
||||
# electricity, annual for gas). cert_to_inputs supplies these from
|
||||
|
|
|
|||
|
|
@ -270,6 +270,28 @@ def test_calculator_returns_twelve_month_breakdown_and_plausible_sap_score() ->
|
|||
)
|
||||
|
||||
|
||||
def test_sap_score_continuous_floored_at_1_for_degenerate_high_cost() -> None:
|
||||
# Arrange — SAP 10.2 §13 / RdSAP 10 §13: the SAP rating is floored at 1
|
||||
# ("if the result of the calculation is less than 1, the rating is 1").
|
||||
# Drive the cost so high that the raw ECF formula returns a negative SAP
|
||||
# (a degenerate dwelling, e.g. a cert lodged at the floor of 1); both the
|
||||
# integer AND the continuous score must clamp to 1 rather than emit a
|
||||
# physically impossible negative rating.
|
||||
inputs = replace(
|
||||
_baseline_inputs(),
|
||||
space_heating_fuel_cost_gbp_per_kwh=5.0,
|
||||
hot_water_fuel_cost_gbp_per_kwh=5.0,
|
||||
other_fuel_cost_gbp_per_kwh=5.0,
|
||||
)
|
||||
|
||||
# Act
|
||||
result = calculate_sap_from_inputs(inputs)
|
||||
|
||||
# Assert — raw SAP would be < 1 here; the floor holds on both outputs.
|
||||
assert result.sap_score == 1
|
||||
assert abs(result.sap_score_continuous - 1.0) <= 1e-9
|
||||
|
||||
|
||||
def test_calculate_exposes_dimensions_intermediates() -> None:
|
||||
# Arrange — P5 trace mode: `result.intermediate` must surface the
|
||||
# worksheet-named dimensions variables for per-section diffing
|
||||
|
|
|
|||
|
|
@ -150,8 +150,14 @@ _CORPUS = Path(
|
|||
# MAE 0.12 -> 0.08 t/yr (bias +0.04 -> 0.00). A prior session deferred enum 9
|
||||
# ("direction not understood") while the PE/CO2 lens was confounded by the
|
||||
# climate-cascade bug (fc7c4d2d); the corrected lens shows the over-rate.
|
||||
# SAP RATING FLOOR (SAP 10.2 §13 / RdSAP 10 §13): the rating is floored at 1
|
||||
# ("if the result is less than 1, the rating is 1"). `calculate_sap_from_inputs`
|
||||
# now applies that floor to the CONTINUOUS score too (was integer-only), so a
|
||||
# degenerate dwelling no longer emits a negative SAP. Removed a -12.3 outlier
|
||||
# (cert 422000111926, lodged at the floor of 1, was computing -11.3): within-0.5
|
||||
# 70.2% -> 70.3%, MAE 0.845 -> 0.833.
|
||||
_MIN_WITHIN_HALF_SAP = 0.70
|
||||
_MAX_SAP_MAE = 0.85
|
||||
_MAX_SAP_MAE = 0.84
|
||||
_MAX_CO2_MAE_TONNES = 0.09 # t CO2 / yr vs co2_emissions_current
|
||||
_MAX_PE_PER_M2_MAE = 4.0 # kWh / m2 / yr vs energy_consumption_current
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue