diff --git a/backend/Property.py b/backend/Property.py index 045b6220..b0c6b083 100644 --- a/backend/Property.py +++ b/backend/Property.py @@ -597,9 +597,26 @@ class Property(Definitions): def set_floor_type(self): """ This method sets the floor type of the property, which is used for calculating u-values - :return: + + Section 5.6 of the BRE indicates that + "to simplify data collection no distinction is made in terms of U-value between an exposed floor (to + outside air below) and a semi-exposed floor (to an enclosed but unheated space below) + and the U-values in Table S12 are used. + + Therefore, we treat the exposed floor and suspended floor as the same type of floor, which is used for + calculating u-values """ - self.floor_type = "suspended" if self.floor["is_suspended"] else "solid" + + if self.floor["is_suspended"] | self.floor["another_property_below"]: + self.floor_type = "suspended" + elif self.floor["is_solid"]: + self.floor_type = "solid" + elif self.floor["is_to_unheated_space"] | self.floor["is_to_external_air"]: + self.floor_type = "exposed_floor" + elif self.floor["thermal_transmittance"] is not None: + self.floor_type = "solid" + else: + raise NotImplementedError("Implement this floor type") @staticmethod def _extract_component(component_data, component_rename_cols, component_drop_cols, rename_prefix=None): diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index 59e6cc32..d3ea3f83 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -121,7 +121,7 @@ async def trigger_plan(body: PlanTriggerRequest): # TODO: Move this to a class. We probably want a Recommender class which takes the injects the optimisers # in as a dependency and then the optimisers can take the input measures in as part of the setup() method - + # import pickle # with open("input_properties.pickle", "rb") as f: # input_properties = pickle.load(f) @@ -132,7 +132,7 @@ async def trigger_plan(body: PlanTriggerRequest): # import pickle # with open("cleaned.pickle", "rb") as f: - # cleaned = pickle.dump(f) + # cleaned = pickle.load(f) # with open("sap_dataset.pickle", "rb") as f: # sap_dataset = pickle.load(f) @@ -144,11 +144,11 @@ async def trigger_plan(body: PlanTriggerRequest): recommendations_scoring_data = [] for p in input_properties: - property_recommendations = [] - # Property recommendations p.get_components(cleaned) + property_recommendations = [] + # Floor recommendations floor_recommender = FloorRecommendations(property_instance=p, materials=materials_by_type["floor"]) floor_recommender.recommend() diff --git a/etl/epc/property_change_app.py b/etl/epc/property_change_app.py index 435b668d..4f49f6da 100644 --- a/etl/epc/property_change_app.py +++ b/etl/epc/property_change_app.py @@ -415,13 +415,11 @@ def app(): all_equal_rows = [] for directory in tqdm(directories): - filepath = directory / "certificates.csv" data_processor = DataProcessor(filepath=filepath) df = data_processor.pre_process() - df[df["WALLS_DESCRIPTION"].str.contains("Cavity")]["WALLS_DESCRIPTION"].unique() cleaning_averages = data_processor.make_cleaning_averages() diff --git a/recommendations/FloorRecommendations.py b/recommendations/FloorRecommendations.py index 35e34648..a20a4fe1 100644 --- a/recommendations/FloorRecommendations.py +++ b/recommendations/FloorRecommendations.py @@ -97,6 +97,8 @@ class FloorRecommendations(Definitions): # Given the U-value, we recommend solid floor insulation options which are usually solid foam self.recommend_floor_insulation(u_value=u_value, parts=self.solid_floor_insulation_parts) + raise NotImplementedError("Implement me!") + @staticmethod def _make_floor_description(part, depth): return f"Install {depth}{part['depth_unit']} {part['description']} insulation" diff --git a/recommendations/rdsap_tables.py b/recommendations/rdsap_tables.py index 0ce139ab..e396f727 100644 --- a/recommendations/rdsap_tables.py +++ b/recommendations/rdsap_tables.py @@ -463,6 +463,34 @@ s11_list = [ table_s11 = pd.DataFrame(s11_list) +######################################################################################################################## +# Table s12 is used for assigning the u-values of floors to unheated spaces or external air +# which can be found on page 26 of the BRE document, section 5.6 +# https://bregroup.com/wp-content/uploads/2019/09/RdSAP_2012_9.94-20-09-2019.pdf +# +# the insulation_{thickness} fields indicate the u-value at that insulation thickness +######################################################################################################################## + +s12_list = [ + {"age_band": "A", "insulation_0": 1.2, "insulation_50": 0.5, "insulation_100": 0.3, "insulation_150": 0.22}, + {"age_band": "B", "insulation_0": 1.2, "insulation_50": 0.5, "insulation_100": 0.3, "insulation_150": 0.22}, + {"age_band": "C", "insulation_0": 1.2, "insulation_50": 0.5, "insulation_100": 0.3, "insulation_150": 0.22}, + {"age_band": "D", "insulation_0": 1.2, "insulation_50": 0.5, "insulation_100": 0.3, "insulation_150": 0.22}, + {"age_band": "E", "insulation_0": 1.2, "insulation_50": 0.5, "insulation_100": 0.3, "insulation_150": 0.22}, + {"age_band": "F", "insulation_0": 1.2, "insulation_50": 0.5, "insulation_100": 0.3, "insulation_150": 0.22}, + {"age_band": "G", "insulation_0": 1.2, "insulation_50": 0.5, "insulation_100": 0.3, "insulation_150": 0.22}, + + {"age_band": "H", "insulation_0": 0.51, "insulation_50": 0.5, "insulation_100": 0.3, "insulation_150": 0.22}, + {"age_band": "I", "insulation_0": 0.51, "insulation_50": 0.5, "insulation_100": 0.3, "insulation_150": 0.22}, + + {"age_band": "J", "insulation_0": 0.25, "insulation_50": 0.25, "insulation_100": 0.25, "insulation_150": 0.22}, + + {"age_band": "K", "insulation_0": 0.22, "insulation_50": 0.22, "insulation_100": 0.22, "insulation_150": 0.22}, + {"age_band": "L", "insulation_0": 0.22, "insulation_50": 0.22, "insulation_100": 0.22, "insulation_150": 0.22}, +] + +table_s12 = pd.DataFrame(s12_list) + ######################################################################################################################## # diff --git a/recommendations/recommendation_utils.py b/recommendations/recommendation_utils.py index 67550fc8..8e67a384 100644 --- a/recommendations/recommendation_utils.py +++ b/recommendations/recommendation_utils.py @@ -5,7 +5,7 @@ import pandas as pd from recommendations.rdsap_tables import ( epc_wall_description_map, wall_uvalues_df, default_wall_thickness, table_s9 as s9, table_s10 as s10, - table_s11 as s11 + table_s11 as s11, table_s12 as s12 ) from recommendations.config import PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION, PARTIAL_CAVITY_DESCRIPTIONS @@ -340,6 +340,32 @@ def estimate_perimeter(floor_area, num_rooms): return perimeter +def get_exposed_floor_uvalue(insulation_thickness_str, age_band): + """ + We implement the methodology as defined in section 5.6 and table S12 of the RdSAP document + :param insulation_thickness_str: + :return: + """ + + if age_band in ["A", "B", "C", "D", "E", "F", "G", "H", "I"]: + # As directed by the documentation, if the insulation thickness is not known, we assume it's + # 50mm for these age bands + if insulation_thickness_str in ["below_average", "average", "above_average"]: + insulation_thickness = 50 + elif insulation_thickness_str in ["none", None]: + insulation_thickness = 0 + elif insulation_thickness_str in ["below_average"]: + insulation_thickness = 50 + elif insulation_thickness_str == "average": + insulation_thickness = 100 + elif insulation_thickness_str == "above_average": + insulation_thickness = 150 + else: + insulation_thickness = int(insulation_thickness_str.replace("mm", "")) + + return s12[s12["age_band"] == age_band][f"insulation_{insulation_thickness}"].values[0] + + def get_floor_u_value(floor_type, area, perimeter, age_band, wall_type, insulation_thickness=None): """ Estimate the u-value of a suspended floor, based on RdSap methodology @@ -372,6 +398,12 @@ def get_floor_u_value(floor_type, area, perimeter, age_band, wall_type, insulati 0.701 """ + if floor_type == "exposed_floor": + # In this case, we extract the u-value from table s12 + # See section 5.6 of the RdSAP document for more details + # https://bregroup.com/wp-content/uploads/2019/09/RdSAP_2012_9.94-20-09-2019.pdf + return get_exposed_floor_uvalue(insulation_thickness, age_band) + # Cleans our regularly inputted insulation thickness for usage in this function insulation_thickness = extract_insulation_thickness(insulation_thickness)