implementing loft insulation wip

This commit is contained in:
Khalim Conn-Kowlessar 2023-10-20 18:51:49 +11:00
parent 62fe7e3ede
commit 13ceb4031d
3 changed files with 179 additions and 21 deletions

View file

@ -0,0 +1,134 @@
import math
from backend import Property
from typing import List
from datatypes.enums import QuantityUnits
from recommendations.recommendation_utils import (
get_roof_u_value, r_value_per_mm_to_u_value, calculate_u_value_uplift, is_diminishing_returns,
update_lowest_selected_u_value, get_recommended_part
)
class RoofRecommendations:
# part L building regulations indicate that any rennovations on an existing property's roof should
# achieve a U-value of no higher than 0.16
# This can be seen in table 4.3 in building regulations part L:
# https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/1133079
# /Approved_Document_L__Conservation_of_fuel_and_power__Volume_1_Dwellings__2021_edition_incorporating_2023_amendments.pdf
BUILDING_REGULATIONS_PART_L_MAX_U_VALUE = 0.16
DIMINISHING_RETURNS_U_VALUE = 0.14
def __init__(
self,
property_instance: Property,
materials: List
):
self.property = property_instance
# For audit purposes, when estimating u values we'll store it
self.estimated_u_value = None
# Will contains a list of recommended measures
self.recommendations = []
self.materials = materials
def recommend(self):
u_value = self.property.roof["thermal_transmittance"]
insulation_thickness = self.property.walls["insulation_thickness"]
# We check if the roof is already insulated and if so, we exit
if insulation_thickness in ["average", "above average"]:
return
# If we have a u-value already, need to implement this
if u_value:
raise NotImplementedError("Implement me")
u_value = get_roof_u_value(**{**self.property.roof, "age_band": self.property.age_band})
# With loft insulation, 100mm goes between the joists and the rest is rolled on top
# Therefore the price is 100mm + whatever thickness is rolled on top, rolled at a 90 degree angle
# from the base layer
materials = [
{
'id': 4,
'type': 'loft_insulation',
'description': 'Iso Spacesaver Mineral Wool insulation',
'depths': [270, 300],
'depth_unit': 'mm',
'cost': [9, 10],
'cost_unit': 'gbp_sq_meter',
'r_value_per_mm': 0.022727272727272728,
'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
},
]
self.materials = materials
if self.property.roof["is_pitched"]:
# We recommend loft insulation
self.recommend_loft_insulation(u_value)
return
def recommend_loft_insulation(self, u_value):
"""
This method will recommend which insulation materials to use
:return:
"""
loft_insulation_materials = [m for m in self.materials if m["type"] == "loft_insulation"]
lowest_selected_u_value = None
recommendations = []
for material in loft_insulation_materials:
for depth, cost_per_unit in zip(material["depths"], material["cost"]):
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.insulation_wall_area
recommendations.append(
{
"parts": [
get_recommended_part(
part=material,
selected_depth=depth,
quantity=self.property.insulation_wall_area,
quantity_unit=QuantityUnits.m2.value,
selected_total_cost=estimated_cost
)
],
"type": "roof_insulation",
"description": "TODO ",
"starting_u_value": u_value,
"new_u_value": new_u_value,
"sap_points": None,
"cost": estimated_cost,
}
)

View file

@ -23,11 +23,17 @@ class WallRecommendations(Definitions):
# part L building regulations indicate that any rennovations on an existing property's walls should
# achieve a U-value of no higher than 0.3
# This can be seen in table 4.3 in building regulations part L:
# https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/1133079
# /Approved_Document_L__Conservation_of_fuel_and_power__Volume_1_Dwellings__2021_edition_incorporating_2023_amendments.pdf
BUILDING_REGULATIONS_PART_L_MAX_U_VALUE = 0.3
# We don't recommend measures that are too low because it becomes expensive, therefore we aim to avoid
# diminishing returns. This value should be verified with Osmosis (TODO)
DIMINISHING_RETURNS_U_VALUE = 0.25
# Building regulations part L also indicates that cavity wall insulation should result in 0.55 u-value
BUILDING_REGULATIONS_PART_L_CAVITY_WALL_MAX_U_VALUE = 0.55
# Part L regulations indicate that any new build should have walls that achieve a u-value of no higher
# than 0.18.
BUILDING_REGULATIONS_PART_L_NEW_BUILD_MAX_U_VALUE = 0.18
@ -167,29 +173,30 @@ class WallRecommendations(Definitions):
):
continue
lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value)
if new_u_value <= self.BUILDING_REGULATIONS_PART_L_CAVITY_WALL_MAX_U_VALUE:
lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value)
estimated_cost = part["cost"] * self.property.insulation_wall_area
estimated_cost = part["cost"] * self.property.insulation_wall_area
recommendations.append(
{
"parts": [
get_recommended_part(
part=part,
selected_depth=None,
quantity=self.property.insulation_wall_area,
quantity_unit=QuantityUnits.m2.value,
selected_total_cost=estimated_cost
)
],
"type": "wall_insulation",
"description": f"Fill cavity with {part['description']}",
"starting_u_value": u_value,
"new_u_value": new_u_value,
"sap_points": None,
"cost": estimated_cost,
}
)
recommendations.append(
{
"parts": [
get_recommended_part(
part=part,
selected_depth=None,
quantity=self.property.insulation_wall_area,
quantity_unit=QuantityUnits.m2.value,
selected_total_cost=estimated_cost
)
],
"type": "wall_insulation",
"description": f"Fill cavity with {part['description']}",
"starting_u_value": u_value,
"new_u_value": new_u_value,
"sap_points": None,
"cost": estimated_cost,
}
)
self.recommendations = recommendations

View file

@ -517,3 +517,20 @@ def estimate_wall_area(num_floors, floor_height, perimeter):
total_wall_area = wall_area_one_floor * num_floors
return total_wall_area
def calculate_r_value_per_mm(thickness_mm, thermal_conductivity_w_mK):
"""
# Calculate R-value (thermal resistance) using the formula: R = thickness / thermal_conductivity
# Note: The thickness should be converted to meters for the units to be consistent.
:param thickness_mm:
:param thermal_conductivity_w_mK:
:return:
"""
r_value_m2k_w = (thickness_mm / 1000) / thermal_conductivity_w_mK
# Calculate R-value per mm
r_value_per_mm = r_value_m2k_w / thickness_mm
return r_value_per_mm