From ada2101a735e41844504b114fa216a3349637081 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 18 Oct 2023 17:25:50 +1100 Subject: [PATCH] Wrote ventilation recommendations, still needs unit tests --- .idea/Model.iml | 2 +- .idea/misc.xml | 2 +- backend/app/db/models/materials.py | 2 + backend/app/plan/router.py | 14 +++- backend/app/plan/utils.py | 9 ++- recommendations/VentilationRecommendations.py | 70 +++++++++++++++++++ recommendations/WallRecommendations.py | 1 - 7 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 recommendations/VentilationRecommendations.py diff --git a/.idea/Model.iml b/.idea/Model.iml index b0f9c00d..4413bb06 100644 --- a/.idea/Model.iml +++ b/.idea/Model.iml @@ -7,7 +7,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index ca0e1cd9..3b05c6ac 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/backend/app/db/models/materials.py b/backend/app/db/models/materials.py index 09d7369d..cf6dd971 100644 --- a/backend/app/db/models/materials.py +++ b/backend/app/db/models/materials.py @@ -13,6 +13,7 @@ class MaterialType(enum.Enum): external_wall_insulation = "external_wall_insulation" internal_wall_insulation = "internal_wall_insulation" cavity_wall_insulation = "cavity_wall_insulation" + mechanical_ventilation = "mechanical_ventilation" class DepthUnit(enum.Enum): @@ -21,6 +22,7 @@ class DepthUnit(enum.Enum): class CostUnit(enum.Enum): gbp_sq_meter = "gbp_sq_meter" + gbp_per_unit = "gbp_per_unit" class RValueUnit(enum.Enum): diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index 400b2dee..135b877d 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -30,6 +30,7 @@ from backend.Property import Property from etl.epc.DataProcessor import DataProcessor from etl.epc.settings import COLUMNS_TO_MERGE_ON from recommendations.FloorRecommendations import FloorRecommendations +from recommendations.VentilationRecommendations import VentilationRecommendations from recommendations.optimiser.CostOptimiser import CostOptimiser from recommendations.optimiser.GainOptimiser import GainOptimiser from recommendations.optimiser.optimiser_functions import prepare_input_measures @@ -125,9 +126,6 @@ async def trigger_plan(body: PlanTriggerRequest): # # with open("cleaned.pickle", "rb") as f: # cleaned = pickle.load(f) - # - # with open("materials_by_type.pickle", "wb") as f: - # materials_by_type = pickle.load(f) recommendations = {} recommendations_scoring_data = [] @@ -160,6 +158,16 @@ async def trigger_plan(body: PlanTriggerRequest): if wall_recomender.recommendations: property_recommendations.append(wall_recomender.recommendations) + # Ventilation recommendations + ventilation_recomender = VentilationRecommendations( + property_instance=p, + materials=materials_by_type["ventilation"] + ) + ventilation_recomender.recommend() + + if ventilation_recomender.recommendation: + property_recommendations.append(ventilation_recomender.recommendation) + # We insert temporary ids into the recommendations which is important for the optimiser later property_recommendations = insert_temp_recommendation_id(property_recommendations) diff --git a/backend/app/plan/utils.py b/backend/app/plan/utils.py index 85a02b61..0a1eaa72 100644 --- a/backend/app/plan/utils.py +++ b/backend/app/plan/utils.py @@ -15,7 +15,8 @@ def filter_materials(materials): mapping = { "walls": ["internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"], - "floor": ["suspended_floor_insulation", "solid_floor_insulation"] + "floor": ["suspended_floor_insulation", "solid_floor_insulation"], + "ventilation": ["mechanical_ventilation"], } materials = [row2dict(material) for material in materials] @@ -164,9 +165,13 @@ def create_recommendation_scoring_data( if scoring_dict["floor_insulation_thickness_ENDING"] is None: scoring_dict["floor_insulation_thickness_ENDING"] = "none" - if recommendation["type"] not in ["wall_insulation", "floor_insulation"]: + if recommendation["type"] == "mechanical_ventilation": + scoring_dict["MECHANICAL_VENTILATION_ENDING"] = 'mechanical, extract only' + + if recommendation["type"] not in ["wall_insulation", "floor_insulation", "mechanical_ventilation"]: raise NotImplementedError("Implement me") + # Fill missing roof u-values - this fill is not based on recommended upgrades if scoring_dict["roof_thermal_transmittance_ENDING"] is None: scoring_dict["roof_thermal_transmittance_ENDING"] = get_roof_u_value( insulation_thickness=property.roof["insulation_thickness"], diff --git a/recommendations/VentilationRecommendations.py b/recommendations/VentilationRecommendations.py new file mode 100644 index 00000000..35de9b3b --- /dev/null +++ b/recommendations/VentilationRecommendations.py @@ -0,0 +1,70 @@ +import pandas as pd +from BaseUtility import Definitions +from backend.Property import Property + + +class VentilationRecommendations(Definitions): + """ + For properties that do not have ventilation, we recommend installing ventilaion + This is particularly important for properties that have insulated walls and is also + crucial for prevent overheating risks in warmer months + """ + + VENTILATION_DESCRIPTIONS = [ + 'mechanical, extract only', + 'mechanical, supply and extract' + ] + + def __init__( + self, + property_instance: Property, + materials + ): + self.property = property_instance + + self.has_ventilaion = None + self.recommendation = None + self.materials = materials + + def identify_ventilation(self): + self.has_ventilaion = self.property.data["mechanical-ventilation"] in self.VENTILATION_DESCRIPTIONS + + def recommend(self): + """ + If there is no ventilation, we recommend installing ventilation + + Generally, best practice is to install controlled ventilation for insulated walls so we still recommend + ventilation if there is natural ventilation + :return: + """ + + self.identify_ventilation() + if self.has_ventilaion: + return + + if len(self.materials) != 1: + raise NotImplementedError("Only handled the case of having one venilation option") + + # We recommend installing 2 units + n_units = 2 + + part = self.materials.copy() + + estimated_cost = n_units * part[0]["cost"] + + part[0]["estimated_cost"] = estimated_cost + part[0]["quantity"] = n_units + part[0]["quantity_unit"] = None + + # We recommend installing two mechanical ventilation systems + self.recommendation = [ + { + "parts": part, + "type": part[0]["type"], + "description": "Install %s" % part[0]["description"], + "starting_u_value": None, + "new_u_value": None, + "sap_points": None, + "cost": estimated_cost, + } + ] diff --git a/recommendations/WallRecommendations.py b/recommendations/WallRecommendations.py index b3eea7e0..ad2ca861 100644 --- a/recommendations/WallRecommendations.py +++ b/recommendations/WallRecommendations.py @@ -1,4 +1,3 @@ -import itertools import math from typing import List