diff --git a/recommendations/RoofRecommendations.py b/recommendations/RoofRecommendations.py index 5a011d82..daaa523c 100644 --- a/recommendations/RoofRecommendations.py +++ b/recommendations/RoofRecommendations.py @@ -38,7 +38,10 @@ class RoofRecommendations: def recommend(self): u_value = self.property.roof["thermal_transmittance"] - insulation_thickness = convert_thickness_to_numeric(self.property.roof["insulation_thickness"]) + insulation_thickness = convert_thickness_to_numeric( + self.property.roof["insulation_thickness"], + self.property.roof["is_pitched"] + ) # We check if the roof is already insulated and if so, we exit @@ -53,7 +56,7 @@ class RoofRecommendations: u_value = get_roof_u_value(**{**self.property.roof, "age_band": self.property.age_band}) - if self.property.roof["is_pitched"]: + if self.property.roof["is_pitched"] or self.property.roof["is_flat"]: self.recommend_roof_insulation(u_value, insulation_thickness, self.property.roof) return @@ -61,10 +64,6 @@ class RoofRecommendations: self.recommend_room_roof_insulation(u_value, insulation_thickness) return - if self.property.roof["is_flat"]: - self.recommend_flat_roof_insulation(u_value, insulation_thickness) - return - raise NotImplementedError("Implement me") @staticmethod @@ -117,7 +116,7 @@ class RoofRecommendations: raise ValueError("Roof is not pitched or flat") if not materials: - raise ValueError("No loft insulation materials found") + raise ValueError("No roof insulation materials found") lowest_selected_u_value = None recommendations = [] diff --git a/recommendations/recommendation_utils.py b/recommendations/recommendation_utils.py index 954998f4..13f58fd9 100644 --- a/recommendations/recommendation_utils.py +++ b/recommendations/recommendation_utils.py @@ -571,20 +571,34 @@ def calculate_r_value_per_mm(thickness_mm, thermal_conductivity_w_mK): return r_value_per_mm -def convert_thickness_to_numeric(string_thickness): +def convert_thickness_to_numeric(string_thickness, is_pitched): """ Roof insulation thickness could be a string like "None", "300mm+" or a numeric string. This function will convert these strings to a number for easy usage + + we handle loft insulation differently to flat roof or room in roof insulation, since for loft insulation, + we are presented with an insulation thickness, whereas for the other forms of roof, we are just told whether or not + the roof is insulated or not. + :param string_thickness: string measure of insulation thickness + :param is_pitched: boolean indicating if the roof is a pitched roof :return: integer measure of insulation thickness """ - lookup = { - "none": 0, - "below average": 50, - "average": 100, - "above average": 270 - } + if is_pitched: + lookup = { + "none": 0, + "below average": 50, + "average": 100, + "above average": 270 + } + else: + lookup = { + "none": 0, + "below average": 100, + "average": 270, + "above average": 270 + } mapped = lookup.get(string_thickness) @@ -621,7 +635,7 @@ def esimtate_pitched_roof_area(floor_area: float, floor_height: float) -> float: # We're modelling the roof as two triangles where we know two of the three sides. # The floor height makes up one side and half of the wall width makes up the other side slope = np.sqrt(np.square(wall_width / 2) + np.square(floor_height)) - + area = 2 * (slope * wall_width) return area diff --git a/recommendations/tests/test_roof_recommendations.py b/recommendations/tests/test_roof_recommendations.py index 5be4c6a7..04ab5f77 100644 --- a/recommendations/tests/test_roof_recommendations.py +++ b/recommendations/tests/test_roof_recommendations.py @@ -48,6 +48,19 @@ room_roof_insulation_materials = [ } ] +flat_roof_insulation_materials = [ + { + 'id': 18, + 'type': 'flat_roof_insulation', + 'description': 'Example flat roof insulation', + 'depths': [50, 150, 220, 270, 300], 'depth_unit': 'mm', 'cost': [9, 10, 11, 12, 13], + 'cost_unit': 'gbp_sq_meter', + 'r_value_per_mm': 0.022727273, 'r_value_unit': 'square_meter_kelvin_per_watt', + 'thermal_conductivity': 0.044, 'thermal_conductivity_unit': 'watt_per_meter_kelvin', + 'link': None, 'is_active': True + } +] + class TestRoofRecommendations: @@ -347,9 +360,48 @@ class TestRoofRecommendations: } roof_recommender11 = RoofRecommendations( - property_instance=property_instance11, materials=room_roof_insulation_materials + property_instance=property_instance11, materials=flat_roof_insulation_materials ) assert not roof_recommender11.recommendations roof_recommender11.recommend() + + assert len(roof_recommender11.recommendations) == 2 + + assert roof_recommender11.recommendations[0]["parts"][0]["depths"] == [270] + assert roof_recommender11.recommendations[1]["parts"][0]["depths"] == [300] + + assert roof_recommender11.recommendations[0]["new_u_value"] == 0.16 + assert roof_recommender11.recommendations[1]["new_u_value"] == 0.14 + + assert roof_recommender11.recommendations[0]["starting_u_value"] == 2.3 + assert roof_recommender11.recommendations[1]["starting_u_value"] == 2.3 + + assert roof_recommender11.recommendations[0]["description"] == \ + "Insulate the home's flat roof with 270mm of Example flat roof insulation" + assert roof_recommender11.recommendations[1]["description"] == \ + "Insulate the home's flat roof with 300mm of Example flat roof insulation" + + def test_flat_insulated(self): + property_instance12 = Property(id=12, address1="fake", postcode="fake", epc_client=Mock()) + property_instance12.age_band = "D" + property_instance12.floor_area = 150 + property_instance12.roof = { + 'original_description': 'Flat, insulated (assumed)', + 'clean_description': 'Flat, insulated', + 'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': False, + 'is_roof_room': False, + 'is_loft': False, 'is_flat': True, 'is_thatched': False, 'is_at_rafters': False, 'is_assumed': True, + 'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': 'average' + } + + roof_recommender12 = RoofRecommendations( + property_instance=property_instance12, materials=flat_roof_insulation_materials + ) + + assert not roof_recommender12.recommendations + + roof_recommender12.recommend() + + assert not roof_recommender12.recommendations