diff --git a/domain/modelling/simulation.py b/domain/modelling/simulation.py index c39d960e..deafd66c 100644 --- a/domain/modelling/simulation.py +++ b/domain/modelling/simulation.py @@ -22,6 +22,10 @@ class BuildingPartOverlay: """ wall_insulation_type: Optional[int] = None + # Added solid-wall insulation depth (mm) — drives the calculator's Table 6 + # bucket / §5.8 documentary U-value for EWI (`wall_insulation_type=1`) and + # IWI (`wall_insulation_type=3`); λ defaults to 0.04 W/m·K in the calculator. + wall_insulation_thickness: Optional[int] = None roof_insulation_thickness: Optional[int] = None floor_insulation_thickness: Optional[int] = None floor_insulation_type_str: Optional[str] = None diff --git a/tests/domain/modelling/fixtures/solid_brick_ewi_001431_after.pdf b/tests/domain/modelling/fixtures/solid_brick_ewi_001431_after.pdf new file mode 100644 index 00000000..566c1e79 Binary files /dev/null and b/tests/domain/modelling/fixtures/solid_brick_ewi_001431_after.pdf differ diff --git a/tests/domain/modelling/fixtures/solid_brick_ewi_001431_before.pdf b/tests/domain/modelling/fixtures/solid_brick_ewi_001431_before.pdf new file mode 100644 index 00000000..3d0c7396 Binary files /dev/null and b/tests/domain/modelling/fixtures/solid_brick_ewi_001431_before.pdf differ diff --git a/tests/domain/modelling/fixtures/solid_brick_iwi_001431_after.pdf b/tests/domain/modelling/fixtures/solid_brick_iwi_001431_after.pdf new file mode 100644 index 00000000..9f135a34 Binary files /dev/null and b/tests/domain/modelling/fixtures/solid_brick_iwi_001431_after.pdf differ diff --git a/tests/domain/modelling/fixtures/solid_brick_iwi_001431_before.pdf b/tests/domain/modelling/fixtures/solid_brick_iwi_001431_before.pdf new file mode 100644 index 00000000..3d0c7396 Binary files /dev/null and b/tests/domain/modelling/fixtures/solid_brick_iwi_001431_before.pdf differ diff --git a/tests/domain/modelling/test_elmhurst_cascade_pins.py b/tests/domain/modelling/test_elmhurst_cascade_pins.py index dd53df22..b02d1f97 100644 --- a/tests/domain/modelling/test_elmhurst_cascade_pins.py +++ b/tests/domain/modelling/test_elmhurst_cascade_pins.py @@ -16,13 +16,16 @@ from __future__ import annotations from typing import Final -from datatypes.epc.domain.epc_property_data import EpcPropertyData +from datatypes.epc.domain.epc_property_data import ( + BuildingPartIdentifier, + EpcPropertyData, +) from domain.modelling.scoring.package_scorer import PackageScorer, Score from domain.modelling.product import Product from domain.modelling.recommendation import Recommendation from domain.modelling.generators.floor_recommendation import recommend_floor_insulation from domain.modelling.generators.roof_recommendation import recommend_loft_insulation -from domain.modelling.simulation import EpcSimulation +from domain.modelling.simulation import BuildingPartOverlay, EpcSimulation from domain.modelling.generators.wall_recommendation import recommend_cavity_wall from domain.sap10_calculator.calculator import Sap10Calculator, SapResult from repositories.product.product_repository import ProductRepository @@ -35,6 +38,14 @@ from tests.domain.modelling._elmhurst_recommendation import ( # e2e tolerance. _PIN_ABS: Final[float] = 1e-4 +# RdSAP wall_insulation_type codes for solid-wall insulation (Elmhurst +# Summary "E External" / "I Internal"); cf. domain/sap10_ml/rdsap_uvalues.py. +_WALL_INSULATION_EXTERNAL: Final[int] = 1 +_WALL_INSULATION_INTERNAL: Final[int] = 3 +# Recommended solid-wall insulation depth (mm); the calculator's λ default +# (0.04 W/m·K) matches Elmhurst's lodged thermal conductivity. +_SOLID_WALL_INSULATION_MM: Final[int] = 100 + class _AnyProduct(ProductRepository): """In-memory ProductRepository returning a fixed Product for any Measure @@ -83,6 +94,49 @@ def test_cavity_wall_overlay_reproduces_the_relodged_after() -> None: ) +def test_solid_brick_ewi_overlay_reproduces_the_relodged_after() -> None: + # Arrange — 100 mm external wall insulation on a solid-brick main wall. + before: EpcPropertyData = parse_recommendation_summary( + "solid_brick_ewi_001431_before.pdf" + ) + after: EpcPropertyData = parse_recommendation_summary( + "solid_brick_ewi_001431_after.pdf" + ) + overlay = EpcSimulation( + building_parts={ + BuildingPartIdentifier.MAIN: BuildingPartOverlay( + wall_insulation_type=_WALL_INSULATION_EXTERNAL, + wall_insulation_thickness=_SOLID_WALL_INSULATION_MM, + ) + } + ) + + # Act / Assert + _assert_overlay_reproduces_after(before, after, overlay) + + +def test_solid_brick_iwi_overlay_reproduces_the_relodged_after() -> None: + # Arrange — 100 mm internal wall insulation on a solid-brick main wall + # (also lowers the thermal-mass parameter, unlike EWI). + before: EpcPropertyData = parse_recommendation_summary( + "solid_brick_iwi_001431_before.pdf" + ) + after: EpcPropertyData = parse_recommendation_summary( + "solid_brick_iwi_001431_after.pdf" + ) + overlay = EpcSimulation( + building_parts={ + BuildingPartIdentifier.MAIN: BuildingPartOverlay( + wall_insulation_type=_WALL_INSULATION_INTERNAL, + wall_insulation_thickness=_SOLID_WALL_INSULATION_MM, + ) + } + ) + + # Act / Assert + _assert_overlay_reproduces_after(before, after, overlay) + + def test_loft_overlay_reproduces_the_relodged_after() -> None: # Arrange before: EpcPropertyData = parse_recommendation_summary(