From 036f3c562e7bd2a99cf6a167a79a3be7e2459011 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Sat, 2 Dec 2023 11:05:09 +0000 Subject: [PATCH] targeting optimiser movement requirements --- backend/app/plan/router.py | 29 ++++------- backend/app/utils.py | 4 +- recommendations/optimiser/CostOptimiser.py | 15 ++++-- .../optimiser/optimiser_functions.py | 48 +++++-------------- 4 files changed, 32 insertions(+), 64 deletions(-) diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index 2277a6b4..42014bb3 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -81,17 +81,12 @@ async def trigger_plan(body: PlanTriggerRequest): if not is_new: continue # TODO: Need to add heat demand target - # TODO: Temp for Keyzy - if config['address'] == "25 Albert Street": - epc_target = "C" - else: - epc_target = body.goal_value create_property_targets( session, property_id=property_id, portfolio_id=body.portfolio_id, - epc_target=epc_target, + epc_target=body.goal_value, heat_demand_target=None ) @@ -242,14 +237,7 @@ async def trigger_plan(body: PlanTriggerRequest): else: # The minimum gain is the minimum number of SAP points required to get to the target SAP band current_sap_points = int(property_instance.data["current-energy-efficiency"]) - - # TODO: TEMP - if property_instance.address1 == "25 Albert Street": - opt_epc_target = "C" - else: - opt_epc_target = body.goal_value - - target_sap_points = epc_to_sap_lower_bound(opt_epc_target) + target_sap_points = epc_to_sap_lower_bound(body.goal_value) # If the gain is negative, the optimiser will return an empty solution optimiser = CostOptimiser( @@ -263,6 +251,7 @@ async def trigger_plan(body: PlanTriggerRequest): selected_recommendations = {r["id"] for r in solution} + # If wall ventilation is selected, we also include mechanical ventilation as a best practice measure if any(x in [r["type"] for r in solution] for x in [ "internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation" ]): @@ -413,14 +402,14 @@ async def trigger_plan(body: PlanTriggerRequest): current_epc_rating=property_instance.data["current-energy-rating"], ) - print("Hardcoded B - fix me") - if property_instance.address1 == "25 Albert Street": - hardcoded_expected_epc = "C" - else: - hardcoded_expected_epc = "B" + # We sum up the SAP points of the default recommendations and calculate a new EPC category. This + # category is then used to produce adjusted energy figures + total_sap_points = sum([x["sap_points"] for x in representative_recs[property_id]]) + expected_epc = sap_to_epc(float(property_instance.data["current-energy-efficiency"]) + total_sap_points) + expected_adjusted_energy = AnnualBillSavings.adjust_energy_to_metered( epc_energy_consumption=expected_heat_demand, - current_epc_rating=hardcoded_expected_epc, + current_epc_rating=expected_epc, ) heat_demand_change = ( diff --git a/backend/app/utils.py b/backend/app/utils.py index b4ba1bb9..d912a94a 100644 --- a/backend/app/utils.py +++ b/backend/app/utils.py @@ -69,10 +69,10 @@ def generate_api_key(): return api_key -def sap_to_epc(sap_points: int): +def sap_to_epc(sap_points: int | float): """ Simple utility function to convert SAP points to EPC rating. - :param sapPoints: numerical value of SAP points, typically between 0 and 100 + :param sap_points: numerical value of SAP points, typically between 0 and 100 :return: """ diff --git a/recommendations/optimiser/CostOptimiser.py b/recommendations/optimiser/CostOptimiser.py index 80924fd1..622d5b47 100644 --- a/recommendations/optimiser/CostOptimiser.py +++ b/recommendations/optimiser/CostOptimiser.py @@ -24,13 +24,18 @@ class CostOptimiser: self.solution_gain = None @classmethod - def calculate_sap_gain_with_slack(cls, min_gain): - if min_gain <= 10: - return min_gain + 2 + def calculate_sap_gain_with_slack(cls, min_gain: int | float): + """ + Adds a small amount of buffer to the minimum gain, to account for possible error in SAP predictions + :param min_gain: Numerical value for the minimum gain + :return: + """ + if min_gain <= 5: + return min_gain + 0.5 elif min_gain <= 20: - return min_gain + 3 + return min_gain + 1.5 else: - return min_gain + 4 + return min_gain + 2 def setup(self): # Initialize Model diff --git a/recommendations/optimiser/optimiser_functions.py b/recommendations/optimiser/optimiser_functions.py index 9b16c6b5..03aa38bd 100644 --- a/recommendations/optimiser/optimiser_functions.py +++ b/recommendations/optimiser/optimiser_functions.py @@ -16,44 +16,18 @@ def prepare_input_measures(property_recommendations, goal): if not goal_key: raise NotImplementedError("Not implemented this gain type - investigate me") - ventilation_rec = [rec for rec in property_recommendations if rec[0]["type"] == "mechanical_ventilation"][0] - input_measures = [] for recs in property_recommendations: - - # We don't actually optimise ventilation - if recs[0]["type"] == "mechanical_ventilation": - continue - - if recs[0]["type"] in ["internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"]: - # Wall insulation and mechanical ventilation are paired. You can't have wall insulation without mechanical - # ventilation - - ventilation_cost = ventilation_rec[0]["total"] if ventilation_rec else 0 - ventilation_gain = ventilation_rec[0][goal_key] if ventilation_rec else 0 - - input_measures.append( - [ - { - "id": rec["recommendation_id"], - "cost": rec["total"] + ventilation_cost, - "gain": rec[goal_key] + ventilation_gain, - "type": rec["type"] - } - for rec in recs - ] - ) - else: - input_measures.append( - [ - { - "id": rec["recommendation_id"], - "cost": rec["total"], - "gain": rec[goal_key], - "type": rec["type"] - } - for rec in recs - ] - ) + input_measures.append( + [ + { + "id": rec["recommendation_id"], + "cost": rec["total"], + "gain": rec[goal_key], + "type": rec["type"] + } + for rec in recs + ] + ) return input_measures