diff --git a/backend/app/plan/router.py b/backend/app/plan/router.py index ab0e32b5..400b2dee 100644 --- a/backend/app/plan/router.py +++ b/backend/app/plan/router.py @@ -122,6 +122,12 @@ async def trigger_plan(body: PlanTriggerRequest): # import pickle # with open("input_properties.pickle", "rb") as f: # input_properties = pickle.load(f) + # + # with open("cleaned.pickle", "rb") as f: + # cleaned = pickle.load(f) + # + # with open("materials_by_type.pickle", "wb") as f: + # materials_by_type = pickle.load(f) recommendations = {} recommendations_scoring_data = [] diff --git a/backend/app/plan/utils.py b/backend/app/plan/utils.py index 0565ab94..85a02b61 100644 --- a/backend/app/plan/utils.py +++ b/backend/app/plan/utils.py @@ -86,7 +86,7 @@ def create_recommendation_scoring_data( } # Set staring u-values if we don't have them - if not scoring_dict["walls_thermal_transmittance"]: + if scoring_dict["walls_thermal_transmittance"] is None: scoring_dict["walls_thermal_transmittance"] = get_wall_u_value( clean_description=property.walls["clean_description"], age_band=property.age_band, @@ -94,7 +94,7 @@ def create_recommendation_scoring_data( is_sandstone_or_limestone=property.walls["is_sandstone_or_limestone"] ) - if not scoring_dict["floor_thermal_transmittance"]: + if scoring_dict["floor_thermal_transmittance"] is None: scoring_dict["floor_thermal_transmittance"] = get_floor_u_value( floor_type=property.floor_type, area=property.floor_area, @@ -104,7 +104,7 @@ def create_recommendation_scoring_data( age_band=property.age_band, ) - if not scoring_dict["roof_thermal_transmittance"]: + if scoring_dict["roof_thermal_transmittance"] is None: scoring_dict["roof_thermal_transmittance"] = get_roof_u_value( insulation_thickness=property.roof["insulation_thickness"], has_dwelling_above=property.roof["has_dwelling_above"], @@ -130,7 +130,7 @@ def create_recommendation_scoring_data( scoring_dict["walls_thermal_transmittance_ENDING"] = recommendation["new_u_value"] scoring_dict["walls_insulation_thickness_ENDING"] = "above average" else: - if not scoring_dict["walls_thermal_transmittance_ENDING"]: + if scoring_dict["walls_thermal_transmittance_ENDING"] is None: scoring_dict["walls_thermal_transmittance_ENDING"] = get_wall_u_value( clean_description=property.walls["clean_description"], age_band=property.age_band, @@ -151,7 +151,7 @@ def create_recommendation_scoring_data( # We don't really see above average for this in the training data scoring_dict["floor_insulation_thickness_ENDING"] = "average" else: - if not scoring_dict["floor_thermal_transmittance_ENDING"]: + if scoring_dict["floor_thermal_transmittance_ENDING"] is None: scoring_dict["floor_thermal_transmittance_ENDING"] = get_floor_u_value( floor_type=property.floor_type, area=property.floor_area, @@ -167,7 +167,7 @@ def create_recommendation_scoring_data( if recommendation["type"] not in ["wall_insulation", "floor_insulation"]: raise NotImplementedError("Implement me") - if not scoring_dict["roof_thermal_transmittance_ENDING"]: + if scoring_dict["roof_thermal_transmittance_ENDING"] is None: scoring_dict["roof_thermal_transmittance_ENDING"] = get_roof_u_value( insulation_thickness=property.roof["insulation_thickness"], has_dwelling_above=property.roof["has_dwelling_above"], @@ -180,7 +180,7 @@ def create_recommendation_scoring_data( is_at_rafters=property.roof["is_at_rafters"], ) - if scoring_dict["roof_insulation_thickness_ENDING"] is None: - scoring_dict["roof_insulation_thickness_ENDING"] = "none" + if scoring_dict["roof_insulation_thickness_ENDING"] is None: + scoring_dict["roof_insulation_thickness_ENDING"] = "none" return scoring_dict diff --git a/backend/tests/test_sap_model_prep.py b/backend/tests/test_sap_model_prep.py index 4f36dbe8..c11a0fae 100644 --- a/backend/tests/test_sap_model_prep.py +++ b/backend/tests/test_sap_model_prep.py @@ -1,15 +1,16 @@ from backend.Property import Property from etl.epc.DataProcessor import DataProcessor -from backend.app.plan.utils import create_recommendation_scoring_data +from backend.app.plan.utils import create_recommendation_scoring_data, get_cleaned from etl.epc.settings import COLUMNS_TO_MERGE_ON from epc_api.client import EpcClient import pandas as pd -import os import pytest -import pickle + +from utils.s3 import read_dataframe_from_s3_parquet from tqdm import tqdm +# import pickle # with open("sap_change_dataset.pickle", "rb") as f: # sap_change_dataset = pickle.load(f) # @@ -109,18 +110,14 @@ from tqdm import tqdm class TestSapModelPrep: @pytest.fixture - def cleaned(self): - with open( - os.path.abspath(os.path.dirname(__file__)) + "/test_data/cleaned.pickle", "rb" - ) as f: - return pickle.load(f) + def cleaning_data(self): + return read_dataframe_from_s3_parquet( + bucket_name="retrofit-data-dev", file_key="sap_change_model/cleaning_dataset.parquet", + ) @pytest.fixture - def cleaning_data(self): - with open( - os.path.abspath(os.path.dirname(__file__)) + "/test_data/cleaning_data.pickle", "rb" - ) as f: - return pickle.load(f) + def cleaned(self): + return get_cleaned() def test_fill_cavity_wall(self, cleaned, cleaning_data): """ @@ -395,7 +392,7 @@ class TestSapModelPrep: 'MULTI_GLAZE_PROPORTION_ENDING': 61.0, 'LOW_ENERGY_LIGHTING_ENDING': 17.0, 'NUMBER_OPEN_FIREPLACES_ENDING': 0.0, 'EXTENSION_COUNT_ENDING': 0.0, 'TOTAL_FLOOR_AREA_ENDING': 70.0, 'FLOOR_HEIGHT_ENDING': 3.64, 'DAYS_TO_STARTING': 2266, 'DAYS_TO_ENDING': 2307, - 'walls_thermal_transmittance': 0.45, 'is_cavity_wall': False, 'is_filled_cavity': False, + 'walls_thermal_transmittance': 1.7, 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': True, 'is_system_built': False, 'is_timber_frame': False, 'is_granite_or_whinstone': False, 'is_as_built': True, 'is_cob': False, 'is_sandstone_or_limestone': False, 'is_park_home': False, 'walls_insulation_thickness': 'none', @@ -482,7 +479,7 @@ class TestSapModelPrep: ending_lodgement_date2 = '2020-11-24' - starting_epc_data2["DAYS_TO_ENDING"] = data_processor2.calculate_days_to(ending_lodgement_date2) + ending_epc_data2["DAYS_TO_ENDING"] = data_processor2.calculate_days_to(ending_lodgement_date2) recommendation2 = { "recommendation_id": 0, diff --git a/recommendations/WallRecommendations.py b/recommendations/WallRecommendations.py index 379f7026..b3eea7e0 100644 --- a/recommendations/WallRecommendations.py +++ b/recommendations/WallRecommendations.py @@ -112,15 +112,6 @@ class WallRecommendations(Definitions): self.estimated_u_value = u_value - if self.property.walls["is_solid_brick"]: - - if u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE: - self.find_insulation(u_value) - return - - # If the u-value is within regulations, we don't do anything - return - if is_cavity_wall: if u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE: # Test filling cavity @@ -128,9 +119,13 @@ class WallRecommendations(Definitions): return - logger.error("Not implemented yet") + # Remaining wall types are treated with IWI or EWI + if u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE: + self.find_insulation(u_value) + return + + # If the u-value is within regulations, we don't do anything return - # NotImplementedError("Not implemented yet") def find_cavity_insulation(self, u_value, insulation_thickness): """ @@ -273,63 +268,6 @@ class WallRecommendations(Definitions): self.recommendations += ewi_recommendations + iwi_recommendations - # We also can recommend both internal and external wall insulation together - # By looping through ewi first, if there is nothing there, that ensures not combinations are tested - for ewi_part in ewi_parts: - for iwi_part in iwi_parts: - for (ewi_depth, ewi_cost_per_unit), (iwi_depth, iwi_cost_per_unit) in itertools.product( - zip(ewi_part["depths"], ewi_part["cost"]), - zip(iwi_part["depths"], iwi_part["cost"]) - ): - ewi_part_u_value = r_value_per_mm_to_u_value(ewi_depth, ewi_part["r_value_per_mm"]) - iwi_part_u_value = r_value_per_mm_to_u_value(iwi_depth, iwi_part["r_value_per_mm"]) - - # First calculate the new U-value after applying external wall insulation - _, ewi_new_u_value = calculate_u_value_uplift(u_value, ewi_part_u_value) - # Then calculate the new U-value after applying internal wall insulation - _, combined_new_u_value = calculate_u_value_uplift(ewi_new_u_value, iwi_part_u_value) - combined_new_u_value = round(combined_new_u_value, 2) - - if combined_new_u_value < self.DIMINISHING_RETURNS_U_VALUE: - # We don't recommend an overkill solution - continue - - # Check if the combined new U-value meets the requirement - if combined_new_u_value - self.U_VALUE_ERROR <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE: - # Here you might want to define a way to add both recommendations together. - # For now, I'm adding them as separate items in the list - ewi_esimtated_cost = ewi_cost_per_unit * self.property.insulation_wall_area - iwi_esimtated_cost = iwi_cost_per_unit * self.property.insulation_wall_area - - recommendation = { - "parts": [ - get_recommended_part( - part=ewi_part, - selected_depth=ewi_depth, - quantity=self.property.insulation_wall_area, - quantity_unit=QuantityUnits.m2.value, - selected_total_cost=ewi_esimtated_cost - ), - get_recommended_part( - part=iwi_part, - selected_depth=iwi_depth, - quantity=self.property.insulation_wall_area, - quantity_unit=QuantityUnits.m2.value, - selected_total_cost=iwi_esimtated_cost - ) - ], - "type": "wall_insulation", - "description": ( - "Install " + self._make_description(ewi_part, ewi_depth) + " and " + - self._make_description(iwi_part, iwi_depth) - ), - "starting_u_value": u_value, - "new_u_value": combined_new_u_value, - "sap_points": None, - "cost": ewi_esimtated_cost + iwi_esimtated_cost, - } - self.recommendations.append(recommendation) - self.prune_diminishing_recommendations() @staticmethod diff --git a/recommendations/tests/test_data/wall_uvalue_test_cases.py b/recommendations/tests/test_data/wall_uvalue_test_cases.py index 1cc6823c..e0c6ebe3 100644 --- a/recommendations/tests/test_data/wall_uvalue_test_cases.py +++ b/recommendations/tests/test_data/wall_uvalue_test_cases.py @@ -4,21 +4,21 @@ wall_uvalue_test_cases = [ "age_band": "A", "is_granite_or_whinstone": False, "is_sandstone_or_limestone": False, - "uvalue": 0.7 + "uvalue": 1.3 }, { "clean_description": "Cavity wall, as built, partial insulation", "age_band": "F", "is_granite_or_whinstone": False, "is_sandstone_or_limestone": False, - "uvalue": 0.4 + "uvalue": 0.85 }, { "clean_description": "Cavity wall, as built, partial insulation", - "age_band": "F", + "age_band": "G", "is_granite_or_whinstone": False, "is_sandstone_or_limestone": False, - "uvalue": 0.4 + "uvalue": 0.5375 }, { diff --git a/recommendations/tests/test_floor_recommendations.py b/recommendations/tests/test_floor_recommendations.py index f3df168f..f34bbe81 100644 --- a/recommendations/tests/test_floor_recommendations.py +++ b/recommendations/tests/test_floor_recommendations.py @@ -118,6 +118,7 @@ class TestFloorRecommendations: input_properties[2].age_band = "A" input_properties[2].perimeter = 20 input_properties[2].wall_type = "solid brick" + input_properties[2].floor_type = "suspended" recommender = FloorRecommendations( property_instance=input_properties[2], @@ -160,6 +161,7 @@ class TestFloorRecommendations: input_properties[4].age_band = "B" input_properties[4].perimeter = 50 input_properties[4].wall_type = "solid brick" + input_properties[4].floor_type = "solid" recommender = FloorRecommendations( property_instance=input_properties[4], diff --git a/recommendations/tests/test_wall_recommendations.py b/recommendations/tests/test_wall_recommendations.py index e910a8f5..3663364c 100644 --- a/recommendations/tests/test_wall_recommendations.py +++ b/recommendations/tests/test_wall_recommendations.py @@ -407,36 +407,8 @@ class TestWallRecommendationsBase: wall_recommendations_instance.property.data = {"property-type": "house"} assert wall_recommendations_instance.ewi_valid is True - def test_recommend_without_u_value(self, wall_recommendations_instance): - wall_recommendations_instance.property.walls = { - "thermal_transmittance": None, - "is_solid_brick": False, - "is_cavity_wall": False, - "insulation_thickness": "none", - "clean_description": "Solid brick, as built, no insulation", - "is_granite_or_whinstone": False, - "is_sandstone_or_limestone": False, - } - wall_recommendations_instance.property.age_band = "A" - with pytest.raises(NotImplementedError): - wall_recommendations_instance.recommend() - class TestCavityWallRecommensations: - data = { - 'low-energy-fixed-light-count': '', 'address': '123 Fake Street', - 'floor-height': '', 'construction-age-band': 'England and Wales: 1950-1966', - 'address3': '', 'property-type': 'House', 'local-authority-label': 'Melton', - 'county': 'Leicestershire', 'postcode': 'LE14 2QH', - 'solar-water-heating-flag': 'N', 'constituency': 'E14000909', - 'number-heated-rooms': '5', 'local-authority': 'E07000133', 'built-form': 'End-Terrace', - 'address1': '1, 23 fake', 'total-floor-area': '85.0', 'environment-impact-current': '49', - 'number-habitable-rooms': 3, 'address2': 'Fake', 'posttown': 'IDK', - 'walls-energy-eff': 'Poor', 'current-energy-rating': 'D', - 'transaction-type': 'ECO assessment', 'uprn': '999', 'current-energy-efficiency': '57', - 'lodgement-date': '2019-07-10', 'lmk-key': '999', 'tenure': 'rental (private)', 'floor-level': 'NODATA!', - 'walls-description': 'Cavity wall, as built, no insulation (assumed)', - } def test_fill_empty_cavity(self): input_property = Property(id=1, postcode="F4k3", address1="123 fake street", epc_client=Mock()) @@ -465,10 +437,10 @@ class TestCavityWallRecommensations: assert recommender.recommendations assert recommender.estimated_u_value == 1.5 - assert np.isclose(recommender.recommendations[0]["new_u_value"], 0.25) + assert np.isclose(recommender.recommendations[0]["new_u_value"], 0.37) assert np.isclose(recommender.recommendations[0]["cost"], 1000) - assert np.isclose(recommender.recommendations[1]["new_u_value"], 0.26) + assert np.isclose(recommender.recommendations[1]["new_u_value"], 0.38) assert np.isclose(recommender.recommendations[1]["cost"], 1250) def test_fill_partial_filled_cavity(self): @@ -498,8 +470,233 @@ class TestCavityWallRecommensations: assert recommender.recommendations assert recommender.estimated_u_value == 1.3 - assert np.isclose(recommender.recommendations[0]["new_u_value"], 0.56) + assert np.isclose(recommender.recommendations[0]["new_u_value"], 0.43) assert np.isclose(recommender.recommendations[0]["cost"], 1000) - assert np.isclose(recommender.recommendations[1]["new_u_value"], 0.57) + assert np.isclose(recommender.recommendations[1]["new_u_value"], 0.45) assert np.isclose(recommender.recommendations[1]["cost"], 1250) + + def test_system_built_wall(self): + input_property2 = Property(id=1, postcode="F4k3 2", address1="223 fake street", epc_client=Mock()) + input_property2.walls = { + 'original_description': 'System built, as built, no insulation (assumed)', + 'clean_description': 'System built, as built, no insulation', + 'thermal_transmittance': None, 'thermal_transmittance_unit': None, + 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, + 'is_system_built': True, 'is_timber_frame': False, 'is_granite_or_whinstone': False, + 'is_as_built': True, 'is_cob': False, 'is_assumed': True, + 'is_sandstone_or_limestone': False, 'is_park_home': False, + 'insulation_thickness': 'none', 'external_insulation': False, + 'internal_insulation': False + } + input_property2.age_band = "F" + input_property2.insulation_wall_area = 120 + input_property2.restricted_measures = False + input_property2.data = {"property-type": "house"} + + assert input_property2.walls["is_system_built"] + + recommender2 = WallRecommendations( + property_instance=input_property2, + materials=internal_wall_insulation_parts + external_wall_insulation_parts + ) + + assert not recommender2.recommendations + + recommender2.recommend() + + assert recommender2.recommendations + assert len(recommender2.recommendations) == 6 + assert recommender2.estimated_u_value == 1 + assert np.isclose(recommender2.recommendations[0]["new_u_value"], 0.29) + assert np.isclose(recommender2.recommendations[0]["cost"], 10800) + assert recommender2.recommendations[0]["parts"][0]["type"] == "external_wall_insulation" + assert recommender2.recommendations[0]["parts"][0]["depths"] == [90] + + assert np.isclose(recommender2.recommendations[5]["new_u_value"], 0.29) + assert np.isclose(recommender2.recommendations[5]["cost"], 2400) + assert recommender2.recommendations[5]["parts"][0]["type"] == "internal_wall_insulation" + assert recommender2.recommendations[5]["parts"][0]["depths"] == [20] + + assert np.isclose(recommender2.recommendations[3]["new_u_value"], 0.28) + assert np.isclose(recommender2.recommendations[3]["cost"], 4800) + assert recommender2.recommendations[3]["parts"][0]["type"] == "external_wall_insulation" + assert recommender2.recommendations[3]["parts"][0]["depths"] == [40] + + def test_timber_frame_wall(self): + input_property3 = Property(id=1, postcode="F4k3 2", address1="223 fake street", epc_client=Mock()) + input_property3.walls = { + 'original_description': 'Timber frame, as built, no insulation (assumed)', + 'clean_description': 'Timber frame, as built, no insulation', + 'thermal_transmittance': None, 'thermal_transmittance_unit': None, + 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, + 'is_system_built': False, 'is_timber_frame': True, 'is_granite_or_whinstone': False, + 'is_as_built': True, 'is_cob': False, 'is_assumed': True, + 'is_sandstone_or_limestone': False, 'is_park_home': False, + 'insulation_thickness': 'none', 'external_insulation': False, + 'internal_insulation': False + } + input_property3.age_band = "B" + input_property3.insulation_wall_area = 99 + input_property3.restricted_measures = False + input_property3.data = {"property-type": "house"} + + assert input_property3.walls["is_timber_frame"] + + recommender3 = WallRecommendations( + property_instance=input_property3, + materials=internal_wall_insulation_parts + external_wall_insulation_parts + ) + + assert not recommender3.recommendations + + recommender3.recommend() + + assert recommender3.recommendations + assert len(recommender3.recommendations) == 2 + assert recommender3.estimated_u_value == 1.9 + assert np.isclose(recommender3.recommendations[0]["new_u_value"], 0.26) + assert np.isclose(recommender3.recommendations[0]["cost"], 12375) + assert recommender3.recommendations[0]["parts"][0]["type"] == "external_wall_insulation" + assert recommender3.recommendations[0]["parts"][0]["depths"] == [125] + + assert np.isclose(recommender3.recommendations[1]["new_u_value"], 0.26) + assert np.isclose(recommender3.recommendations[1]["cost"], 4950) + assert recommender3.recommendations[1]["parts"][0]["type"] == "external_wall_insulation" + assert recommender3.recommendations[1]["parts"][0]["depths"] == [50] + + def test_granite_or_whinstone_wall(self): + input_property4 = Property(id=1, postcode="F4k3 2", address1="223 fake street", epc_client=Mock()) + input_property4.walls = { + 'original_description': 'Granite or whinstone, as built, no insulation (assumed)', + 'clean_description': 'Granite or whinstone, as built, no insulation', + 'thermal_transmittance': None, 'thermal_transmittance_unit': None, + 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, + 'is_system_built': False, 'is_timber_frame': False, 'is_granite_or_whinstone': True, + 'is_as_built': True, 'is_cob': False, 'is_assumed': True, + 'is_sandstone_or_limestone': False, 'is_park_home': False, + 'insulation_thickness': 'none', 'external_insulation': False, + 'internal_insulation': False + } + input_property4.age_band = "A" + input_property4.insulation_wall_area = 223 + input_property4.restricted_measures = False + input_property4.data = {"property-type": "Bungalow"} + + assert input_property4.walls["is_granite_or_whinstone"] + + recommender4 = WallRecommendations( + property_instance=input_property4, + materials=internal_wall_insulation_parts + external_wall_insulation_parts + ) + + assert not recommender4.recommendations + + recommender4.recommend() + + assert recommender4.recommendations + assert len(recommender4.recommendations) == 2 + assert recommender4.estimated_u_value == 2.3 + assert np.isclose(recommender4.recommendations[0]["new_u_value"], 0.27) + assert np.isclose(recommender4.recommendations[0]["cost"], 27875) + assert recommender4.recommendations[0]["parts"][0]["type"] == "external_wall_insulation" + assert recommender4.recommendations[0]["parts"][0]["depths"] == [125] + + assert np.isclose(recommender4.recommendations[1]["new_u_value"], 0.27) + assert np.isclose(recommender4.recommendations[1]["cost"], 11150) + assert recommender4.recommendations[1]["parts"][0]["type"] == "external_wall_insulation" + assert recommender4.recommendations[1]["parts"][0]["depths"] == [50] + + def test_cob_wall(self): + input_property5 = Property(id=1, postcode="F4k3 2", address1="223 fake street", epc_client=Mock()) + input_property5.walls = { + 'original_description': 'Cob, as built', + 'clean_description': 'Cob, as built', + 'thermal_transmittance': None, 'thermal_transmittance_unit': None, + 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, + 'is_system_built': False, 'is_timber_frame': False, 'is_granite_or_whinstone': False, + 'is_as_built': False, 'is_cob': True, 'is_assumed': False, + 'is_sandstone_or_limestone': False, 'is_park_home': False, + 'insulation_thickness': 'none', 'external_insulation': False, + 'internal_insulation': False + } + input_property5.age_band = "E" + input_property5.insulation_wall_area = 77 + input_property5.restricted_measures = False + input_property5.data = {"property-type": "Bungalow"} + + assert input_property5.walls["is_cob"] + + recommender5 = WallRecommendations( + property_instance=input_property5, + materials=internal_wall_insulation_parts + external_wall_insulation_parts + ) + + assert not recommender5.recommendations + + recommender5.recommend() + + assert recommender5.recommendations + assert len(recommender5.recommendations) == 9 + assert recommender5.estimated_u_value == 0.8 + assert np.isclose(recommender5.recommendations[0]["new_u_value"], 0.29) + assert np.isclose(recommender5.recommendations[0]["cost"], 6160) + assert recommender5.recommendations[0]["parts"][0]["type"] == "external_wall_insulation" + assert recommender5.recommendations[0]["parts"][0]["depths"] == [80] + + assert np.isclose(recommender5.recommendations[3]["new_u_value"], 0.26) + assert np.isclose(recommender5.recommendations[3]["cost"], 7700) + assert recommender5.recommendations[3]["parts"][0]["type"] == "external_wall_insulation" + assert recommender5.recommendations[3]["parts"][0]["depths"] == [100] + + assert np.isclose(recommender5.recommendations[6]["new_u_value"], 0.26) + assert np.isclose(recommender5.recommendations[6]["cost"], 7700) + assert recommender5.recommendations[6]["parts"][0]["type"] == "internal_wall_insulation" + assert recommender5.recommendations[6]["parts"][0]["depths"] == [100] + + def test_sandstone_or_limestone_wall(self): + input_property6 = Property(id=1, postcode="F4k3 6", address1="623 fake street", epc_client=Mock()) + input_property6.walls = { + 'original_description': 'Sandstone or limestone, as built, no insulation (assumed)', + 'clean_description': 'Sandstone or limestone, as built, no insulation', + 'thermal_transmittance': None, 'thermal_transmittance_unit': None, + 'is_cavity_wall': False, 'is_filled_cavity': False, 'is_solid_brick': False, + 'is_system_built': False, 'is_timber_frame': False, 'is_granite_or_whinstone': False, + 'is_as_built': False, 'is_cob': False, 'is_assumed': False, + 'is_sandstone_or_limestone': True, 'is_park_home': False, + 'insulation_thickness': 'none', 'external_insulation': False, + 'internal_insulation': False + } + input_property6.age_band = "F" + input_property6.insulation_wall_area = 350 + input_property6.restricted_measures = False + input_property6.data = {"property-type": "House"} + + assert input_property6.walls["is_sandstone_or_limestone"] + + recommender6 = WallRecommendations( + property_instance=input_property6, + materials=internal_wall_insulation_parts + external_wall_insulation_parts + ) + + assert not recommender6.recommendations + + recommender6.recommend() + + assert recommender6.recommendations + assert len(recommender6.recommendations) == 6 + assert recommender6.estimated_u_value == 1 + assert np.isclose(recommender6.recommendations[0]["new_u_value"], 0.29) + assert np.isclose(recommender6.recommendations[0]["cost"], 31500) + assert recommender6.recommendations[0]["parts"][0]["type"] == "external_wall_insulation" + assert recommender6.recommendations[0]["parts"][0]["depths"] == [90] + + assert np.isclose(recommender6.recommendations[2]["new_u_value"], 0.28) + assert np.isclose(recommender6.recommendations[2]["cost"], 35000) + assert recommender6.recommendations[2]["parts"][0]["type"] == "external_wall_insulation" + assert recommender6.recommendations[2]["parts"][0]["depths"] == [100] + + assert np.isclose(recommender6.recommendations[4]["new_u_value"], 0.28) + assert np.isclose(recommender6.recommendations[4]["cost"], 35000) + assert recommender6.recommendations[4]["parts"][0]["type"] == "internal_wall_insulation" + assert recommender6.recommendations[4]["parts"][0]["depths"] == [100]