mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +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,
|
"secondary_glazing": 0.15,
|
||||||
"low_energy_lighting": 0.26,
|
"low_energy_lighting": 0.26,
|
||||||
"high_heat_retention_storage_heaters": 0.10,
|
"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,
|
"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 {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,6 @@
|
||||||
"double_glazing": { "unit_cost_per_m2": 600.0 },
|
"double_glazing": { "unit_cost_per_m2": 600.0 },
|
||||||
"secondary_glazing": { "unit_cost_per_m2": 510.0 },
|
"secondary_glazing": { "unit_cost_per_m2": 510.0 },
|
||||||
"low_energy_lighting": { "unit_cost_per_m2": 8.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_floor_insulation(effective_epc, products),
|
||||||
recommend_glazing(effective_epc, products, planning_restrictions),
|
recommend_glazing(effective_epc, products, planning_restrictions),
|
||||||
recommend_lighting(effective_epc, products),
|
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]
|
return [recommendation for recommendation in found if recommendation is not None]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ _GENERATOR_MEASURE_TYPES = (
|
||||||
"secondary_glazing",
|
"secondary_glazing",
|
||||||
"low_energy_lighting",
|
"low_energy_lighting",
|
||||||
"high_heat_retention_storage_heaters",
|
"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).
|
# the double-glazing Product (ADR-0022).
|
||||||
session.add_all(
|
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(
|
MaterialRow(
|
||||||
id=1,
|
id=1,
|
||||||
type="solid_floor_insulation",
|
type="solid_floor_insulation",
|
||||||
|
|
@ -244,6 +252,14 @@ def test_modelling_optimises_and_persists_a_multi_measure_plan(
|
||||||
)
|
)
|
||||||
session.add_all(
|
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(
|
MaterialRow(
|
||||||
id=1,
|
id=1,
|
||||||
type="cavity_wall_insulation",
|
type="cavity_wall_insulation",
|
||||||
|
|
@ -407,6 +423,14 @@ def test_modelling_recommends_nothing_when_already_at_the_target_band(
|
||||||
# nothing is ultimately selected.
|
# nothing is ultimately selected.
|
||||||
session.add_all(
|
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(
|
MaterialRow(
|
||||||
id=10,
|
id=10,
|
||||||
type="cavity_wall_insulation",
|
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.
|
# ventilation dependency, so every Product they reach for must exist.
|
||||||
session.add_all(
|
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(
|
MaterialRow(
|
||||||
id=1,
|
id=1,
|
||||||
type="external_wall_insulation",
|
type="external_wall_insulation",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue