mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
fabric measures for ECO
This commit is contained in:
parent
f182773b4b
commit
27f3f136c4
2 changed files with 75 additions and 11 deletions
|
|
@ -11,13 +11,23 @@ TYPICAL_MEASURE_TYPES = [
|
|||
WALL_INSULATION_MEASURES = ["internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"]
|
||||
ROOF_INSULATION_MEASURES = ["loft_insulation", "flat_roof_insulation", "room_roof_insulation"]
|
||||
|
||||
SPECIFIC_MEASURES = WALL_INSULATION_MEASURES + ROOF_INSULATION_MEASURES + [
|
||||
"suspended_floor_insulation", "solid_floor_insulation",
|
||||
"boiler_upgrade", "high_heat_retention_storage_heater", "air_source_heat_pump",
|
||||
"secondary_heating", "solar_pv", "double_glazing", "secondary_glazing",
|
||||
"ventilation", "low_energy_lighting", "fireplace", "hot_water_tank_insulation",
|
||||
"cylinder_thermostat"
|
||||
# Both all and roof insulaiton measures are eligible for ECO4. These are the remaining fabric and heating measures
|
||||
# This is based on th measures we have recommendations for
|
||||
ECO4_ELIGIBILE_FABRIC_MEASURES = [
|
||||
"suspended_floor_insulation", "solid_floor_insulation", "double_glazing", "secondary_glazing"
|
||||
]
|
||||
ECO4_ELIGIBLE_HEATING_MEASURES = [
|
||||
"boiler_upgrade", "high_heat_retention_storage_heater", "air_source_heat_pump", "solar_pv"
|
||||
]
|
||||
|
||||
SPECIFIC_MEASURES = (
|
||||
WALL_INSULATION_MEASURES + ROOF_INSULATION_MEASURES + ECO4_ELIGIBILE_FABRIC_MEASURES +
|
||||
ECO4_ELIGIBLE_HEATING_MEASURES + [
|
||||
"secondary_heating" "ventilation", "low_energy_lighting", "fireplace",
|
||||
"hot_water_tank_insulation",
|
||||
"cylinder_thermostat"
|
||||
]
|
||||
)
|
||||
|
||||
INSULATION_MEASURES = [
|
||||
"internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ from numpy import nan
|
|||
import datetime
|
||||
from copy import deepcopy
|
||||
|
||||
from app.plan.schemas import (
|
||||
WALL_INSULATION_MEASURES, ROOF_INSULATION_MEASURES, ECO4_ELIGIBILE_FABRIC_MEASURES, ECO4_ELIGIBLE_HEATING_MEASURES
|
||||
)
|
||||
from recommendations.optimiser.CostOptimiser import CostOptimiser
|
||||
from recommendations.optimiser.GainOptimiser import GainOptimiser
|
||||
from backend.Funding import Funding
|
||||
|
|
@ -472,7 +475,7 @@ def _find_measure(input_measures, measure_type):
|
|||
return False
|
||||
|
||||
|
||||
def _make_generic_eco4_funding_paths(p, input_measures, funding_paths, remaining_insulation_type):
|
||||
def _make_solar_heating_funding_paths(p, input_measures, funding_paths, remaining_insulation_type):
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Solar PV with existing eligible heating system
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
@ -624,7 +627,7 @@ def make_funding_paths(p, input_measures, tenure):
|
|||
):
|
||||
input_gbis_measures_innovation.append([measure])
|
||||
|
||||
funding_paths = _make_generic_eco4_funding_paths(
|
||||
funding_paths = _make_solar_heating_funding_paths(
|
||||
p, input_measures_innovation, funding_paths, remaining_insulation_type
|
||||
)
|
||||
|
||||
|
|
@ -653,7 +656,7 @@ def make_funding_paths(p, input_measures, tenure):
|
|||
ewi_or_iwi[0]["reference"] = "+".join(reference_measures) + ":eco4"
|
||||
funding_paths.append(ewi_or_iwi)
|
||||
|
||||
funding_paths = _make_generic_eco4_funding_paths(
|
||||
funding_paths = _make_solar_heating_funding_paths(
|
||||
p, input_measures, funding_paths, remaining_insulation_type
|
||||
)
|
||||
|
||||
|
|
@ -758,6 +761,24 @@ def violates_min_insulation(fixed):
|
|||
return is_heating and not has_insul
|
||||
|
||||
|
||||
# Treat "type" like "external_wall_insulation+mechanical_ventilation" → "external_wall_insulation"
|
||||
def _base_type(s: str) -> str:
|
||||
return s.split("+", 1)[0]
|
||||
|
||||
|
||||
def _filter_measures_by_types(input_measures, allowed_types):
|
||||
"""
|
||||
Keep only groups that have ≥1 allowed option; inside each group keep only allowed options.
|
||||
"""
|
||||
allowed_set = set(allowed_types)
|
||||
filtered = []
|
||||
for group in input_measures:
|
||||
kept_opts = [opt for opt in group if _base_type(opt["type"]) in allowed_set]
|
||||
if kept_opts:
|
||||
filtered.append(kept_opts)
|
||||
return filtered
|
||||
|
||||
|
||||
def optimise_with_funding_paths(input_measures, budget=None, target_gain=None, social=False):
|
||||
"""
|
||||
run_optimizer(sub_measures, budget, target_gain) -> (picked_options, sub_cost, sub_gain)
|
||||
|
|
@ -765,8 +786,40 @@ def optimise_with_funding_paths(input_measures, budget=None, target_gain=None, s
|
|||
|
||||
funding_paths = make_funding_paths(p, input_measures, body.housing_type)
|
||||
|
||||
# We now produce a fabric only path for ECO4
|
||||
# We add in generic insulation funding paths (where there is no fixed measure)
|
||||
# Heating controls are only eligible if installed as part of a heating upgrade and so we do not include them
|
||||
# here
|
||||
allowed_types = WALL_INSULATION_MEASURES + ROOF_INSULATION_MEASURES + ECO4_ELIGIBILE_FABRIC_MEASURES
|
||||
funding_paths = [{'AND': [], 'reference': 'fabric-only:eco4'}] + funding_paths
|
||||
|
||||
solutions = []
|
||||
for path_spec in funding_paths:
|
||||
|
||||
if path_spec["reference"] == "fabric-only:eco4":
|
||||
sub_measures = _filter_measures_by_types(input_measures, allowed_types)
|
||||
if not sub_measures:
|
||||
continue
|
||||
|
||||
picked, sub_cost, sub_gain = run_optimizer(
|
||||
sub_measures,
|
||||
budget=budget, # no fixed items; budget unchanged
|
||||
sub_target_gain=target_gain
|
||||
)
|
||||
|
||||
if picked is None:
|
||||
continue
|
||||
|
||||
solutions.append(
|
||||
{
|
||||
"fixed_ids": [],
|
||||
"items": picked,
|
||||
"total_cost": sub_cost,
|
||||
"total_gain": sub_gain,
|
||||
"path": path_spec,
|
||||
}
|
||||
)
|
||||
|
||||
# 1) expand fixed selections for this path
|
||||
fixed_selections = expand_funding_path(input_measures, path_spec) if path_spec else [[]]
|
||||
if not fixed_selections:
|
||||
|
|
@ -1031,8 +1084,9 @@ def run_optimizer(input_measures, budget=None, sub_target_gain=None, allow_slack
|
|||
else:
|
||||
if sub_target_gain is None:
|
||||
raise ValueError("Either budget or target_gain must be provided.")
|
||||
opt = CostOptimiser(input_measures, min_gain=sub_target_gain)
|
||||
opt = CostOptimiser(sub_measures, min_gain=sub_target_gain)
|
||||
|
||||
opt.setup()
|
||||
opt.solve()
|
||||
return opt.solution, opt.solution_cost, opt.solution_gain
|
||||
cost = sum([x["cost"] for x in opt.solution])
|
||||
return opt.solution, cost, opt.solution_gain
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue