From f288d47e3816214e4050301b24e0efc9241e14b3 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Fri, 22 Sep 2023 11:24:51 +0100 Subject: [PATCH] implemented new u-value methdology in floor recommendations --- recommendations/FloorRecommendations.py | 40 ++++++------ recommendations/recommendation_utils.py | 61 ++----------------- .../tests/test_floor_recommendations.py | 3 +- 3 files changed, 26 insertions(+), 78 deletions(-) diff --git a/recommendations/FloorRecommendations.py b/recommendations/FloorRecommendations.py index 8a5e9cf3..4fda5791 100644 --- a/recommendations/FloorRecommendations.py +++ b/recommendations/FloorRecommendations.py @@ -3,10 +3,11 @@ from typing import List from model_data.BaseUtility import Definitions from datatypes.enums import QuantityUnits from backend.Property import Property -from recommendations.rdsap_tables import default_wall_thickness, age_band_data +from recommendations.rdsap_tables import england_wales_age_band_lookup from recommendations.recommendation_utils import ( r_value_per_mm_to_u_value, calculate_u_value_uplift, is_diminishing_returns, update_lowest_selected_u_value, - get_recommended_part, get_uvalue_estimate, estimate_perimeter, estimate_perimeter_2_rooms + get_recommended_part, estimate_perimeter, estimate_perimeter_2_rooms, get_wall_type, + get_floor_u_value ) @@ -111,29 +112,24 @@ class FloorRecommendations(Definitions): else: raise NotImplementedError("Implement me") - if insulation_thickness == "none": + scaled_num_rooms = number_of_rooms / num_floors - region_str, age_band = self.property.data["construction-age-band"].split(":") - region_str = region_str.strip() - age_band = age_band.strip() - region = self.REGION_LOOKUP[region_str] - - u_value = self._estimate_suspended_floor_u_value( - floor_area=total_floor_area / num_floors, - number_of_rooms=number_of_rooms / num_floors, - insulation_thickness=0, - wall_type=wall_type, - region=region, - age_band=age_band, - ) + if scaled_num_rooms <= 2.5: + estimated_perimeter = estimate_perimeter_2_rooms(total_floor_area / num_floors) else: - u_value = get_uvalue_estimate( - uvalue_estimates=self.uvalue_estimates, - property=self.property, - total_floor_area_group_decile=self.total_floor_area_group_decile - ) + estimated_perimeter = estimate_perimeter(total_floor_area / num_floors, scaled_num_rooms) - self.estimated_u_value = u_value + age_band = england_wales_age_band_lookup[self.property.data["construction-age-band"]] + wall_type = get_wall_type(**self.property.walls) + + self.estimated_u_value = get_floor_u_value( + floor_type="suspended" if is_suspended else "solid", + area=total_floor_area, + perimeter=estimated_perimeter, + age_band=age_band, + insulation_thickness=insulation_thickness, + wall_type=wall_type + ) if is_suspended: # Given the U-value, we recommend underfloor insulation diff --git a/recommendations/recommendation_utils.py b/recommendations/recommendation_utils.py index c121443b..712eabdd 100644 --- a/recommendations/recommendation_utils.py +++ b/recommendations/recommendation_utils.py @@ -128,59 +128,6 @@ def get_recommended_part(part, selected_depth, selected_total_cost, quantity, qu return recommended_part -def get_uvalue_estimate(uvalue_estimates, property: Property, total_floor_area_group_decile): - """ - Wrapper function which contains the methodology to extract a property's walls u-value estimate - when we don't have a true value and if we can't base our assumption off of the material - :return: - """ - - if not uvalue_estimates: - raise ValueError("No U-value estimate found for the given property - investigate") - - # We try and filter on total_floor_area_group_decile - floor_area_filter = [ - x for x in uvalue_estimates if - x["total-floor-area_group"] == total_floor_area_group_decile - ] - - if not floor_area_filter: - # Take a mean of all the u-value estimates - return mean( - [x["median_thermal_transmittance"] for x in uvalue_estimates if x["median_thermal_transmittance"]] - ) - - # Because of how spuriously populated the data is for number-habitable-rooms and number-heated-rooms, - # we will try and filter on these to see if we get a result - - habitable_rooms_filer = [ - x for x in floor_area_filter if - x["number-habitable-rooms"] == property.data["number-habitable-rooms"] - ] - - if not habitable_rooms_filer: - # Take a mean of all the u-value estimates - return mean( - [x["median_thermal_transmittance"] for x in floor_area_filter if x["median_thermal_transmittance"]] - ) - - # Try perform a filter on heated rooms - heated_rooms_filter = [ - x for x in habitable_rooms_filer if - x["number-heated-rooms"] == property.data["number-heated-rooms"] - ] - - if not heated_rooms_filter: - # Take a mean of all the u-value estimates - return mean( - [x["median_thermal_transmittance"] for x in habitable_rooms_filer if x["median_thermal_transmittance"]] - ) - - return mean( - [x["median_thermal_transmittance"] for x in heated_rooms_filter if x["median_thermal_transmittance"]] - ) - - def apply_formula_s_5_1_1(is_granite_or_whinstone, is_sandstone_or_limestone, age_band): """ As the u-value table in https://bregroup.com/wp-content/uploads/2019/09/RdSAP_2012_9.94-20-09-2019.pdf @@ -362,8 +309,12 @@ def estimate_perimeter(floor_area, num_rooms): # Estimate total side length assuming rooms are lined up in a row total_side_length = avg_room_side_length * num_rooms - # Assuming the house is a square (which is a very rough approximation), compute the perimeter - perimeter = math.sqrt(total_side_length) * 4 + # Estimate the length and width of the property assuming it is rectangular + length = total_side_length / 2 + width = floor_area / length + + # Compute the perimeter of the property + perimeter = 2 * (length + width) return perimeter diff --git a/recommendations/tests/test_floor_recommendations.py b/recommendations/tests/test_floor_recommendations.py index ee52abe2..7023d70f 100644 --- a/recommendations/tests/test_floor_recommendations.py +++ b/recommendations/tests/test_floor_recommendations.py @@ -101,7 +101,7 @@ class TestWallRecommendations: uvalue_estimates_mock = Mock() - mock_wall_rec_instance = FloorRecommendations(property_mock, uvalue_estimates_mock, "Decile 1") + mock_wall_rec_instance = FloorRecommendations(property_mock, uvalue_estimates_mock, "Decile 1", parts) return mock_wall_rec_instance def test_init(self, input_properties, uvalue_estimates): @@ -135,6 +135,7 @@ class TestWallRecommendations: """ input_properties[2].floor_area = 50 + input_properties[2].walls["is_park_home"] = False recommender = FloorRecommendations( property_instance=input_properties[2],