diff --git a/model_data/cleaner_app.py b/model_data/cleaner_app.py index 5c895091..b823081b 100644 --- a/model_data/cleaner_app.py +++ b/model_data/cleaner_app.py @@ -82,6 +82,108 @@ def app(): df = pd.DataFrame(cleaned_data["roof-description"]) df = df[pd.isnull(df["thermal_transmittance"])] + def get_u_value_from_s9(thickness, s9, is_loft, is_roof_room, is_thatched): + """Get the U-value from table S9 based on the insulation thickness.""" + if thickness in ["below average", "average", "above average", "none", None] or ( + not is_loft and not is_roof_room + ): + return None + elif thickness.endswith("+"): + thickness = int(thickness[:-1]) + else: + try: + thickness = int(thickness) + except ValueError: + # If thickness is not a valid number (could be a string or None), return None + return None + + # Determine the column to refer based on the roof type + column = 'Thatched_roof_U_value_W_m2K' if is_thatched else 'Slates_or_tiles_U_value_W_m2K' + + # Get the correct U-value based on the insulation thickness + return s9[s9['Insulation_thickness_mm'] >= thickness][column].iloc[0] + + def get_roof_u_value(description_dict, age_band, s9, s10): + """ + Determine the U-value for a roof based on the description dictionary and age band. + + We use table s9 is the insulation thickness was measured, otherwise we use table s10. + + Parameters: + description_dict (dict): Dictionary containing the details of the roof description. + age_band (str): The age band of the property. + s9 (pd.DataFrame): The DataFrame representing table S9. + s10 (pd.DataFrame): The DataFrame representing table S10. + + Returns: + float: The determined U-value. + """ + + # If there is a dwelling above, the U-value is 0 + if description_dict['has_dwelling_above']: + return 0.0 + + # Step 1: Try to get the U-value from table S9 based on the insulation thickness + u_value = get_u_value_from_s9( + thickness=description_dict['insulation_thickness'], + s9=s9, + is_loft=description_dict['is_loft'], + is_roof_room=description_dict['is_roof_room'], + is_thatched=description_dict['is_thatched'] + ) + + if u_value is not None: + return u_value + + # Step 2: If the U-value could not be determined from table S9, use table S10 + + # Define the columns to be used based on the description details + if description_dict['is_flat']: + column = 'Flat_roof' + elif description_dict['is_thatched']: + if description_dict['is_roof_room']: + column = 'Thatched_roof_room_in_roof' + else: + column = 'Thatched_roof' + elif description_dict['is_roof_room']: + column = 'Room_in_roof_slates_or_tiles' + elif description_dict['is_pitched']: + if description_dict['is_at_rafters']: + column = 'Pitched_slates_or_tiles_insulation_at_rafters' + else: + column = 'Pitched_slates_or_tiles_insulation_between_joists_or_unknown' + else: + # Default to pitched roof with insulation between joists or unknown + column = 'Pitched_slates_or_tiles_insulation_between_joists_or_unknown' + + # Get the U-value from table S10 based on the age band and the determined column + u_value = s10.loc[s10['Age_band'].str.contains(age_band), column].values[0] + + return u_value + + from recommendations.rdsap_tables import age_bands + + z = pd.DataFrame(cleaned_data["roof-description"]) + z = z[pd.isnull(z["thermal_transmittance"])] + z["insulation_thickness"].value_counts() + z[z["insulation_thickness"] == "above average"] + + z.head(30).to_dict("records") + + for i, roof in enumerate(cleaned_data["roof-description"]): + if roof["thermal_transmittance"] is not None or "Average thermal transmittance" in roof["clean_description"]: + continue + + for ab in age_bands: + value = float( + get_roof_u_value( + description_dict=roof, + age_band=ab, + s9=table_s9, + s10=table_s10 + ) + ) + # We store a singular file however we could store the data under the following file path: # cleaned_epc_data/{component}/{original_description}/cleaned.bson # where component is one of the keys of cleaned_data. If we store it against the original data, this diff --git a/recommendations/rdsap_tables.py b/recommendations/rdsap_tables.py index 8d303819..ee0c26bc 100644 --- a/recommendations/rdsap_tables.py +++ b/recommendations/rdsap_tables.py @@ -92,8 +92,10 @@ age_band_data = [ }, ] +######################################################################################################################## # As defined in the rdsap documentation on page 9 # https://bregroup.com/wp-content/uploads/2019/09/RdSAP_2012_9.94-20-09-2019.pdf +######################################################################################################################## default_wall_thickness = [ { "type": "stone", "A": 500, "B": 500, "C": 500, "D": 500, "E": 450, "F": 420, "G": 420, "H": 420, @@ -125,8 +127,10 @@ default_wall_thickness = [ }, ] +######################################################################################################################## # This wall u-value table is defined in the rdsap documentation on page 19 # https://bregroup.com/wp-content/uploads/2019/09/RdSAP_2012_9.94-20-09-2019.pdf +######################################################################################################################## wall_types = [ "Stone: granite or whinstone as built", "Stone: sandstone or limestone as built", @@ -305,3 +309,123 @@ epc_wall_description_map = { "Park home wall, with external insulation": "Park home with additional insulation", "Park home wall, with internal insulation": "Park home with additional insulation", } + +######################################################################################################################## +# These following tables define table s9 and s10 which are used to assign roofs with their assumed u-values. +# The tables can be found on pages 23 and 24 of the BRE document +# https://bregroup.com/wp-content/uploads/2019/09/RdSAP_2012_9.94-20-09-2019.pdf +######################################################################################################################## + +s9_list = [ + {"Insulation_thickness_mm": None, "Slates_or_tiles_U_value_W_m2K": 2.3, "Thatched_roof_U_value_W_m2K": 0.35}, + {"Insulation_thickness_mm": 12, "Slates_or_tiles_U_value_W_m2K": 1.5, "Thatched_roof_U_value_W_m2K": 0.32}, + {"Insulation_thickness_mm": 25, "Slates_or_tiles_U_value_W_m2K": 1.0, "Thatched_roof_U_value_W_m2K": 0.30}, + {"Insulation_thickness_mm": 50, "Slates_or_tiles_U_value_W_m2K": 0.68, "Thatched_roof_U_value_W_m2K": 0.25}, + {"Insulation_thickness_mm": 75, "Slates_or_tiles_U_value_W_m2K": 0.50, "Thatched_roof_U_value_W_m2K": 0.22}, + {"Insulation_thickness_mm": 100, "Slates_or_tiles_U_value_W_m2K": 0.40, "Thatched_roof_U_value_W_m2K": 0.20}, + {"Insulation_thickness_mm": 150, "Slates_or_tiles_U_value_W_m2K": 0.30, "Thatched_roof_U_value_W_m2K": 0.17}, + {"Insulation_thickness_mm": 200, "Slates_or_tiles_U_value_W_m2K": 0.21, "Thatched_roof_U_value_W_m2K": 0.14}, + {"Insulation_thickness_mm": 250, "Slates_or_tiles_U_value_W_m2K": 0.17, "Thatched_roof_U_value_W_m2K": 0.12}, + {"Insulation_thickness_mm": 270, "Slates_or_tiles_U_value_W_m2K": 0.16, "Thatched_roof_U_value_W_m2K": 0.12}, + {"Insulation_thickness_mm": 300, "Slates_or_tiles_U_value_W_m2K": 0.14, "Thatched_roof_U_value_W_m2K": 0.11}, + {"Insulation_thickness_mm": 350, "Slates_or_tiles_U_value_W_m2K": 0.12, "Thatched_roof_U_value_W_m2K": 0.10}, + {"Insulation_thickness_mm": 400, "Slates_or_tiles_U_value_W_m2K": 0.11, + "Thatched_roof_U_value_W_m2K": 0.09}, +] + +s10_list = [ + { + "Age_band": "A, B, C, D", + "Pitched_slates_or_tiles_insulation_between_joists_or_unknown": "2.3", + "Pitched_slates_or_tiles_insulation_at_rafters": "2.3", + "Flat_roof": "2.3", + "Room_in_roof_slates_or_tiles": "2.3", + "Thatched_roof": 0.35, + "Thatched_roof_room_in_roof": 0.25, + "Park_home": None + }, + { + "Age_band": "E", + "Pitched_slates_or_tiles_insulation_between_joists_or_unknown": "1.5", + "Pitched_slates_or_tiles_insulation_at_rafters": "1.5", + "Flat_roof": "1.5", + "Room_in_roof_slates_or_tiles": "1.5", + "Thatched_roof": 0.35, + "Thatched_roof_room_in_roof": 0.25, + "Park_home": None + }, + { + "Age_band": "F", + "Pitched_slates_or_tiles_insulation_between_joists_or_unknown": "0.68", + "Pitched_slates_or_tiles_insulation_at_rafters": "0.68", + "Flat_roof": "0.68", + "Room_in_roof_slates_or_tiles": "0.80", + "Thatched_roof": 0.35, + "Thatched_roof_room_in_roof": 0.25, + "Park_home": 1.7 + }, + { + "Age_band": "G", + "Pitched_slates_or_tiles_insulation_between_joists_or_unknown": "0.40", + "Pitched_slates_or_tiles_insulation_at_rafters": "0.40", + "Flat_roof": "0.40", + "Room_in_roof_slates_or_tiles": "0.50", + "Thatched_roof": 0.35, + "Thatched_roof_room_in_roof": 0.25, + "Park_home": 0.6 + }, + { + "Age_band": "H", + "Pitched_slates_or_tiles_insulation_between_joists_or_unknown": "0.30", + "Pitched_slates_or_tiles_insulation_at_rafters": "0.35", + "Flat_roof": "0.35", + "Room_in_roof_slates_or_tiles": "0.35", + "Thatched_roof": 0.35, + "Thatched_roof_room_in_roof": 0.25, + "Park_home": None + }, + { + "Age_band": "I", + "Pitched_slates_or_tiles_insulation_between_joists_or_unknown": "0.26", + "Pitched_slates_or_tiles_insulation_at_rafters": "0.35", + "Flat_roof": "0.35", + "Room_in_roof_slates_or_tiles": "0.35", + "Thatched_roof": 0.35, + "Thatched_roof_room_in_roof": 0.25, + "Park_home": 0.35 + }, + { + "Age_band": "J", + "Pitched_slates_or_tiles_insulation_between_joists_or_unknown": "0.16", + "Pitched_slates_or_tiles_insulation_at_rafters": 0.20, + "Flat_roof": 0.25, + "Room_in_roof_slates_or_tiles": 0.30, + "Thatched_roof": 0.30, + "Thatched_roof_room_in_roof": 0.25, + "Park_home": None + }, + { + "Age_band": "K", + "Pitched_slates_or_tiles_insulation_between_joists_or_unknown": "0.16", + "Pitched_slates_or_tiles_insulation_at_rafters": 0.20, + "Flat_roof": "0.25", + "Room_in_roof_slates_or_tiles": "0.25", + "Thatched_roof": "0.25", + "Thatched_roof_room_in_roof": "0.25", + "Park_home": 0.30 + }, + { + "Age_band": "L", + "Pitched_slates_or_tiles_insulation_between_joists_or_unknown": "0.16", + "Pitched_slates_or_tiles_insulation_at_rafters": 0.18, + "Flat_roof": 0.18, + "Room_in_roof_slates_or_tiles": 0.18, + "Thatched_roof": 0.18, + "Thatched_roof_room_in_roof": 0.18, + "Park_home": None + } +] + +table_s9 = pd.DataFrame(s9_list) + +table_s10 = pd.DataFrame(s10_list)