diff --git a/domain/modelling/contingencies.py b/domain/modelling/contingencies.py index 8d011ffb..7114313d 100644 --- a/domain/modelling/contingencies.py +++ b/domain/modelling/contingencies.py @@ -19,6 +19,7 @@ _CONTINGENCY_RATES: dict[str, float] = { "secondary_glazing": 0.15, "low_energy_lighting": 0.26, "high_heat_retention_storage_heaters": 0.10, + "air_source_heat_pump": 0.25, } diff --git a/harness/report.py b/harness/report.py index f9459bc4..c93f0d02 100644 --- a/harness/report.py +++ b/harness/report.py @@ -144,6 +144,15 @@ def _triggers_for(epc: EpcPropertyData, measure_type: str) -> dict[str, Any]: ), "mains_gas": epc.sap_energy_source.mains_gas, } + if measure_type == "air_source_heat_pump": + # heating_recommendation.py offers ASHP to any non-flat house/bungalow + # not already a heat pump (eligibility is physical/planning only). + return { + "property_type": epc.property_type, + "main_heating_category": ( + epc.sap_heating.main_heating_details[0].main_heating_category + ), + } return {} diff --git a/harness/sample_catalogue.json b/harness/sample_catalogue.json index fb34e28b..0d17b2b6 100644 --- a/harness/sample_catalogue.json +++ b/harness/sample_catalogue.json @@ -11,5 +11,6 @@ "double_glazing": { "unit_cost_per_m2": 600.0 }, "secondary_glazing": { "unit_cost_per_m2": 510.0 }, "low_energy_lighting": { "unit_cost_per_m2": 8.0 }, - "high_heat_retention_storage_heaters": { "unit_cost_per_m2": 3500.0 } + "high_heat_retention_storage_heaters": { "unit_cost_per_m2": 3500.0 }, + "air_source_heat_pump": { "unit_cost_per_m2": 12000.0 } } diff --git a/orchestration/modelling_orchestrator.py b/orchestration/modelling_orchestrator.py index df375f65..4773b2f1 100644 --- a/orchestration/modelling_orchestrator.py +++ b/orchestration/modelling_orchestrator.py @@ -210,7 +210,7 @@ def _candidate_recommendations( recommend_floor_insulation(effective_epc, products), recommend_glazing(effective_epc, products, planning_restrictions), recommend_lighting(effective_epc, products), - recommend_heating(effective_epc, products), + recommend_heating(effective_epc, products, planning_restrictions), ) return [recommendation for recommendation in found if recommendation is not None] diff --git a/tests/harness/test_console.py b/tests/harness/test_console.py index 2f1d6bd5..a252b06a 100644 --- a/tests/harness/test_console.py +++ b/tests/harness/test_console.py @@ -35,6 +35,7 @@ _GENERATOR_MEASURE_TYPES = ( "secondary_glazing", "low_energy_lighting", "high_heat_retention_storage_heaters", + "air_source_heat_pump", ) diff --git a/tests/orchestration/test_ara_first_run_pipeline_integration.py b/tests/orchestration/test_ara_first_run_pipeline_integration.py index 96f2b0dd..49c77033 100644 --- a/tests/orchestration/test_ara_first_run_pipeline_integration.py +++ b/tests/orchestration/test_ara_first_run_pipeline_integration.py @@ -128,6 +128,14 @@ def test_first_run_baselines_through_repos_and_is_idempotent_on_rerun( # the double-glazing Product (ADR-0022). session.add_all( [ + MaterialRow( + id=5, + type="air_source_heat_pump", + total_cost=12000.0, + cost_unit="gbp_per_unit", + is_active=True, + description="Air source heat pump", + ), MaterialRow( id=1, type="solid_floor_insulation", @@ -244,6 +252,14 @@ def test_modelling_optimises_and_persists_a_multi_measure_plan( ) session.add_all( [ + MaterialRow( + id=5, + type="air_source_heat_pump", + total_cost=12000.0, + cost_unit="gbp_per_unit", + is_active=True, + description="Air source heat pump", + ), MaterialRow( id=1, type="cavity_wall_insulation", @@ -407,6 +423,14 @@ def test_modelling_recommends_nothing_when_already_at_the_target_band( # nothing is ultimately selected. session.add_all( [ + MaterialRow( + id=14, + type="air_source_heat_pump", + total_cost=12000.0, + cost_unit="gbp_per_unit", + is_active=True, + description="Air source heat pump", + ), MaterialRow( id=10, type="cavity_wall_insulation", @@ -548,6 +572,14 @@ def test_listed_uprn_ingested_blocks_solid_wall_insulation_in_modelling( # ventilation dependency, so every Product they reach for must exist. session.add_all( [ + MaterialRow( + id=5, + type="air_source_heat_pump", + total_cost=12000.0, + cost_unit="gbp_per_unit", + is_active=True, + description="Air source heat pump", + ), MaterialRow( id=1, type="external_wall_insulation",