wrote complete loft insulation recommendations

This commit is contained in:
Khalim Conn-Kowlessar 2023-10-24 06:56:29 +11:00
parent 57c8788a37
commit 0a549e6916
3 changed files with 168 additions and 4 deletions

View file

@ -121,7 +121,7 @@ async def trigger_plan(body: PlanTriggerRequest):
# TODO: Move this to a class. We probably want a Recommender class which takes the injects the optimisers
# in as a dependency and then the optimisers can take the input measures in as part of the setup() method
# import pickle
# with open("input_properties.pickle", "rb") as f:
# input_properties = pickle.load(f)

View file

@ -18,6 +18,9 @@ class RoofRecommendations:
DIMINISHING_RETURNS_U_VALUE = 0.14
# It is recommended that lofts should have at least 270mm of insulation
MINIMUM_LOFT_ISULATION_MM = 270
def __init__(
self,
property_instance: Property,
@ -41,7 +44,7 @@ class RoofRecommendations:
# Building regulations part L recommend installing at least 270mm of insulation, however generally we
# experience diminishing returns in terms of SAP once we go beyond around 150mm of insulation
if insulation_thickness < 270:
if insulation_thickness >= self.MINIMUM_LOFT_ISULATION_MM:
return
# If we have a u-value already, need to implement this
@ -52,7 +55,7 @@ class RoofRecommendations:
if self.property.roof["is_pitched"]:
# We recommend loft insulation
self.recommend_loft_insulation(u_value)
self.recommend_loft_insulation(u_value, insulation_thickness)
return
raise NotImplementedError("Implement me")
@ -61,10 +64,12 @@ class RoofRecommendations:
def make_loft_insulation_description(material, depth):
return f"Install {depth}{material['depth_unit']} of {material['description']}"
def recommend_loft_insulation(self, u_value):
def recommend_loft_insulation(self, u_value, insulation_thickness):
"""
This method will recommend which insulation materials to use
:param u_value: U-value of the roof before any retrofit measures have been installed
:param insulation_thickness: Existing Insulation thickness of the loft
:return:
"""
@ -79,6 +84,10 @@ class RoofRecommendations:
for material in loft_insulation_materials:
for depth, cost_per_unit in zip(material["depths"], material["cost"]):
# We make sure we hit a depth of 270mm. We should factor in any existing insulation if the
# loft is already partially insulated
if (depth + insulation_thickness) < self.MINIMUM_LOFT_ISULATION_MM:
continue
part_u_value = r_value_per_mm_to_u_value(depth, material["r_value_per_mm"])

View file

@ -13,6 +13,28 @@ loft_insulation_materials = [
}
]
loft_insulation_materials_50mm_existing = [
{
'id': 18, 'type': 'loft_insulation', 'description': 'Iso Spacesaver Mineral Wool insulation',
'depths': [220, 210], 'depth_unit': 'mm', 'cost': [9, 10], 'cost_unit': 'gbp_sq_meter',
'r_value_per_mm': 0.022727273, 'r_value_unit': 'square_meter_kelvin_per_watt',
'thermal_conductivity': 0.044, 'thermal_conductivity_unit': 'watt_per_meter_kelvin',
'link': 'https://flooringwarehousedirect.co.uk/product/isover-spacesaver-roll-100mm-x-1160mm-x-12-18m-14-13m2/',
'is_active': True
}
]
loft_insulation_materials_150mm_existing = [
{
'id': 18, 'type': 'loft_insulation', 'description': 'Iso Spacesaver Mineral Wool insulation',
'depths': [130, 119], 'depth_unit': 'mm', 'cost': [9, 10], 'cost_unit': 'gbp_sq_meter',
'r_value_per_mm': 0.022727273, 'r_value_unit': 'square_meter_kelvin_per_watt',
'thermal_conductivity': 0.044, 'thermal_conductivity_unit': 'watt_per_meter_kelvin',
'link': 'https://flooringwarehousedirect.co.uk/product/isover-spacesaver-roll-100mm-x-1160mm-x-12-18m-14-13m2/',
'is_active': True
}
]
class TestRoofRecommendations:
@ -37,3 +59,136 @@ class TestRoofRecommendations:
roof_recommender.recommend()
assert len(roof_recommender.recommendations)
def test_loft_insulation_recommendation_50mm_insulation(self):
property_instance2 = Property(id=0, address1="fake", postcode="fake", epc_client=Mock())
property_instance2.age_band = "F"
property_instance2.floor_area = 100
property_instance2.roof = {
'original_description': 'Pitched, 50mm loft insulation (assumed)',
'clean_description': 'Pitched, 50mm loft insulation',
'thermal_transmittance': None,
'thermal_transmittance_unit': None,
'is_pitched': True, 'is_roof_room': False, 'is_loft': True, 'is_flat': False, 'is_thatched': False,
'is_at_rafters': False, 'is_assumed': True, 'has_dwelling_above': False, 'is_valid': True,
'insulation_thickness': '50', 'roof_thermal_transmittance': None, 'roof_insulation_thickness': 'none'
}
roof_recommender2 = RoofRecommendations(
property_instance=property_instance2, materials=loft_insulation_materials
)
assert not roof_recommender2.recommendations
roof_recommender2.recommend()
assert len(roof_recommender2.recommendations) == 1
assert roof_recommender2.recommendations[0]["cost"] == 900
assert roof_recommender2.recommendations[0]["new_u_value"] == 0.14
assert roof_recommender2.recommendations[0]["starting_u_value"] == 0.68
property_instance3 = Property(id=0, address1="fake", postcode="fake", epc_client=Mock())
property_instance3.age_band = "F"
property_instance3.floor_area = 100
property_instance3.roof = {
'original_description': 'Pitched, 50mm loft insulation (assumed)',
'clean_description': 'Pitched, 50mm loft insulation',
'thermal_transmittance': None,
'thermal_transmittance_unit': None,
'is_pitched': True, 'is_roof_room': False, 'is_loft': True, 'is_flat': False, 'is_thatched': False,
'is_at_rafters': False, 'is_assumed': True, 'has_dwelling_above': False, 'is_valid': True,
'insulation_thickness': '50', 'roof_thermal_transmittance': None, 'roof_insulation_thickness': 'none'
}
roof_recommender3 = RoofRecommendations(
property_instance=property_instance3, materials=loft_insulation_materials_50mm_existing
)
assert not roof_recommender3.recommendations
roof_recommender3.recommend()
# The 220mm insulation should be selected, not the 210
assert roof_recommender3.recommendations
assert len(roof_recommender3.recommendations) == 1
assert roof_recommender3.recommendations[0]["parts"][0]["depths"] == [220]
def test_loft_insulation_recommendation_150mm_insulation(self):
property_instance4 = Property(id=0, address1="fake", postcode="fake", epc_client=Mock())
property_instance4.age_band = "F"
property_instance4.floor_area = 100
property_instance4.roof = {
'original_description': 'Pitched, 150mm loft insulation (assumed)',
'clean_description': 'Pitched, 150mm loft insulation',
'thermal_transmittance': None,
'thermal_transmittance_unit': None,
'is_pitched': True, 'is_roof_room': False, 'is_loft': True, 'is_flat': False, 'is_thatched': False,
'is_at_rafters': False, 'is_assumed': True, 'has_dwelling_above': False, 'is_valid': True,
'insulation_thickness': '150', 'roof_thermal_transmittance': None, 'roof_insulation_thickness': 'none'
}
roof_recommender4 = RoofRecommendations(
property_instance=property_instance4, materials=loft_insulation_materials
)
assert not roof_recommender4.recommendations
roof_recommender4.recommend()
assert len(roof_recommender4.recommendations) == 1
assert roof_recommender4.recommendations[0]["cost"] == 900
assert roof_recommender4.recommendations[0]["new_u_value"] == 0.11
assert roof_recommender4.recommendations[0]["starting_u_value"] == 0.3
property_instance5 = Property(id=0, address1="fake", postcode="fake", epc_client=Mock())
property_instance5.age_band = "F"
property_instance5.floor_area = 100
property_instance5.roof = {
'original_description': 'Pitched, 150mm loft insulation (assumed)',
'clean_description': 'Pitched, 150mm loft insulation',
'thermal_transmittance': None,
'thermal_transmittance_unit': None,
'is_pitched': True, 'is_roof_room': False, 'is_loft': True, 'is_flat': False, 'is_thatched': False,
'is_at_rafters': False, 'is_assumed': True, 'has_dwelling_above': False, 'is_valid': True,
'insulation_thickness': '150', 'roof_thermal_transmittance': None, 'roof_insulation_thickness': 'none'
}
roof_recommender5 = RoofRecommendations(
property_instance=property_instance5, materials=loft_insulation_materials_150mm_existing
)
assert not roof_recommender5.recommendations
roof_recommender5.recommend()
# The 130mm insulation should be selected, not the 110
assert roof_recommender5.recommendations
assert len(roof_recommender5.recommendations) == 1
assert roof_recommender5.recommendations[0]["parts"][0]["depths"] == [130]
def test_loft_insulation_recommendation_270mm_insulation(self):
# We shouldn't recommend anything in this case
property_instance6 = Property(id=0, address1="fake", postcode="fake", epc_client=Mock())
property_instance6.age_band = "F"
property_instance6.floor_area = 100
property_instance6.roof = {
'original_description': 'Pitched, 270mm loft insulation (assumed)',
'clean_description': 'Pitched, 270mm loft insulation',
'thermal_transmittance': None,
'thermal_transmittance_unit': None,
'is_pitched': True, 'is_roof_room': False, 'is_loft': True, 'is_flat': False, 'is_thatched': False,
'is_at_rafters': False, 'is_assumed': True, 'has_dwelling_above': False, 'is_valid': True,
'insulation_thickness': '270', 'roof_thermal_transmittance': None, 'roof_insulation_thickness': 'none'
}
roof_recommender6 = RoofRecommendations(
property_instance=property_instance6, materials=loft_insulation_materials
)
assert not roof_recommender6.recommendations
roof_recommender6.recommend()
assert len(roof_recommender6.recommendations) == 0