mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
wrote unit tests for room roof insulation
This commit is contained in:
parent
c96fafa701
commit
15e48c2165
3 changed files with 266 additions and 4 deletions
|
|
@ -67,6 +67,10 @@ class RoofRecommendations:
|
|||
def make_loft_insulation_description(material, depth):
|
||||
return f"Install {depth}{material['depth_unit']} of {material['description']}"
|
||||
|
||||
@staticmethod
|
||||
def make_room_roof_insulation(material, depth):
|
||||
return f"Insulate your room roof with {depth}{material['depth_unit']} of {material['description']}"
|
||||
|
||||
def recommend_loft_insulation(self, u_value, insulation_thickness):
|
||||
|
||||
"""
|
||||
|
|
@ -81,6 +85,8 @@ class RoofRecommendations:
|
|||
# from the base layer
|
||||
|
||||
loft_insulation_materials = [m for m in self.materials if m["type"] == "loft_insulation"]
|
||||
if not loft_insulation_materials:
|
||||
raise ValueError("No loft insulation materials found")
|
||||
|
||||
lowest_selected_u_value = None
|
||||
recommendations = []
|
||||
|
|
@ -149,7 +155,93 @@ class RoofRecommendations:
|
|||
2) Insulation of external walls is covered by the wall recommendation class
|
||||
3) We assume a "Gable" roof type
|
||||
|
||||
:param u_value:
|
||||
:param insulation_thickness:
|
||||
Further, we recommend internal roof insulation for the room in roof
|
||||
|
||||
The following document contains details around best practices for insulating a room in roof
|
||||
https://assets.publishing.service.gov.uk/media/61d727d18fa8f50594b59305/retrofit-room-in-roof-insulation-best
|
||||
-practice.pdf
|
||||
Of particular interest are the following:
|
||||
|
||||
We also follow advide provided in this article on the Energy Saving Trust website, providing
|
||||
high level guidance around roof insulation:
|
||||
https://energysavingtrust.org.uk/advice/roof-and-loft-insulation/
|
||||
|
||||
To insulate a warm loft, the following advice is given
|
||||
"An alternative way to insulate your loft is to fit rigid insulation boards between and over the rafters.
|
||||
Rafters are the sloping timbers that make up the roof itself."
|
||||
|
||||
To then insulate a room roof, the following recommendation is provided:
|
||||
"If you want to use your loft as a living space, or it is already being used as a living space,
|
||||
then you need to make sure that all the walls and ceilings between a heated room and an unheated space
|
||||
are insulated.
|
||||
|
||||
- Sloping ceilings can be insulated in the same way as for a warm roof,
|
||||
but with a layer of plasterboard on the inside of the insulation.
|
||||
- Vertical walls can be insulated in the same way.
|
||||
- Flat ceilings can be insulated like a standard loft.
|
||||
"
|
||||
|
||||
:param u_value: Current u-value of the roof
|
||||
:param insulation_thickness: Current insulation thickness of the roof
|
||||
:return:
|
||||
"""
|
||||
|
||||
roof_roof_insulation_materials = [m for m in self.materials if m["type"] == "room_roof_insulation"]
|
||||
if not roof_roof_insulation_materials:
|
||||
raise ValueError("No room in roof insulation materials found")
|
||||
|
||||
if self.property.pitched_roof_area is None:
|
||||
raise ValueError("pitched_roof_area not included as property attribute")
|
||||
|
||||
lowest_selected_u_value = None
|
||||
recommendations = []
|
||||
for material in roof_roof_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"])
|
||||
|
||||
_, 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 I have a lowest U value and my new u value is higher than that but lower than the
|
||||
# diminishing returns threshold, it can be considered
|
||||
|
||||
# If I have a lowest U value and my new u value is lower than the lowest value, it's
|
||||
# further into the diminishing returns threshold and can shouldn't be
|
||||
|
||||
if is_diminishing_returns(
|
||||
recommendations, new_u_value, lowest_selected_u_value, self.DIMINISHING_RETURNS_U_VALUE
|
||||
):
|
||||
continue
|
||||
|
||||
# We allow a small tolerance for error so we don't discount the recommendation entirely
|
||||
if new_u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
||||
lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value)
|
||||
|
||||
estimated_cost = cost_per_unit * self.property.pitched_roof_area
|
||||
|
||||
recommendations.append(
|
||||
{
|
||||
"parts": [
|
||||
get_recommended_part(
|
||||
part=material,
|
||||
selected_depth=depth,
|
||||
quantity=self.property.pitched_roof_area,
|
||||
quantity_unit=QuantityUnits.m2.value,
|
||||
selected_total_cost=estimated_cost
|
||||
)
|
||||
],
|
||||
"type": "roof_insulation",
|
||||
"description": self.make_room_roof_insulation(material, depth),
|
||||
"starting_u_value": u_value,
|
||||
"new_u_value": new_u_value,
|
||||
"sap_points": None,
|
||||
"cost": estimated_cost,
|
||||
}
|
||||
)
|
||||
|
||||
self.recommendations = recommendations
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import numpy as np
|
||||
import pytest
|
||||
import math
|
||||
from unittest.mock import MagicMock
|
||||
|
|
@ -344,3 +345,58 @@ def test_park_home():
|
|||
assert recommendation_utils.get_floor_u_value(
|
||||
'suspended', 100, 40, 'A', 'park home', insulation_thickness="20mm"
|
||||
) == 0
|
||||
|
||||
|
||||
def test_esimtate_pitched_roof_area():
|
||||
roof_area1 = recommendation_utils.esimtate_pitched_roof_area(
|
||||
floor_area=100, floor_height=2
|
||||
)
|
||||
|
||||
assert np.isclose(roof_area1, 107.70329614269008)
|
||||
|
||||
# As the floor height gets bigger, the area should get bigger
|
||||
roof_area2 = recommendation_utils.esimtate_pitched_roof_area(
|
||||
floor_area=100, floor_height=3
|
||||
)
|
||||
|
||||
assert np.isclose(roof_area2, 116.61903789690601)
|
||||
|
||||
# As the floor area gets smaller, the area should get smaller
|
||||
roof_area3 = recommendation_utils.esimtate_pitched_roof_area(
|
||||
floor_area=100, floor_height=1
|
||||
)
|
||||
|
||||
assert np.isclose(roof_area3, 101.9803902718557)
|
||||
|
||||
# As the floor area decreases, area should decrease
|
||||
roof_area4 = recommendation_utils.esimtate_pitched_roof_area(
|
||||
floor_area=50, floor_height=2
|
||||
)
|
||||
|
||||
assert np.isclose(roof_area4, 57.44562646538029)
|
||||
|
||||
# As the floor area increases, area should increase
|
||||
roof_area5 = recommendation_utils.esimtate_pitched_roof_area(
|
||||
floor_area=150, floor_height=2
|
||||
)
|
||||
|
||||
assert np.isclose(roof_area5, 157.797338380595)
|
||||
|
||||
zero_roof_area = recommendation_utils.esimtate_pitched_roof_area(
|
||||
floor_area=0, floor_height=1000
|
||||
)
|
||||
|
||||
assert zero_roof_area == 0
|
||||
|
||||
# If the floor height zero, we don't have a traingle, it's a flat roof
|
||||
flat_roof_area = recommendation_utils.esimtate_pitched_roof_area(
|
||||
floor_area=1000, floor_height=0
|
||||
)
|
||||
|
||||
assert flat_roof_area == 1000
|
||||
|
||||
zero_roof_area2 = recommendation_utils.esimtate_pitched_roof_area(
|
||||
floor_area=0, floor_height=0
|
||||
)
|
||||
|
||||
assert zero_roof_area2 == 0
|
||||
|
|
|
|||
|
|
@ -35,6 +35,19 @@ loft_insulation_materials_150mm_existing = [
|
|||
}
|
||||
]
|
||||
|
||||
room_roof_insulation_materials = [
|
||||
{
|
||||
'id': 18,
|
||||
'type': 'room_roof_insulation',
|
||||
'description': 'Example room roof insulation',
|
||||
'depths': [50, 150, 220, 270, 300], 'depth_unit': 'mm', 'cost': [9, 10, 11, 12, 13],
|
||||
'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': None, 'is_active': True
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class TestRoofRecommendations:
|
||||
|
||||
|
|
@ -205,12 +218,113 @@ class TestRoofRecommendations:
|
|||
'is_assumed': True, 'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': 'none'
|
||||
}
|
||||
|
||||
property_instance7
|
||||
property_instance7.pitched_roof_area = 110
|
||||
|
||||
roof_recommender7 = RoofRecommendations(
|
||||
property_instance=property_instance7, materials=loft_insulation_materials
|
||||
property_instance=property_instance7, materials=room_roof_insulation_materials
|
||||
)
|
||||
|
||||
assert not roof_recommender7.recommendations
|
||||
|
||||
roof_recommender7.recommend()
|
||||
|
||||
# Even though we have 3 depths, we only end with 1 due to diminishin returns
|
||||
assert len(roof_recommender7.recommendations) == 1
|
||||
|
||||
assert roof_recommender7.recommendations[0]["parts"][0]["depths"] == [270]
|
||||
|
||||
assert roof_recommender7.recommendations[0]["new_u_value"] == 0.14
|
||||
assert roof_recommender7.recommendations[0]["starting_u_value"] == 0.8
|
||||
assert roof_recommender7.recommendations[0]["description"] == \
|
||||
"Insulate your room roof with 270mm of Example room roof insulation"
|
||||
|
||||
def test_ceiling_insulated_room_in_roof(self):
|
||||
property_instance8 = Property(id=8, address1="fake", postcode="fake", epc_client=Mock())
|
||||
property_instance8.age_band = "F"
|
||||
property_instance8.floor_area = 100
|
||||
property_instance8.roof = {
|
||||
'original_description': 'Roof room(s), ceiling insulated',
|
||||
'clean_description': 'Roof room(s), ceiling insulated',
|
||||
'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': False,
|
||||
'is_roof_room': True, 'is_loft': False, 'is_flat': False, 'is_thatched': False,
|
||||
'is_at_rafters': False,
|
||||
'is_assumed': False, 'has_dwelling_above': False, 'is_valid': True,
|
||||
'insulation_thickness': 'average'
|
||||
}
|
||||
|
||||
property_instance8.pitched_roof_area = 110
|
||||
|
||||
roof_recommender8 = RoofRecommendations(
|
||||
property_instance=property_instance8, materials=room_roof_insulation_materials
|
||||
)
|
||||
|
||||
assert not roof_recommender8.recommendations
|
||||
|
||||
roof_recommender8.recommend()
|
||||
|
||||
# No recommendations in this case
|
||||
assert not roof_recommender8.recommendations
|
||||
|
||||
def test_insulated_room_in_roof(self):
|
||||
property_instance9 = Property(id=9, address1="fake", postcode="fake", epc_client=Mock())
|
||||
property_instance9.age_band = "F"
|
||||
property_instance9.floor_area = 100
|
||||
property_instance9.roof = {
|
||||
'original_description': 'Roof room(s), insulated (assumed)',
|
||||
'clean_description': 'Roof room(s), insulated',
|
||||
'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': False,
|
||||
'is_roof_room': True, 'is_loft': False, 'is_flat': False, 'is_thatched': False, 'is_at_rafters': False,
|
||||
'is_assumed': True, 'has_dwelling_above': False, 'is_valid': True, 'insulation_thickness': 'average'
|
||||
}
|
||||
|
||||
property_instance9.pitched_roof_area = 110
|
||||
|
||||
roof_recommender9 = RoofRecommendations(
|
||||
property_instance=property_instance9, materials=room_roof_insulation_materials
|
||||
)
|
||||
|
||||
assert not roof_recommender9.recommendations
|
||||
|
||||
roof_recommender9.recommend()
|
||||
|
||||
# No recommendations in this case
|
||||
assert not roof_recommender9.recommendations
|
||||
|
||||
def test_limited_insulated_room_in_roof(self):
|
||||
property_instance10 = Property(id=10, address1="fake", postcode="fake", epc_client=Mock())
|
||||
property_instance10.age_band = "F"
|
||||
property_instance10.floor_area = 100
|
||||
property_instance10.roof = {
|
||||
'original_description': 'Roof room(s), limited insulation (assumed)',
|
||||
'clean_description': 'Roof room(s), limited insulation',
|
||||
'thermal_transmittance': None, 'thermal_transmittance_unit': None, 'is_pitched': False,
|
||||
'is_roof_room': True, 'is_loft': False, 'is_flat': False, 'is_thatched': False, 'is_at_rafters': False,
|
||||
'is_assumed': True, 'has_dwelling_above': False, 'is_valid': True,
|
||||
'insulation_thickness': 'below average'
|
||||
}
|
||||
|
||||
property_instance10.pitched_roof_area = 110
|
||||
|
||||
roof_recommender10 = RoofRecommendations(
|
||||
property_instance=property_instance10, materials=room_roof_insulation_materials
|
||||
)
|
||||
|
||||
assert not roof_recommender10.recommendations
|
||||
|
||||
roof_recommender10.recommend()
|
||||
|
||||
assert len(roof_recommender10.recommendations) == 2
|
||||
|
||||
assert roof_recommender10.recommendations[0]["parts"][0]["depths"] == [220]
|
||||
assert roof_recommender10.recommendations[1]["parts"][0]["depths"] == [270]
|
||||
|
||||
assert roof_recommender10.recommendations[0]["new_u_value"] == 0.16
|
||||
assert roof_recommender10.recommendations[1]["new_u_value"] == 0.14
|
||||
|
||||
assert roof_recommender10.recommendations[0]["starting_u_value"] == 0.8
|
||||
assert roof_recommender10.recommendations[1]["starting_u_value"] == 0.8
|
||||
|
||||
assert roof_recommender10.recommendations[0]["description"] == \
|
||||
"Insulate your room roof with 220mm of Example room roof insulation"
|
||||
assert roof_recommender10.recommendations[1]["description"] == \
|
||||
"Insulate your room roof with 270mm of Example room roof insulation"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue