mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
wrote complete loft insulation recommendations
This commit is contained in:
parent
57c8788a37
commit
0a549e6916
3 changed files with 168 additions and 4 deletions
|
|
@ -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
|
# 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
|
# in as a dependency and then the optimisers can take the input measures in as part of the setup() method
|
||||||
|
|
||||||
# import pickle
|
# import pickle
|
||||||
# with open("input_properties.pickle", "rb") as f:
|
# with open("input_properties.pickle", "rb") as f:
|
||||||
# input_properties = pickle.load(f)
|
# input_properties = pickle.load(f)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ class RoofRecommendations:
|
||||||
|
|
||||||
DIMINISHING_RETURNS_U_VALUE = 0.14
|
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__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
property_instance: Property,
|
property_instance: Property,
|
||||||
|
|
@ -41,7 +44,7 @@ class RoofRecommendations:
|
||||||
|
|
||||||
# Building regulations part L recommend installing at least 270mm of insulation, however generally we
|
# 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
|
# 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
|
return
|
||||||
|
|
||||||
# If we have a u-value already, need to implement this
|
# If we have a u-value already, need to implement this
|
||||||
|
|
@ -52,7 +55,7 @@ class RoofRecommendations:
|
||||||
|
|
||||||
if self.property.roof["is_pitched"]:
|
if self.property.roof["is_pitched"]:
|
||||||
# We recommend loft insulation
|
# We recommend loft insulation
|
||||||
self.recommend_loft_insulation(u_value)
|
self.recommend_loft_insulation(u_value, insulation_thickness)
|
||||||
return
|
return
|
||||||
|
|
||||||
raise NotImplementedError("Implement me")
|
raise NotImplementedError("Implement me")
|
||||||
|
|
@ -61,10 +64,12 @@ class RoofRecommendations:
|
||||||
def make_loft_insulation_description(material, depth):
|
def make_loft_insulation_description(material, depth):
|
||||||
return f"Install {depth}{material['depth_unit']} of {material['description']}"
|
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
|
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:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -79,6 +84,10 @@ class RoofRecommendations:
|
||||||
for material in loft_insulation_materials:
|
for material in loft_insulation_materials:
|
||||||
|
|
||||||
for depth, cost_per_unit in zip(material["depths"], material["cost"]):
|
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"])
|
part_u_value = r_value_per_mm_to_u_value(depth, material["r_value_per_mm"])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
class TestRoofRecommendations:
|
||||||
|
|
||||||
|
|
@ -37,3 +59,136 @@ class TestRoofRecommendations:
|
||||||
roof_recommender.recommend()
|
roof_recommender.recommend()
|
||||||
|
|
||||||
assert len(roof_recommender.recommendations)
|
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
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue