diff --git a/.idea/Model.iml b/.idea/Model.iml
index b0f9c00d..4413bb06 100644
--- a/.idea/Model.iml
+++ b/.idea/Model.iml
@@ -7,7 +7,7 @@
-
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index ca0e1cd9..3b05c6ac 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/recommendations/WallRecommendations.py b/recommendations/WallRecommendations.py
index 818e7e98..8464c02b 100644
--- a/recommendations/WallRecommendations.py
+++ b/recommendations/WallRecommendations.py
@@ -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 = []
diff --git a/recommendations/config.py b/recommendations/config.py
index 750453b0..20169ffd 100644
--- a/recommendations/config.py
+++ b/recommendations/config.py
@@ -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
diff --git a/recommendations/rdsap_tables.py b/recommendations/rdsap_tables.py
index a622a4cc..0ce139ab 100644
--- a/recommendations/rdsap_tables.py
+++ b/recommendations/rdsap_tables.py
@@ -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":
diff --git a/recommendations/recommendation_utils.py b/recommendations/recommendation_utils.py
index 8289624d..f405d962 100644
--- a/recommendations/recommendation_utils.py
+++ b/recommendations/recommendation_utils.py
@@ -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
diff --git a/recommendations/tests/test_wall_recommendations.py b/recommendations/tests/test_wall_recommendations.py
index eeed8daf..e910a8f5 100644
--- a/recommendations/tests/test_wall_recommendations.py
+++ b/recommendations/tests/test_wall_recommendations.py
@@ -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)