mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
implementing loft insulation wip
This commit is contained in:
parent
62fe7e3ede
commit
13ceb4031d
3 changed files with 179 additions and 21 deletions
134
recommendations/RoofRecommendations.py
Normal file
134
recommendations/RoofRecommendations.py
Normal 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,
|
||||
}
|
||||
)
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue