From 1393a99b8bcba535bea7d3021ef3e5f7de47dbbf Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 4 Jun 2024 16:21:45 +0100 Subject: [PATCH] checking hhr recommendations| --- backend/Property.py | 8 ---- backend/app/plan/router.py | 69 +++++++++++++++++++++++++++ recommendations/HeatingRecommender.py | 45 +++++++++++------ recommendations/Mds.py | 22 ++++++--- 4 files changed, 115 insertions(+), 29 deletions(-) diff --git a/backend/Property.py b/backend/Property.py index ce21bd52..6336e42d 100644 --- a/backend/Property.py +++ b/backend/Property.py @@ -858,14 +858,6 @@ class Property: self.floor_level = 1 return - def is_mid_floor_flat(self): - """ - Simple utility function to check if the property is a mid-floor flat - :return: - """ - - return self.data["property-type"] == "Flat" and self.epc_record.original_epc["floor-level"] == "mid floor" - def set_wall_type(self): """ This method sets the wall type of the property, using a simple approach based on the wall description diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index f86b1759..ee36ea80 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -970,6 +970,7 @@ async def build_mds(body: MdsRequest): results.append({ "config_address": config["address"], "config_postcode": config["postcode"], + "uprn": p.uprn, "address": p.address, "postcode": p.postcode, "measures": package_comparison["measures"], @@ -988,6 +989,74 @@ async def build_mds(body: MdsRequest): results = pd.DataFrame(results) + # For the different measures, we check the impact with a few debugging functions + + def check_mds(results, input_properties, recommendations): + import ast + walls_check = [] + hhr_check = [] + for p in input_properties: + res = results[results["uprn"] == p.uprn] + wall = p.walls + heating = p.main_heating + wall_recommendation = [ + x for x in res["measures"].values[0] if + x in ["internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"] + ] + + hhr_recommendation = [ + x for x in res["measures"].values[0] if + x in ["high_heat_retention_storage_heaters"] + ] + + possible_measures = [ast.literal_eval(x) for x in list(recommendations[p.id].keys())] + # Unlist them + possible_measures = [x for sublist in possible_measures for x in sublist] + possible_measures = list(set(possible_measures)) + + if wall_recommendation: + if len(wall_recommendation) > 1: + raise Exception("something went wrong") + wall_recommendation = wall_recommendation[0] + else: + wall_recommendation = None + + hhr_recommendation = hhr_recommendation[0] if hhr_recommendation else None + + walls_check.append( + { + "uprn": p.uprn, + "address": p.address, + "postcode": p.postcode, + "conservation_status": p.spatial["conservation_status"], + "is_listed_building": p.spatial["is_listed_building"], + "is_heritage_building": p.spatial["is_heritage_building"], + "wall": wall["clean_description"], + "recommendation": wall_recommendation, + "possible_measures": possible_measures, + "selected_measures": res["measures"].values[0], + } + ) + + hhr_check.append( + { + "uprn": p.uprn, + "address": p.address, + "postcode": p.postcode, + "heating": heating["clean_description"], + "recommendation": hhr_recommendation, + "possible_measures": possible_measures, + "selected_measures": res["measures"].values[0], + } + ) + + walls_check = pd.DataFrame(walls_check) + hhr_check = pd.DataFrame(hhr_check) + + return walls_check, hhr_check + + walls_check, hhr_check = check_mds(results, input_properties, recommendations) + results = [] for p in input_properties: measures = p.measures diff --git a/recommendations/HeatingRecommender.py b/recommendations/HeatingRecommender.py index 1b8c5035..11a7b663 100644 --- a/recommendations/HeatingRecommender.py +++ b/recommendations/HeatingRecommender.py @@ -11,9 +11,12 @@ class HeatingRecommender: ELECTRIC_HEATING_DESCRIPTIONS = [ "Room heaters, electric", "Electric storage heaters", - "Electric storage heaters, radiators" + "Electric storage heaters, radiators", + "Portable electric heaters assumed for most rooms", ] + high_heat_retention_contols_desc = "Controls for high heat retention storage heaters" + def __init__(self, property_instance: Property): self.property = property_instance self.costs = Costs(self.property) @@ -31,12 +34,13 @@ class HeatingRecommender: :return: """ - no_heating_no_mains = ( - self.property.main_heating["clean_description"] in ["No system present, electric heaters assumed"] and - not self.property.data["mains-gas-flag"] + # If the property has assumed electric heating, regardless of whether or not it has a mains connection, we + # can consider hhr storage heaters + electric_heating_assumed = ( + self.property.main_heating["clean_description"] in ["No system present, electric heaters assumed"] ) - return self.has_electric_heating_description or no_heating_no_mains + return self.has_electric_heating_description or electric_heating_assumed def recommend(self, has_cavity_or_loft_recommendations, phase=0): """ @@ -330,6 +334,25 @@ class HeatingRecommender: return output + def is_hhr_already_installed(self): + """ + Check if the property already has high heat retention storage heaters + :return: + """ + + already_has_hhr = "Electric storage heaters" in self.property.main_heating["clean_description"] + already_has_hhr_contols = ( + self.property.main_heating_controls[ + "clean_description" + ].lower() == self.high_heat_retention_contols_desc.lower() + ) + + # Conditions for not needing this recommendation + # Modern hhr storage heaters will have the specific controls so we can check for this + already_installed_hh_retention = already_has_hhr and already_has_hhr_contols + + return already_installed_hh_retention + def recommend_hhr_storage_heaters(self, phase, system_change, heating_controls_only, _return=False): """ We will recommend upgrading to a high heat retention storage system, if the current system is not already @@ -346,19 +369,13 @@ class HeatingRecommender: controls_recommender = HeatingControlRecommender(self.property) # The heating controls we're recommending for are based on the recommended heating system - high_heat_retention_contols_desc = "Controls for high heat retention storage heaters" + # We only recommend Celect-type controls if the current heating system is not Celect-type controls - if self.property.main_heating_controls["clean_description"] != high_heat_retention_contols_desc: + if self.property.main_heating_controls["clean_description"] != self.high_heat_retention_contols_desc: controls_recommender.recommend(heating_description="Electric storage heaters, radiators") - # Conditions for not needing this recommendation - already_installed_hh_retention = ( - "Electric storage heaters" in self.property.main_heating["clean_description"] and - self.property.main_heating_controls["clean_description"].lower() == high_heat_retention_contols_desc.lower() - ) - # Conditions for not recommending electric storage heaters - if already_installed_hh_retention: + if self.is_hhr_already_installed(): # No recommendation needed return diff --git a/recommendations/Mds.py b/recommendations/Mds.py index 638b6ca8..27f6f871 100644 --- a/recommendations/Mds.py +++ b/recommendations/Mds.py @@ -116,6 +116,7 @@ class Mds: final_combinations.append([m for m in one_choice + multi_path + remaining_measures]) pruned_combinations = [] + # TODO: We can do these checks once, outside of the loop and prune the combinations for combination in final_combinations: pruned_measures = [] for measure in combination: @@ -142,10 +143,12 @@ class Mds: if measure == "loft_insulation": # Check if the roof is suitable for loft insulation and the loft isn't already done + # Or, if the home had a u-value for the roof, we don't recommend loft insulation if ( self.property_instance.roof["is_pitched"] and - not self.roof_recommender.is_loft_already_insulated() - ) or self.property_instance.is_mid_floor_flat(): + not self.roof_recommender.is_loft_already_insulated() and + self.property_instance.roof["thermal_transmittance_unit"] is None + ): pruned_measures.append(measure) continue @@ -153,8 +156,9 @@ class Mds: # Check if the floor is solid if ( self.property_instance.floor["is_solid"] and - self.property_instance.floor["insulation_thickness"] not in ["average", "above average"] - ) or self.property_instance.is_mid_floor_flat(): + self.property_instance.floor["insulation_thickness"] not in ["average", "above average"] and + self.property_instance.floor["thermal_transmittance_unit"] is not None + ): pruned_measures.append(measure) continue @@ -162,13 +166,17 @@ class Mds: # Check if the floor is suspended if ( self.property_instance.floor["is_suspended"] and - self.property_instance.floor["insulation_thickness"] not in ["average", "above average"] - ) or self.property_instance.is_mid_floor_flat(): + self.property_instance.floor["insulation_thickness"] not in ["average", "above average"] and + self.property_instance.floor["thermal_transmittance_unit"] is not None + ): pruned_measures.append(measure) continue if measure == "high_heat_retention_storage_heaters": - if self.heating_recommender.is_high_heat_retention_valid(): + if ( + self.heating_recommender.is_high_heat_retention_valid() and + not self.heating_recommender.is_hhr_already_installed() + ): pruned_measures.append(measure) continue