implementing partial cavity wall fill recommendation

This commit is contained in:
Khalim Conn-Kowlessar 2023-10-09 10:31:08 +08:00
parent 847370ed39
commit dbf5f63e44
7 changed files with 74 additions and 25 deletions

2
.idea/Model.iml generated
View file

@ -7,7 +7,7 @@
<sourceFolder url="file://$MODULE_DIR$/open_uprn" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/recommendations" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="Python 3.10 (model_data)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.10 (backend)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyNamespacePackagesService">

2
.idea/misc.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (model_data)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (backend)" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" />
</component>

View file

@ -9,6 +9,7 @@ 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_wall_u_value
)
from recommendations.config import PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION
class WallRecommendations(Definitions):
@ -35,14 +36,6 @@ class WallRecommendations(Definitions):
# we still consider it as an option
U_VALUE_ERROR = 0.01
# TODO: Review this value against RdSAP
# Page 19 of rdsap here:
# https://files.bregroup.com/bre-co-uk-file-library-copy/filelibrary/SAP/2012/RdSAP-9.93/RdSAP_2012_9.93.pdf
# provides default U-values for solid brick walls depending on age band
DEFAULT_U_VALUES = {
"solid_brick": 2,
}
def __init__(
self,
property_instance: Property,
@ -113,6 +106,7 @@ class WallRecommendations(Definitions):
is_granite_or_whinstone=self.property.walls["is_granite_or_whinstone"],
is_sandstone_or_limestone=self.property.walls["is_sandstone_or_limestone"],
)
self.estimated_u_value = u_value
if self.property.walls["is_solid_brick"]:
@ -127,16 +121,13 @@ class WallRecommendations(Definitions):
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")
self.find_cavity_insulation(u_value, insulation_thickness)
return
raise NotImplementedError("Not implemented yet")
def find_cavity_insulation(self, u_value):
def find_cavity_insulation(self, u_value, insulation_thickness):
"""
This method tests different materials to fill the cavity wall, determining which
material will give us the best U-value.
@ -150,11 +141,15 @@ class WallRecommendations(Definitions):
therefore we'll use 50mm as the base assumption
:param u_value: u_value of the starting wall
:param insulation_thickness: describes the insulation level of the wall. If "below average", we have a partially
filled cavity wall
"""
cavity_wall_fills = [m for m in self.materials if m["type"] == "cavity_wall_insulation"]
cavity_width = 50
if insulation_thickness == "below average":
cavity_width = 50 * (1 - PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION)
# Test the different fill options
lowest_selected_u_value = None
recommendations = []

View file

@ -6,3 +6,11 @@ UPGRADES_MAP = {
'Suspended, no insulation (assumed)': 'Suspended, insulated (assumed)',
'Solid, no insulation (assumed)': 'Solid, insulated (assumed)',
}
PARTIAL_CAVITY_DESCRIPTIONS = [
"Cavity wall, as built, partial insulation",
"Cavity wall, partial insulation",
]
# We assume that the cavity is partially filled with insulation, and is filled 25% full
PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION = 0.25

View file

@ -226,10 +226,10 @@ epc_wall_description_map = {
"Cavity wall, as built, partial insulation": "Filled cavity",
"Cavity wall, filled cavity": "Filled cavity",
"Cavity wall, as built, no insulation": "Cavity as built",
"Cavity wall, as built, insulated": "Unfilled cavity with 100 mm external or internal insulation",
"Cavity wall, as built, insulated": "Filled cavity",
"Cavity wall, with external insulation": "Unfilled cavity with 100 mm external or internal insulation",
"Cavity wall, insulated": "Unfilled cavity with 100 mm external or internal insulation",
'Cavity wall, partial insulation': "Unfilled cavity with 50 mm external or internal insulation",
"Cavity wall, insulated": "Filled cavity",
'Cavity wall, partial insulation': "Filled cavity",
"Cavity wall,": "Cavity as built", # General case of cavity wall without further details
"Cavity wall, filled cavity and external insulation":

View file

@ -7,6 +7,7 @@ from recommendations.rdsap_tables import (
epc_wall_description_map, wall_uvalues_df, default_wall_thickness, table_s9 as s9, table_s10 as s10,
table_s11 as s11
)
from recommendations.config import PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION, PARTIAL_CAVITY_DESCRIPTIONS
def r_value_per_mm_to_u_value(depth_mm: int, r_value_per_mm: float):
@ -146,7 +147,9 @@ def apply_formula_s_5_1_1(is_granite_or_whinstone, is_sandstone_or_limestone, ag
raise ValueError("This should only be called when is_granite_or_whinstone or is_sandstone_or_limestone is True")
def get_wall_u_value(clean_description, age_band, is_granite_or_whinstone, is_sandstone_or_limestone):
def get_wall_u_value(
clean_description, age_band, is_granite_or_whinstone, is_sandstone_or_limestone
):
"""
Given some features about a wall, this function will query the wall u-value table and return the u-value
:param clean_description: Cleaned up description of the wall from the EPC data
@ -156,13 +159,23 @@ def get_wall_u_value(clean_description, age_band, is_granite_or_whinstone, is_sa
:return:
"""
mapped_description = epc_wall_description_map[clean_description]
if clean_description in PARTIAL_CAVITY_DESCRIPTIONS:
# If we have a partial cavity fill, we linearly interpolate the u-value. This isn't necessarily the perfect
# method and how we do this should be explored, however we want to distinguish between the old
filled_uvalue = float(wall_uvalues_df[wall_uvalues_df["Wall_type"] == "Filled cavity"][age_band].values[0])
unfilled_uvalue = float(wall_uvalues_df[wall_uvalues_df["Wall_type"] == "Cavity as built"][age_band].values[0])
mapped_value = wall_uvalues_df[wall_uvalues_df["Wall_type"] == mapped_description][age_band].values[0]
mapped_value = str(
unfilled_uvalue - (PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION * (unfilled_uvalue - filled_uvalue))
)
else:
mapped_description = epc_wall_description_map[clean_description]
if pd.isnull(mapped_value) and "Park home" in mapped_description:
# We don't know enough in this case so we default to 0
return 0
mapped_value = wall_uvalues_df[wall_uvalues_df["Wall_type"] == mapped_description][age_band].values[0]
if pd.isnull(mapped_value) and "Park home" in mapped_description:
# We don't know enough in this case so we default to 0
return 0
if mapped_value == "a":
# The rdSap documentation indicateswe should use a formula to calculate the u-value

View file

@ -470,3 +470,36 @@ class TestCavityWallRecommensations:
assert np.isclose(recommender.recommendations[1]["new_u_value"], 0.26)
assert np.isclose(recommender.recommendations[1]["cost"], 1250)
def test_fill_partial_filled_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, partial insulation (assumed)',
'clean_description': 'Cavity wall, as built, partial 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': 'below average', '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.3
assert np.isclose(recommender.recommendations[0]["new_u_value"], 0.56)
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]["cost"], 1250)