mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
feat(modelling): wire the ASHP bundle into the candidate pool
recommend_heating now receives planning_restrictions in the orchestrator (the ASHP planning gate); the ASHP bundle joins the free candidate pool for every house/bungalow. Catalogue + contingency (legacy 0.25) gain air_source_heat_pump; report.py _triggers_for explains the ASHP trigger; the harness forcing test covers it. Integration tests seed an air_source_heat_pump MaterialRow (ASHP fires on every house, the broadest trigger yet). NB the optimiser correctly does NOT select ASHP for an EPC-band goal — gas->electric does not improve the SAP cost-rating; ASHP is a CO2/PE measure, selectable once non-EPC goals land. ASHP bundle COMPLETE (S5-S7). ADR-0024. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a1fc697d93
commit
0f89845321
6 changed files with 46 additions and 2 deletions
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ _GENERATOR_MEASURE_TYPES = (
|
|||
"secondary_glazing",
|
||||
"low_energy_lighting",
|
||||
"high_heat_retention_storage_heaters",
|
||||
"air_source_heat_pump",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue