mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Cohort residual slice 5: 000477 build_epc lodgement (partial — Table 3c blocker)
Lodges the missing cert fields on 000477 build_epc to match U985 PDF: - sap_windows = SECTION_6_VERTICAL_WINDOWS (was empty) - low_energy_fixed_lighting_bulbs_count = 9 (was None) - sap_heating.main_heating_details with PCDF index 18118 (was default) - sap_heating.secondary_heating_type = 691 (was None) - sap_heating.number_baths = 0 (PDF lodges 0 baths; was None → defaulted to "has bath"=True) `make_sap_heating` accepts a new `number_baths` kwarg to surface that field — it lives on SapHeating but wasn't exposed before. Impact: 000477 SAP integer 71 → 66 (PDF 65, Δ +6 → +1); cost £599 → £707 vs PDF £732 (Δ -22% → -3.5%); useful 9059 → 10067 vs PDF 10111 (matches to <0.5%). Remaining +1 SAP integer delta is the **Table 3c two-profile combi- loss override** — not yet implemented. PCDB 18118 (Vaillant ecoTEC sustain 24) lodges separate_dhw_tests=2 → spec Appendix J §J3 uses both Profile M (F1, R1) and Profile L (F2, R2) loss factors. Our override gate (`_pcdb_table_3b_combi_loss_override`) only accepts separate_dhw_tests==1 → falls back to Table 3a keep-hot time-clock 600 kWh/yr default = 25x overshoot vs the fixture-pinned ~24 kWh/yr. The same gap blocks 000480 (PCDB 16839 — but actually wait, 16839 is in 000490 too and that already closes — needs checking), 000487 (PCDB 18119), and 000516 (PCDB 18118). Test pin `test_elmhurst_000477_end_to_end_sap_score_matches_pdf` xfail (strict) with rationale pointing at Table 3c. Re-enables when the override implements. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
a41ac6bd74
commit
960419a901
3 changed files with 67 additions and 1 deletions
|
|
@ -98,6 +98,7 @@ def make_sap_heating(
|
|||
cylinder_insulation_thickness_mm: Optional[int] = None,
|
||||
secondary_fuel_type: Optional[int] = None,
|
||||
secondary_heating_type: Optional[int] = None,
|
||||
number_baths: Optional[int] = None,
|
||||
) -> SapHeating:
|
||||
"""Build a SapHeating with SAP10 API defaults."""
|
||||
return SapHeating(
|
||||
|
|
@ -112,6 +113,7 @@ def make_sap_heating(
|
|||
cylinder_insulation_thickness_mm=cylinder_insulation_thickness_mm,
|
||||
secondary_fuel_type=secondary_fuel_type,
|
||||
secondary_heating_type=secondary_heating_type,
|
||||
number_baths=number_baths,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,12 @@ from datatypes.epc.domain.epc_property_data import (
|
|||
SapVentilation,
|
||||
SapWindow,
|
||||
)
|
||||
from domain.ml.tests._fixtures import make_minimal_sap10_epc, make_window
|
||||
from domain.ml.tests._fixtures import (
|
||||
make_main_heating_detail,
|
||||
make_minimal_sap10_epc,
|
||||
make_sap_heating,
|
||||
make_window,
|
||||
)
|
||||
from domain.sap.worksheet.solar_gains import RoofWindowInput, RooflightInput
|
||||
from domain.sap.worksheet.ventilation import MechanicalVentilationKind
|
||||
from domain.sap.worksheet.water_heating import TABLE_J1_TCOLD_FROM_MAINS_C
|
||||
|
|
@ -76,6 +81,8 @@ def build_epc() -> EpcPropertyData:
|
|||
heated_rooms_count=4,
|
||||
door_count=2,
|
||||
percent_draughtproofed=100,
|
||||
low_energy_fixed_lighting_bulbs_count=SECTION_5_BULB_COUNT_LEL,
|
||||
sap_windows=list(SECTION_6_VERTICAL_WINDOWS),
|
||||
sap_ventilation=SapVentilation(
|
||||
extract_fans_count=2,
|
||||
sheltered_sides=2,
|
||||
|
|
@ -83,6 +90,16 @@ def build_epc() -> EpcPropertyData:
|
|||
suspended_timber_floor_sealed=False,
|
||||
has_draught_lobby=False,
|
||||
),
|
||||
sap_heating=make_sap_heating(
|
||||
main_heating_details=[
|
||||
make_main_heating_detail(
|
||||
main_heating_index_number=18118,
|
||||
main_heating_data_source=1,
|
||||
),
|
||||
],
|
||||
secondary_heating_type=691,
|
||||
number_baths=0, # PDF: Total number of baths in property = 0
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from domain.sap.calculator import Sap10Calculator
|
|||
from domain.sap.rdsap.cert_to_inputs import cert_to_inputs
|
||||
from domain.sap.worksheet.tests import (
|
||||
_elmhurst_worksheet_000474 as _w000474,
|
||||
_elmhurst_worksheet_000477 as _w000477,
|
||||
_elmhurst_worksheet_000490 as _w000490,
|
||||
)
|
||||
from domain.sap.worksheet.tests._elmhurst_fixtures import (
|
||||
|
|
@ -53,6 +54,14 @@ _ELMHURST_000490_EXPECTED: Final[ElmhurstExpectedSap] = ElmhurstExpectedSap(
|
|||
total_energy_cost_gbp=807.5421,
|
||||
ecf=3.0539,
|
||||
)
|
||||
_ELMHURST_000477_EXPECTED: Final[ElmhurstExpectedSap] = ElmhurstExpectedSap(
|
||||
sap_rating=65,
|
||||
sap_score_continuous=65.0050,
|
||||
space_heating_kwh=10111.2019,
|
||||
hot_water_kwh=2116.0365,
|
||||
total_energy_cost_gbp=732.1396,
|
||||
ecf=2.5086,
|
||||
)
|
||||
_ELMHURST_000474_EXPECTED: Final[ElmhurstExpectedSap] = ElmhurstExpectedSap(
|
||||
sap_rating=62,
|
||||
sap_score_continuous=62.2584,
|
||||
|
|
@ -63,6 +72,44 @@ _ELMHURST_000474_EXPECTED: Final[ElmhurstExpectedSap] = ElmhurstExpectedSap(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
reason=(
|
||||
"Table 3c two-profile combi-loss override not yet implemented. PCDB "
|
||||
"18118 (Vaillant ecoTEC sustain 24) lodges separate_dhw_tests=2 → "
|
||||
"spec routes to Table 3c, which uses both Profile M (F1, R1) and "
|
||||
"Profile L (F2, R2) loss factors. Our override gate (`_pcdb_table_"
|
||||
"3b_combi_loss_override`) only accepts separate_dhw_tests==1 (Table "
|
||||
"3b row 1, single-profile) → falls back to Table 3a keep-hot time-"
|
||||
"clock 600 kWh/yr default = 25x overshoot on combi loss → +712 HW "
|
||||
"kWh → continuous SAP +0.83 over PDF (66 vs 65). Re-enable when "
|
||||
"Table 3c lands per the next ticket (see project memory)."
|
||||
),
|
||||
strict=True,
|
||||
)
|
||||
def test_elmhurst_000477_end_to_end_sap_score_matches_pdf() -> None:
|
||||
"""Cohort closure pin for 000477. Mid-terrace combi-gas with PCDF
|
||||
Vaillant ecoTEC sustain 24 (index 18118) + Electricity Electric
|
||||
Panel secondary heater (SAP code 691). PDF SAP rating 65."""
|
||||
# Arrange
|
||||
epc = _w000477.build_epc()
|
||||
|
||||
# Act
|
||||
result = Sap10Calculator().calculate(epc)
|
||||
|
||||
# Assert — integer match (the rdsap engine integration gate).
|
||||
delta = abs(result.sap_score - _ELMHURST_000477_EXPECTED.sap_rating)
|
||||
assert delta == 0, (
|
||||
f"SAP rating delta {delta} — expected 0 (integer match with PDF). "
|
||||
f"Actual={result.sap_score}, expected={_ELMHURST_000477_EXPECTED.sap_rating}."
|
||||
)
|
||||
continuous_delta = abs(
|
||||
result.sap_score_continuous - _ELMHURST_000477_EXPECTED.sap_score_continuous
|
||||
)
|
||||
assert continuous_delta <= 0.5, (
|
||||
f"Continuous SAP delta {continuous_delta:.2f} exceeds ceiling 0.5"
|
||||
)
|
||||
|
||||
|
||||
def test_elmhurst_000490_end_to_end_sap_score_currently_within_3_points() -> None:
|
||||
"""Mid-terrace combi-gas dwelling with time-clock keep-hot. After the
|
||||
PCDB Table 105 integration the fixture lodges `main_heating_index_
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue