mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
test(modelling): secondary-heating-removal cascade validation (ADR-0028)
Two cascade tests on the worksheet-pinned 001431 build_epc() (the user's before/after Summary PDFs trip the documented 001431 window-extraction bug, so the repo's sanctioned 001431 baseline is used instead): - electric-storage main (code 402) + secondary 691: removal reproduces the secondary-removed cert at delta 0 — RdSAP §A.2.2 re-forces a default secondary, matching the user's F35→F35 example; - gas combi main (code 104) + secondary 691: removal strictly raises SAP (74.22→77.61) — the Table 11 fraction reallocates to the cheaper main. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f9a89a8e11
commit
797b71cd13
1 changed files with 70 additions and 0 deletions
|
|
@ -14,6 +14,7 @@ is a named generator/overlay/calculator gap to fix, never a tolerance to widen
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
from dataclasses import replace
|
||||
from typing import Final
|
||||
|
||||
|
|
@ -46,6 +47,9 @@ from domain.modelling.generators.solid_wall_recommendation import (
|
|||
from domain.modelling.generators.glazing_recommendation import recommend_glazing
|
||||
from domain.modelling.generators.lighting_recommendation import recommend_lighting
|
||||
from domain.modelling.generators.heating_recommendation import recommend_heating
|
||||
from domain.modelling.generators.secondary_heating_recommendation import (
|
||||
recommend_secondary_heating_removal,
|
||||
)
|
||||
from domain.modelling.scoring.overlay_applicator import apply_simulations
|
||||
from domain.modelling.recommendation import MeasureOption
|
||||
from domain.sap10_calculator.calculator import Sap10Calculator, SapResult
|
||||
|
|
@ -53,6 +57,17 @@ from repositories.product.product_repository import ProductRepository
|
|||
from tests.domain.modelling._elmhurst_recommendation import (
|
||||
parse_recommendation_summary,
|
||||
)
|
||||
from tests.domain.sap10_calculator.worksheet._elmhurst_worksheet_001431 import (
|
||||
build_epc as build_001431_epc,
|
||||
)
|
||||
|
||||
# RdSAP §A.2.2 forces a secondary system for electric-storage mains; SAP code
|
||||
# 402 (slimline storage) is in that set. Code 104 (a gas combi boiler) is not.
|
||||
_ELECTRIC_STORAGE_MAIN_CODE: Final[int] = 402
|
||||
_STANDARD_ELECTRICITY_FUEL: Final[int] = 30
|
||||
# SAP 10.2 Table 4a code 691 — electric panel/convector/radiant heaters, the
|
||||
# fixed secondary the user's example cert lodges.
|
||||
_SECONDARY_ELECTRIC_PANEL_CODE: Final[int] = 691
|
||||
|
||||
# Pin tolerance: the Summary PDFs are deterministic test vectors, so the
|
||||
# overlay must reproduce the re-lodged cert exactly. Matches the worksheet
|
||||
|
|
@ -1122,3 +1137,58 @@ def test_solar_before_baselines() -> None:
|
|||
|
||||
# Act / Assert — currently raises MissingMainFuelType.
|
||||
Sap10Calculator().calculate(before)
|
||||
|
||||
|
||||
# --- Secondary Heating Removal (ADR-0028) ----------------------------------
|
||||
# The user's Elmhurst before/after Summary for this measure (cert 001431,
|
||||
# electric-storage main + secondary 691) cannot be parsed — that PDF export
|
||||
# trips the documented 001431 Summary window-extraction bug. So these pins use
|
||||
# the worksheet-pinned `build_epc()` (a validated real-001431 representation,
|
||||
# the repo's sanctioned 001431 baseline) with the secondary configuration set on
|
||||
# it, exercising the real generator → overlay → calculator cascade.
|
||||
|
||||
|
||||
def test_secondary_removal_on_an_electric_storage_main_is_a_no_op() -> None:
|
||||
# Arrange — 001431 recast to an electric-storage main (SAP code 402, fuel 30)
|
||||
# with a lodged secondary (691). RdSAP §A.2.2 forces a default secondary back
|
||||
# on storage mains, so removal reproduces the after at delta 0 — exactly why
|
||||
# the user's before/after Summaries both print SAP F35.
|
||||
before: EpcPropertyData = build_001431_epc()
|
||||
main = before.sap_heating.main_heating_details[0]
|
||||
main.sap_main_heating_code = _ELECTRIC_STORAGE_MAIN_CODE
|
||||
main.main_fuel_type = _STANDARD_ELECTRICITY_FUEL
|
||||
main.main_heating_index_number = None
|
||||
before.sap_heating.secondary_heating_type = _SECONDARY_ELECTRIC_PANEL_CODE
|
||||
after: EpcPropertyData = copy.deepcopy(before)
|
||||
after.sap_heating.secondary_heating_type = None
|
||||
after.sap_heating.secondary_fuel_type = None
|
||||
recommendation: Recommendation | None = recommend_secondary_heating_removal(
|
||||
before, _AnyProduct()
|
||||
)
|
||||
assert recommendation is not None
|
||||
|
||||
# Act / Assert — the overlay reproduces the secondary-removed cert at delta 0.
|
||||
_assert_overlay_reproduces_after(
|
||||
before, after, recommendation.options[0].overlay
|
||||
)
|
||||
|
||||
|
||||
def test_secondary_removal_on_a_non_forced_main_raises_sap() -> None:
|
||||
# Arrange — 001431's lodged gas combi (SAP code 104, NOT a forced-secondary
|
||||
# main) with an added electric secondary (691). Removing it reallocates the
|
||||
# Table 11 secondary fraction to the cheaper gas main, so cost-based SAP rises
|
||||
# (the value path the forced-secondary example can't exercise).
|
||||
before: EpcPropertyData = build_001431_epc()
|
||||
before.sap_heating.secondary_heating_type = _SECONDARY_ELECTRIC_PANEL_CODE
|
||||
recommendation: Recommendation | None = recommend_secondary_heating_removal(
|
||||
before, _AnyProduct()
|
||||
)
|
||||
assert recommendation is not None
|
||||
scorer = PackageScorer(Sap10Calculator())
|
||||
|
||||
# Act
|
||||
with_secondary: Score = scorer.score(before, [])
|
||||
removed: Score = scorer.score(before, [recommendation.options[0].overlay])
|
||||
|
||||
# Assert — removal strictly raises SAP (delta well above the pin tolerance).
|
||||
assert removed.sap_continuous - with_secondary.sap_continuous > _PIN_ABS
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue