Implementing HHRSH upgrade EPC D projects for ECO4

This commit is contained in:
Khalim Conn-Kowlessar 2025-11-07 18:42:17 +00:00
parent 4640fa77bf
commit 3edf5549af
2 changed files with 48 additions and 24 deletions

View file

@ -915,22 +915,6 @@ async def model_engine(body: PlanTriggerRequest):
work_package=eco_packages[p.id][2]
)
# Given the solutions we select the optimal one
# 1) If the scheme is ECO4, the full project funding and uplift are deducted from the cost
# 2) If the sheme is GBIS, the partial project funding and uplift are deducted from the cost
# 3) Otherwise, no funding is deducted from the cost
solutions["cost_less_full_project_funding"] = np.where(
solutions["scheme"] == "none",
solutions["total_cost"],
np.where(
solutions["scheme"] == "eco4",
solutions["total_cost"] - solutions["full_project_funding"] - solutions["total_uplift"],
solutions["total_cost"] - solutions["partial_project_funding"] - solutions["total_uplift"]
)
)
solutions = solutions.sort_values("cost_less_full_project_funding", ascending=True)
# If the solution isn't eligible, we can't really consider it
solutions = solutions[
(solutions["is_eligible"] & (solutions["scheme"] != "none")) | (solutions["scheme"] == "none")
@ -950,13 +934,6 @@ async def model_engine(body: PlanTriggerRequest):
# default measures
solution = deepcopy(optimal_solution["items"]) + deepcopy(optimal_solution["unfunded_items"])
funded_measures = deepcopy(optimal_solution["items"]) if scheme != "none" else []
unfunded_measures = deepcopy(optimal_solution["unfunded_items"])
# If we have an EPC D + HHRSH project, we move HHRSH out of funded measures
if eco_packages.get(p.id)[2] == "solar_hhrsh_eco4" and p.data["current-energy-rating"] == "D":
unfunded_measures.extend(
[x for x in funded_measures if x["type"] == "high_heat_retention_storage_heaters"]
)
funded_measures = [x for x in funded_measures if x["type"] != "high_heat_retention_storage_heaters"]
# This is the total amount of funding that the project will produce (EXCLUDING uplifts) (£)
project_funding = optimal_solution["full_project_funding"] if scheme == "eco4" else \

View file

@ -214,6 +214,30 @@ def _get_already_installed_gain(selected_measures, needs_pre_eco_hhrsh_upgrade):
return sum([x["gain"] for x in selected_measures if x["already_installed"]])
def _move_hhrsh_to_unfunded(picked, unfunded_picked, needs_pre_eco_hhrsh_upgrade):
"""
This function handles the case of moving HHRSH to unfunded picks if needed, where we have an ECO4 project
where an unfunded measure needs to be installed first.
:param picked: List of picked measures
:param unfunded_picked: List of unfunded picked measures
:param needs_pre_eco_hhrsh_upgrade: Boolean indicating if pre-ECO4 HHRSH upgrade is needed
:return:
"""
if not needs_pre_eco_hhrsh_upgrade:
return picked, unfunded_picked
# We append HHRSH to unfunded items
hhrsh_measure = [x for x in picked if x["type"] == "high_heat_retention_storage_heaters"]
if not hhrsh_measure:
raise ValueError("Expected HHRSH measure to be in total picks")
unfunded_picked += hhrsh_measure
# Remove from total picks
picked = [x for x in picked if x["type"] != "high_heat_retention_storage_heaters"]
return picked, unfunded_picked
def optimise_with_funding_paths(
p, input_measures, housing_type, funding: Funding, budget=None, target_gain=None, work_package=None
):
@ -310,6 +334,8 @@ def optimise_with_funding_paths(
already_installed_gain = _get_already_installed_gain(
picked, needs_pre_eco_hhrsh_upgrade
)
# If we need a pre-eco4 HHRSH upgrade, we move HHRSH to unfunded items
picked, unfunded_picked = _move_hhrsh_to_unfunded(picked, [], needs_pre_eco_hhrsh_upgrade)
solutions.append(
{
@ -322,7 +348,7 @@ def optimise_with_funding_paths(
"is_eligible": _is_eligible_funding_package(
scheme, float(p.data["current-energy-efficiency"]), sub_gain
),
"unfunded_items": [],
"unfunded_items": unfunded_picked,
"already_installed_gain": already_installed_gain
}
)
@ -455,6 +481,11 @@ def optimise_with_funding_paths(
total_picks, needs_pre_eco_hhrsh_upgrade
)
# If we need a pre-eco4 HHRSH upgrade, we move HHRSH to unfunded items
total_picks, unfunded_picked = _move_hhrsh_to_unfunded(
total_picks, unfunded_picked, needs_pre_eco_hhrsh_upgrade
)
solutions.append({
"fixed_ids": fixed_ids,
"items": total_picks,
@ -510,6 +541,22 @@ def optimise_with_funding_paths(
solutions["total_uplift"] = solutions.apply(lambda x: get_total_uplift(x), axis=1)
solutions["total_uplift_score"] = solutions.apply(lambda x: get_total_innovation_score(x), axis=1)
# Given the solutions we select the optimal one
# 1) If the scheme is ECO4, the full project funding and uplift are deducted from the cost
# 2) If the sheme is GBIS, the partial project funding and uplift are deducted from the cost
# 3) Otherwise, no funding is deducted from the cost
solutions["cost_less_full_project_funding"] = np.where(
solutions["scheme"] == "none",
solutions["total_cost"],
np.where(
solutions["scheme"] == "eco4",
solutions["total_cost"] - solutions["full_project_funding"] - solutions["total_uplift"],
solutions["total_cost"] - solutions["partial_project_funding"] - solutions["total_uplift"]
)
)
solutions = solutions.sort_values("cost_less_full_project_funding", ascending=True)
return solutions