added in cavity fill recommendation

This commit is contained in:
Khalim Conn-Kowlessar 2023-10-07 05:18:35 +01:00
parent cbef2b5e45
commit 2db0526003
4 changed files with 163 additions and 22 deletions

View file

@ -56,10 +56,26 @@ plan_input = local_data["plan_input"]
uprn_filenames = local_data["uprn_filenames"]
local_property_data = local_data["local_property_data"]
materials = local_data["materials"]
materials_by_type = local_data["materials_by_type"]
materials_by_type = filter_materials(materials)
cleaned = local_data["cleaned"]
cleaning_data = local_data["cleaning_data"]
# Need to find some proper materials
materials_by_type["walls"] += [
{'id': 4, 'type': 'cavity_wall_insulation', 'description': 'Example Material 1',
'depths': None,
'depth_unit': None, 'cost': 20,
'cost_unit': 'gbp_sq_meter', 'r_value_per_mm': 0.0278, 'r_value_unit': 'square_meter_kelvin_per_watt',
'thermal_conductivity': 0.036, 'thermal_conductivity_unit': 'watt_per_meter_kelvin',
'link': None, 'created_at': None, 'is_active': True},
{'id': 10, 'type': "cavity_wall_insulation", 'description': 'Example Material 2',
'depths': None, 'depth_unit': None, 'cost': 25, 'cost_unit': 'gbp_sq_meter',
'r_value_per_mm': 0.02631579, 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.038,
'thermal_conductivity_unit': 'watt_per_meter_kelvin',
'link': None,
'created_at': None, 'is_active': True}
]
epc_client = EpcClient(auth_token="NO-TOKEN")
input_properties = []
@ -102,7 +118,7 @@ for p in input_properties:
# Floor recommendations
floor_recommender = FloorRecommendations(
property_instance=p,
materials=materials_by_type["suspended_floor_insulation"] + materials_by_type["solid_floor_insulation"],
materials=materials_by_type["floor"],
)
floor_recommender.recommend()
@ -113,7 +129,7 @@ for p in input_properties:
wall_recomender = WallRecommendations(
property_instance=p,
materials=materials_by_type["external_wall_insulation"] + materials_by_type["internal_wall_insulation"]
materials=materials_by_type["walls"]
)
wall_recomender.recommend()

View file

@ -11,15 +11,17 @@ import msgpack
def filter_materials(materials):
materials_by_type = defaultdict(list)
for material in materials:
material = row2dict(material)
material_type = material["type"]
materials_by_type[material_type].append(material)
mapping = {
"walls": ["internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation"],
"floor": ["suspended_floor_insulation", "solid_floor_insulation"]
}
# Optionally, you can convert the defaultdict to a normal dict if desired
materials_by_type = dict(materials_by_type)
materials = [row2dict(material) for material in materials]
return materials_by_type
for component, types in mapping.items():
materials_by_type[component] = [part for part in materials if part["type"] in types]
return dict(materials_by_type)
def insert_temp_recommendation_id(property_recommendations):

View file

@ -76,9 +76,7 @@ class WallRecommendations(Definitions):
# recommend internal wall insulation as a possible measure
u_value = self.property.walls["thermal_transmittance"]
is_cavity_wall = self.property.walls["is_cavity_wall"]
is_solid_brick = self.property.walls["is_solid_brick"]
insulation_thickness = self.property.walls["insulation_thickness"]
# We check if the wall is already insulated and if so, we exit
@ -86,9 +84,9 @@ class WallRecommendations(Definitions):
return
if u_value:
if self.property.walls["thermal_transmittance_unit"] != self.U_VALUE_UNIT:
raise NotImplementedError("Haven't handled the case of other u value units yet")
# 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
@ -117,7 +115,7 @@ class WallRecommendations(Definitions):
)
self.estimated_u_value = u_value
if is_solid_brick:
if self.property.walls["is_solid_brick"]:
if u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
self.find_insulation(u_value)
@ -126,8 +124,72 @@ class WallRecommendations(Definitions):
# 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
self.find_cavity_insulation(u_value)
if insulation_thickness not in ["none", None]:
raise ValueError("Implement me")
return
raise NotImplementedError("Not implemented yet")
def find_cavity_insulation(self, u_value):
"""
This method tests different materials to fill the cavity wall, determining which
material will give us the best U-value.
We check for diminishing returns, however this function does not check for meeting building
part L regulations right now
:param u_value: u_value of the starting wall
:return:
"""
cavity_wall_fills = [m for m in self.materials if m["type"] == "cavity_wall_insulation"]
# TODO: Check this and also check the methodology
cavity_width = 125
# Test the different fill options
lowest_selected_u_value = None
recommendations = []
for part in cavity_wall_fills:
part_u_value = r_value_per_mm_to_u_value(cavity_width, part["r_value_per_mm"])
_, new_u_value = calculate_u_value_uplift(u_value, part_u_value)
new_u_value = math.ceil(new_u_value * 100.0) / 100.0
if is_diminishing_returns(
recommendations, new_u_value, lowest_selected_u_value, self.DIMINISHING_RETURNS_U_VALUE
):
continue
lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value)
estimated_cost = part["cost"] * self.property.insulation_wall_area
recommendations.append(
{
"parts": [
get_recommended_part(
part=part,
selected_depth=None,
quantity=self.property.insulation_wall_area,
quantity_unit=QuantityUnits.m2.value,
selected_total_cost=estimated_cost
)
],
"type": "wall_insulation",
"description": f"Fill cavity with {part['description']}",
"starting_u_value": u_value,
"new_u_value": new_u_value,
"sap_points": None,
"cost": estimated_cost,
}
)
self.recommendations = recommendations
def _find_insulation(self, parts, u_value):
lowest_selected_u_value = None
recommendations = []

View file

@ -1,6 +1,7 @@
import os
import pytest
import pickle
import numpy as np
from unittest.mock import Mock, MagicMock
from recommendations.WallRecommendations import WallRecommendations
from backend.Property import Property
@ -190,7 +191,22 @@ internal_wall_insulation_parts = [
},
]
wall_parts = external_wall_insulation_parts + internal_wall_insulation_parts
cavity_wall_insulation_parts = [
{'id': 4, 'type': 'cavity_wall_insulation', 'description': 'Example Material 1',
'depths': None,
'depth_unit': None, 'cost': 20,
'cost_unit': 'gbp_sq_meter', 'r_value_per_mm': 0.0278, 'r_value_unit': 'square_meter_kelvin_per_watt',
'thermal_conductivity': 0.036, 'thermal_conductivity_unit': 'watt_per_meter_kelvin',
'link': None, 'created_at': None, 'is_active': True},
{'id': 10, 'type': "cavity_wall_insulation", 'description': 'Example Material 2',
'depths': None, 'depth_unit': None, 'cost': 25, 'cost_unit': 'gbp_sq_meter',
'r_value_per_mm': 0.02631579, 'r_value_unit': 'square_meter_kelvin_per_watt', 'thermal_conductivity': 0.038,
'thermal_conductivity_unit': 'watt_per_meter_kelvin',
'link': None,
'created_at': None, 'is_active': True}
]
wall_parts = external_wall_insulation_parts + internal_wall_insulation_parts + cavity_wall_insulation_parts
class TestWallRecommendations:
@ -210,19 +226,17 @@ class TestWallRecommendations:
property_mock.data = {"construction-age-band": "1950"} # or any other data that fits your tests
mock_wall_rec_instance = WallRecommendations(
property_mock, "Decile 1", materials=wall_parts
property_mock, materials=wall_parts
)
return mock_wall_rec_instance
def test_init(self, input_properties):
obj = WallRecommendations(
property_instance=input_properties[0],
total_floor_area_group_decile="Decile 1",
materials=wall_parts
)
assert obj
assert obj.property
assert obj.total_floor_area_group_decile == "Decile 1"
def test_uvalue_0_16(self, input_properties):
"""
@ -236,7 +250,6 @@ class TestWallRecommendations:
input_properties[0].year_built = 2014
recommender = WallRecommendations(
property_instance=input_properties[0],
total_floor_area_group_decile="Decile 1",
materials=wall_parts
)
assert recommender.property.walls["original_description"] == "Average thermal transmittance 0.16 W/m-¦K"
@ -261,7 +274,6 @@ class TestWallRecommendations:
recommender = WallRecommendations(
property_instance=input_properties[1],
total_floor_area_group_decile="Decile 1",
materials=wall_parts
)
assert recommender.property.walls["original_description"] == "Solid brick, as built, no insulation (assumed)"
@ -297,7 +309,6 @@ class TestWallRecommendations:
input_properties[6].year_built = 1991
recommender = WallRecommendations(
property_instance=input_properties[6],
total_floor_area_group_decile="Decile 1",
materials=wall_parts
)
@ -377,7 +388,7 @@ class TestWallRecommendationsBase:
@pytest.fixture
def wall_recommendations_instance(self, property_mock):
wall_recommendations_instance = WallRecommendations(
property_mock, "Decile 1", materials=wall_parts
property_mock, materials=wall_parts
)
return wall_recommendations_instance
@ -407,3 +418,53 @@ class TestWallRecommendationsBase:
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())
input_property.walls = {
'original_description': 'Cavity wall, as built, no insulation (assumed)',
'clean_description': 'Cavity wall, as built, no insulation',
'thermal_transmittance': None, 'thermal_transmittance_unit': None,
'is_cavity_wall': True, 'is_filled_cavity': False, 'is_solid_brick': False,
'is_system_built': False, '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_property.age_band = "C"
input_property.insulation_wall_area = 50
recommender = WallRecommendations(
property_instance=input_property,
materials=cavity_wall_insulation_parts
)
assert not recommender.recommendations
recommender.recommend()
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]["cost"], 1000)
assert np.isclose(recommender.recommendations[1]["new_u_value"], 0.26)
assert np.isclose(recommender.recommendations[1]["cost"], 1250)