mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Score gains sap_result: Optional[SapResult], populated by PackageScorer with the calculator output its headline figures came from. This lets the Modelling stage price the post-package (and baseline) end-state via Bill Derivation reusing a SapResult already computed by the optimiser's re-score / the orchestrator's baseline score — no second calculate (ADR-0014 amendment). The optimiser reads only sap_continuous, so it stays domain-agnostic and the stub scorers (which omit sap_result) are unaffected — all optimiser tests pass unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
"""Behaviour of the Package Scorer: composing Simulation Overlays onto a
|
|
baseline EpcPropertyData and scoring the result on the deterministic SAP10
|
|
calculator. The reusable compute primitive (ADR-0016). Elmhurst before/after
|
|
cascade pins land with #1154 once the cert parses; here we exercise the real
|
|
calculator on a hand-built EPD.
|
|
"""
|
|
|
|
from datatypes.epc.domain.epc_property_data import (
|
|
BuildingPartIdentifier,
|
|
EpcPropertyData,
|
|
)
|
|
from domain.modelling.scoring.package_scorer import PackageScorer, Score
|
|
from domain.modelling.simulation import BuildingPartOverlay, EpcSimulation
|
|
from domain.sap10_calculator.calculator import Sap10Calculator, SapResult
|
|
from tests.domain.sap10_calculator.worksheet._elmhurst_worksheet_000490 import (
|
|
build_epc,
|
|
)
|
|
|
|
_CAVITY_FILL = EpcSimulation(
|
|
building_parts={
|
|
BuildingPartIdentifier.MAIN: BuildingPartOverlay(wall_insulation_type=2)
|
|
}
|
|
)
|
|
|
|
|
|
def test_filling_the_main_cavity_improves_sap() -> None:
|
|
# Arrange
|
|
baseline: EpcPropertyData = build_epc() # MAIN: uninsulated cavity
|
|
scorer = PackageScorer(Sap10Calculator())
|
|
|
|
# Act
|
|
base: Score = scorer.score(baseline, [])
|
|
filled: Score = scorer.score(baseline, [_CAVITY_FILL])
|
|
|
|
# Assert
|
|
assert filled.sap_continuous > base.sap_continuous
|
|
|
|
|
|
def test_empty_package_scores_the_unmodified_baseline() -> None:
|
|
# Arrange
|
|
baseline: EpcPropertyData = build_epc()
|
|
calculator = Sap10Calculator()
|
|
direct: SapResult = calculator.calculate(baseline)
|
|
|
|
# Act
|
|
score: Score = PackageScorer(calculator).score(baseline, [])
|
|
|
|
# Assert
|
|
assert abs(score.sap_continuous - direct.sap_score_continuous) <= 1e-9
|
|
assert abs(score.co2_kg_per_yr - direct.co2_kg_per_yr) <= 1e-9
|
|
assert (
|
|
abs(score.primary_energy_kwh_per_yr - direct.primary_energy_kwh_per_yr)
|
|
<= 1e-9
|
|
)
|
|
|
|
|
|
def test_score_carries_the_scored_sap_result_for_billing() -> None:
|
|
# Arrange — the post-package SapResult must ride on the Score so Bill
|
|
# Derivation can price the simulated end-state without a second calculate
|
|
# (ADR-0014 amendment).
|
|
baseline: EpcPropertyData = build_epc()
|
|
scorer = PackageScorer(Sap10Calculator())
|
|
|
|
# Act
|
|
filled: Score = scorer.score(baseline, [_CAVITY_FILL])
|
|
|
|
# Assert — the SapResult is the one the Score's headline figures came from.
|
|
assert filled.sap_result is not None
|
|
assert (
|
|
abs(filled.sap_result.sap_score_continuous - filled.sap_continuous) <= 1e-9
|
|
)
|