From c4087b61242de9a02889545cd01a8b83dc35e525 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 22 Jun 2023 10:34:58 +0100 Subject: [PATCH] Implemented methodology for extracting uvalue estimate --- model_data/analysis/UvalueEstimations.py | 4 +- model_data/app.py | 3 +- .../recommendations/WallRecommendations.py | 61 +++++++++++++++++-- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/model_data/analysis/UvalueEstimations.py b/model_data/analysis/UvalueEstimations.py index 2f19f9ad..0211350f 100644 --- a/model_data/analysis/UvalueEstimations.py +++ b/model_data/analysis/UvalueEstimations.py @@ -4,11 +4,11 @@ from model_data.EpcClean import EpcClean class UvalueEstimations: - def __init__(self, data): + def __init__(self, data: list): """ Initialize the UvalueEstimations class. - :param data: The input data as a pandas DataFrame. + :param data: The input data as a list of dictionaries, to be converted to a dataframe """ self.data = pd.DataFrame(data) self.walls = None diff --git a/model_data/app.py b/model_data/app.py index d9add8d9..9cef7e1e 100644 --- a/model_data/app.py +++ b/model_data/app.py @@ -114,6 +114,7 @@ def handler(): p.get_components(cleaner) uvalue_estimates = UvalueEstimations(data=data) + uvalue_estimates.get_estimates(cleaner=cleaner) # Now, given the components, we want to idenfity upgrade options import pandas as pd @@ -153,7 +154,7 @@ def handler(): # 'Flat 28, 22 Adelina Grove' 'Solid brick, as built, insulated (assumed)' from model_data.recommendations.WallRecommendations import WallRecommendations - self = WallRecommendations(property_instance=input_properties[6]) + self = WallRecommendations(property_instance=input_properties[6], uvalue_estimates=uvalue_estimates) # We need to deduce a U-value for "Good" energy effieciency diff --git a/model_data/recommendations/WallRecommendations.py b/model_data/recommendations/WallRecommendations.py index d98e3179..7fcebd7b 100644 --- a/model_data/recommendations/WallRecommendations.py +++ b/model_data/recommendations/WallRecommendations.py @@ -3,6 +3,8 @@ import re import itertools from model_data.Property import Property +from model_data.ConservationAreaClient import ConservationAreaClient +from model_data.analysis.UvalueEstimations import UvalueEstimations import pandas as pd from copy import deepcopy @@ -197,11 +199,10 @@ class WallRecommendations: "solid_brick": 2, } - def __init__(self, property_instance: Property): + def __init__(self, property_instance: Property, uvalue_estimates: UvalueEstimations): self.property = property_instance self.year_built = self._year_property_was_built() - # TODO: Need to properly implement this - self.in_converation_area = False + self.uvalue_estimates = uvalue_estimates # Will contains a list of recommended measures self.recommendations = [] @@ -265,7 +266,7 @@ class WallRecommendations: ewi_parts = [ part for part in wall_parts if part["type"] == "external_wall_insulation" - ] if not self.in_converation_area else [] + ] if self.property.in_conservation_area == ConservationAreaClient.IN_CONSERVATION_AREA else [] iwi_parts = [part for part in wall_parts if part["type"] == "internal_wall_insulation"] @@ -319,11 +320,61 @@ class WallRecommendations: if is_solid_brick and insulation_thickness == "average" and wall_energy_efficiency in ["Good", "Average"]: - if self.in_converation_area: + # Get uvalue estimate + uvalue_estimate = self._get_walls_uvalue_estimate() + + if self.property.in_conservation_area == ConservationAreaClient.IN_CONSERVATION_AREA: blah raise NotImplementedError("Not implemented yet") + def _get_walls_uvalue_estimate(self): + + """ + Wrapper function which contains the methodology to extract a property's walls u-value estimate + when we don't have a true value and if we can't base our assumption off of the material + :return: + """ + + total_floor_area_group_decile = self.uvalue_estimates.classify_decile_newvalues( + decile_boundaries=self.uvalue_estimates.walls_decile_data["decile_boundaries"], + decile_labels=self.uvalue_estimates.walls_decile_data["decile_labels"], + new_values=[float(self.property.data["total-floor-area"])], + )[0] + + u_value_estimate = self.uvalue_estimates.walls[ + (self.uvalue_estimates.walls["local-authority"] == self.property.data["local-authority"]) & + (self.uvalue_estimates.walls["property-type"] == self.property.data["property-type"]) & + (self.uvalue_estimates.walls["walls-energy-eff"] == self.property.data["walls-energy-eff"]) & + (self.uvalue_estimates.walls["walls-env-eff"] == self.property.data["walls-env-eff"]) & + (self.uvalue_estimates.walls["total-floor-area_group"] == total_floor_area_group_decile) + ] + + # Because of how spuriously populated the data is for number-habitable-rooms and number-heated-rooms, + # we will try and filter on these to see if we get a result + + habitable_rooms_filter = ( + self.uvalue_estimates.walls["number-habitable-rooms"] == self.property.data["number-habitable-rooms"] + ) + + if any(habitable_rooms_filter): + u_value_estimate = u_value_estimate[habitable_rooms_filter] + + heated_rooms_filter = ( + self.uvalue_estimates.walls["number-heated-rooms"] == self.property.data["number-heated-rooms"] + ) + + if any(heated_rooms_filter): + u_value_estimate = u_value_estimate[heated_rooms_filter] + + if u_value_estimate.empty: + raise ValueError("No U-value estimate found for the given property") + + # It's possible for us to have multiple rows if we didn't do a habitable/heated rooms filter so we + # average + + return u_value_estimate["median_thermal_transmittance"].mean() + @staticmethod def _get_recommended_part(part, selected_depth, new_u_value): """