diff --git a/model_data/recommendations/WallRecommendations.py b/model_data/recommendations/WallRecommendations.py index 47dc99d6..b4fc703e 100644 --- a/model_data/recommendations/WallRecommendations.py +++ b/model_data/recommendations/WallRecommendations.py @@ -191,10 +191,19 @@ class WallRecommendations(BaseUtility): # more popular choice YEARS_CAVITY_WALLS_BEGAN = 1930 U_VALUE_UNIT = 'w/m-¦k' - # TODO: REVIEW THESE FIGURES: RETROFIT ACADEMY COUSE INDICATED THAT IT SHOULD BE 0.3 - BUILDING_REGULATIONS_PART_L_MAX_U_VALUE = 0.18 - # Often cited minimum practical u-value - DIMINISHING_RETURNS_U_VALUE = 0.15 + + # part L building regulations indicate that any rennovations on an existing property's walls should + # achieve a U-value of no higher than 0.3 + BUILDING_REGULATIONS_PART_L_MAX_U_VALUE = 0.3 + # We don't recommend measures that are too low because it becomes expensive, therefore we aim to avoid + # diminishing returns. This value should be verified with Osmosis (TODO) + DIMINISHING_RETURNS_U_VALUE = 0.25 + + # Part L regulations indicate that any new build should have walls that achieve a u-value of no higher + # than 0.18. + BUILDING_REGULATIONS_PART_L_NEW_BUILD_MAX_U_VALUE = 0.18 + # 0.15 is an often cited diminishing returns value for new builds + NEW_BUILD_DIMINISHING_RETURNS_U_VALUE = 0.15 # Add some error so that if, for example, a new part we recommend provides a u-value of 0.19, # we still consider it as an option @@ -263,11 +272,25 @@ class WallRecommendations(BaseUtility): # they're likely to be of a certain standard. E.g. properties built within a certain time # period are likely to have cavity walls + # We can't detect it's a cavity wall, but it was built after 1990 so likely built with insulation already + # + it already has a U-value WORSE than the building regulations, so we recommend either internal or + # external wall insulation if (not is_cavity_wall) and (self.year_built >= self.YEAR_WALLS_BUILT_WITH_INSULATION) and ( u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE ): # Recommend insulation self.find_insulation(u_value) + return + + # We can't detect it's a cavity wall, but it was built after 1990 so likely built with insulation already + # + it already has a U-value better than the building regulations, so we don't need to recommend anything + if (not is_cavity_wall) and (self.year_built >= self.YEAR_WALLS_BUILT_WITH_INSULATION) and ( + u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE + ): + # Recommend nothing + return + + raise NotImplementedError("Not implemented yet") if is_solid_brick: @@ -279,6 +302,7 @@ class WallRecommendations(BaseUtility): if u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE: self.find_insulation(u_value) + return raise NotImplementedError("Not implemented yet") @@ -303,9 +327,9 @@ class WallRecommendations(BaseUtility): _, new_u_value = self.calculate_u_value_uplift(u_value, part_u_value) new_u_value = round(new_u_value, 2) - - if new_u_value < self.DIMINISHING_RETURNS_U_VALUE: - # We don't recommend an overkill solution + if new_u_value < self.DIMINISHING_RETURNS_U_VALUE and self.recommendations: + # If we already have a solution that is suitable and does not cross the diminishing returns + # threshold, but meets the part L requirements, we don't need to recommend anything continue # We allow a small tolerance for error so we don't discount the recommendation entirely diff --git a/model_data/tests/test_data/uvalue_estimates_walls.pkl b/model_data/tests/test_data/uvalue_estimates_walls.pkl new file mode 100644 index 00000000..2db094e6 Binary files /dev/null and b/model_data/tests/test_data/uvalue_estimates_walls.pkl differ diff --git a/model_data/tests/test_wall_recommendations.py b/model_data/tests/test_wall_recommendations.py index ab98e5ba..106e30ec 100644 --- a/model_data/tests/test_wall_recommendations.py +++ b/model_data/tests/test_wall_recommendations.py @@ -20,6 +20,59 @@ class TestWallRecommendations: ) as f: return pickle.load(f) + def test_init(self, input_properties, uvalue_estimates): + obj = WallRecommendations(property_instance=input_properties[0], uvalue_estimates=uvalue_estimates) + assert obj + assert obj.property + assert obj.uvalue_estimates + + assert obj.year_built == 2014 + + def test_uvalue_0_16(self, input_properties, uvalue_estimates): + """ + This tests the wall description Average thermal transmittance 0.16 W/m-¦K + The important data for this recommendation is: + - u value of 0.16 + - property built in 2014 + Since properties built after 1990 are typically built with insulation and this property + already has really good insulation, we do NOT recommend any measures for this property + """ + recommender = WallRecommendations(property_instance=input_properties[0], uvalue_estimates=uvalue_estimates) + assert recommender.property.walls["original_description"] == "Average thermal transmittance 0.16 W/m-¦K" + assert recommender.year_built == 2014 + recommender.recommend() + # This should be empty + assert recommender.recommendations == [] + + def test_solid_brick_no_insulation(self, input_properties, uvalue_estimates): + """ + This tests a property with a wall description of Solid brick, as built, no insulation (assumed) + The property was built in 1930, right on the threshold for when cavity walls were introduced + However, we're told this property is solid brik so we assume no cavity. + We're also told that it has no insulation, so we will recommend internal/external wall insulation + + This property is not in a conservation area, however it's a flat so we don't recommend external wall insulation + """ + recommender = WallRecommendations(property_instance=input_properties[1], uvalue_estimates=uvalue_estimates) + assert recommender.property.walls["original_description"] == "Solid brick, as built, no insulation (assumed)" + assert recommender.year_built == 1930 + assert not recommender.ewi_valid + assert recommender.property.in_conservation_area == "not_in_conservation_area" + assert recommender.property.data["property-type"] == "Flat" + + recommender.recommend() + # This should result in some recommendations, all of which should be insulation + assert recommender.recommendations + + rec_types = {rec["type"] for rec in recommender.recommendations} + assert rec_types == {"internal_wall_insulation"} + + # Check the recommendations provide a u value below the minimum + assert all( + rec["new_u_value"] < WallRecommendations.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE for rec in + recommender.recommendations + ) + def test_nothing(self, input_properties, uvalue_estimates): assert input_properties assert uvalue_estimates