implementing u-value population wip

This commit is contained in:
Khalim Conn-Kowlessar 2023-09-20 14:23:46 +01:00
parent f86a466f3d
commit aa3b1e172d
3 changed files with 301 additions and 7 deletions

View file

@ -77,6 +77,239 @@ def app():
if len(descriptions) != len(set(descriptions)):
raise ValueError("Duplicated descriptions found, check me")
# Finally, we attach u-values to the descriptions for walls, roofs and floors
wall_types = [
"Stone: granite or whinstone as built",
"Stone: sandstone or limestone as built",
"Solid brick as built",
"Stone/solid brick with 50 mm external or internal insulation",
"Stone/solid brick with 100 mm external or internal insulation",
"Stone/solid brick with 150 mm external or internal insulation",
"Stone/solid brick with 200 mm external or internal insulation",
"Cob as built",
"Cob with 50 mm external or internal insulation",
"Cob with 100 mm external or internal insulation",
"Cob with 150 mm external or internal insulation",
"Cob with 200 mm external or internal insulation",
"Cavity as built",
"Unfilled cavity with 50 mm external or internal insulation",
"Unfilled cavity with 100 mm external or internal insulation",
"Unfilled cavity with 150 mm external or internal insulation",
"Unfilled cavity with 200 mm external or internal insulation",
"Filled cavity",
"Filled cavity with 50 mm external or internal insulation",
"Filled cavity with 100 mm external or internal insulation",
"Filled cavity with 150 mm external or internal insulation",
"Filled cavity with 200 mm external or internal insulation",
"Timber frame as built",
"Timber frame with internal insulation",
"System build as built",
"System build with 50 mm external or internal insulation",
"System build with 100 mm external or internal insulation",
"System build with 150 mm external or internal insulation",
"System build with 200 mm external or internal insulation",
]
u_values = [
["a", "a", "a", "a", "1.7b", "1.0", "0.6", "0.60", "0.45", "0.35", "0.30", "0.28"],
["a", "a", "a", "a", "1.7b", "1.0", "0.6", "0.60", "0.45", "0.35", "0.30", "0.28"],
["1.7", "1.7", "1.7", "1.7", "1.7", "1.0", "0.60", "0.60", "0.45", "0.35", "0.30", "0.28"],
["0.55", "0.55", "0.55", "0.55", "0.55", "0.45", "0.35", "0.35", "0.30", "0.25", "0.21", "0.21"],
["0.32", "0.32", "0.32", "0.32", "0.32", "0.28", "0.24", "0.24", "0.21", "0.19", "0.17", "0.16"],
["0.23", "0.23", "0.23", "0.23", "0.23", "0.21", "0.18", "0.18", "0.17", "0.15", "0.14", "0.14"],
["0.18", "0.18", "0.18", "0.18", "0.18", "0.17", "0.15", "0.15", "0.14", "0.13", "0.12", "0.12"],
["0.80", "0.80", "0.80", "0.80", "0.80", "0.80", "0.60", "0.60", "0.45", "0.35", "0.30", "0.28"],
["0.40", "0.40", "0.40", "0.40", "0.40", "0.40", "0.35", "0.35", "0.30", "0.25", "0.21", "0.21"],
["0.26", "0.26", "0.26", "0.26", "0.26", "0.26", "0.24", "0.24", "0.21", "0.19", "0.17", "0.16"],
["0.20", "0.20", "0.20", "0.20", "0.20", "0.20", "0.18", "0.18", "0.17", "0.15", "0.14", "0.14"],
["0.16", "0.16", "0.16", "0.16", "0.16", "0.16", "0.15", "0.15", "0.14", "0.13", "0.12", "0.12"],
["1.5", "1.5", "1.5", "1.5", "1.5", "1.0", "0.60", "0.60", "0.45", "0.35", "0.30", "0.28"],
["0.53", "0.53", "0.53", "0.53", "0.53", "0.45", "0.35", "0.35", "0.30", "0.25", "0.21", "0.21"],
["0.32", "0.32", "0.32", "0.32", "0.32", "0.30", "0.24", "0.24", "0.21", "0.19", "0.17", "0.16"],
["0.23", "0.23", "0.23", "0.23", "0.23", "0.21", "0.18", "0.18", "0.17", "0.15", "0.14", "0.14"],
["0.18", "0.18", "0.18", "0.18", "0.18", "0.17", "0.15", "0.15", "0.14", "0.13", "0.12", "0.12"],
["0.7", "0.7", "0.7", "0.7", "0.7", "0.40", "0.35", "0.35", "0.45", "0.35", "0.30", "0.28"],
["0.37", "0.37", "0.37", "0.37", "0.37", "0.27", "0.25", "0.25", "0.25", "0.25", "0.21", "0.21"],
["0.25", "0.25", "0.25", "0.25", "0.25", "0.20", "0.19", "0.19", "0.19", "0.19", "0.17", "0.16"],
["0.19", "0.19", "0.19", "0.19", "0.19", "0.16", "0.15", "0.15", "0.15", "0.15", "0.14", "0.14"],
["0.16", "0.16", "0.16", "0.16", "0.16", "0.13", "0.13", "0.13", "0.13", "0.13", "0.12", "0.12"],
["2.5", "1.9", "1.9", "1.0", "0.80", "0.45", "0.40", "0.40", "0.40", "0.35", "0.30", "0.28"],
["0.60", "0.55", "0.55", "0.40", "0.40", "0.40", "0.40", "0.40", "0.40", "0.35", "0.30", "0.28"],
["2.0", "2.0", "2.0", "2.0", "1.7", "1.0", "0.60", "0.60", "0.45", "0.35", "0.30", "0.28"],
["0.60", "0.60", "0.60", "0.60", "0.55", "0.45", "0.35", "0.35", "0.30", "0.25", "0.21", "0.21"],
["0.35", "0.35", "0.35", "0.35", "0.35", "0.32", "0.24", "0.24", "0.21", "0.19", "0.17", "0.16"],
["0.25", "0.25", "0.25", "0.25", "0.25", "0.21", "0.18", "0.18", "0.17", "0.15", "0.14", "0.14"],
["0.18", "0.18", "0.18", "0.18", "0.18", "0.17", "0.15", "0.15", "0.14", "0.13", "0.12", "0.12"],
]
age_bands = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"]
wall_uvalues = []
for i, wall_type in enumerate(wall_types):
row = {"Wall_type": wall_type}
for j, age_band in enumerate(age_bands):
row[age_band] = u_values[i][j]
wall_uvalues.append(row)
parkhome_wall_uvalues = [
{"Wall_type": "Park home as built", "F": "1.7", "G": "1.2", "I": "0.7", "K": "0.6"},
{"Wall_type": "Park home with additional insulation", }
]
wall_uvalues.extend(parkhome_wall_uvalues)
wall_uvalues_df = pd.DataFrame(wall_uvalues)
# This maps the descriptions in the EPC data to the descriptions in the table
epc_wall_description_map = {
############################
# Cavity wall mappings
############################
"Cavity wall, as built, partial insulation": "Filled cavity",
"Cavity wall, filled cavity": "Filled cavity",
"Cavity wall, as built, no insulation": "Cavity as built",
"Cavity wall, as built, insulated": "Unfilled cavity with 100 mm external or internal insulation",
"Cavity wall, with external insulation": "Unfilled cavity with 100 mm external or internal insulation",
"Cavity wall,": "Cavity as built", # General case of cavity wall without further details
"Cavity wall, filled cavity and external insulation":
"Filled cavity with 100 mm external or internal insulation",
"Cavity wall, filled cavity and internal insulation":
"Filled cavity with 100 mm external or internal insulation",
"Cavity wall, with internal insulation": "Unfilled cavity with 100 mm external or internal insulation",
############################
# Solid brick wall mappings
############################
"Solid brick, as built, no insulation": "Solid brick as built",
"Solid brick, with internal insulation": "Stone/solid brick with 100 mm external or internal insulation",
"Solid brick, as built, insulated": "Stone/solid brick with 100 mm external or internal insulation",
"Solid brick, with external insulation": "Stone/solid brick with 100 mm external or internal insulation",
"Solid brick, as built, partial insulation": "Stone/solid brick with 50 mm external or internal insulation",
############################
# Timber frame wall mappings
############################
# These mappings are perhaps the most dubious due to the lack of timber options in the RdSAP table
"Timber frame, as built, insulated": "Timber frame with internal insulation",
"Timber frame, with additional insulation": "Timber frame with internal insulation",
"Timber frame, as built, partial insulation": "Timber frame as built",
"Timber frame, as built, no insulation": "Timber frame as built",
"Timber frame, with external insulation": "Timber frame with internal insulation",
############################
# Sandstone/limestones wall mappings
############################
"Sandstone or limestone, as built, no insulation": "Stone: sandstone or limestone as built",
"Sandstone or limestone, with internal insulation":
"Stone/solid brick with 100 mm external or internal insulation",
"Sandstone or limestone, as built, partial insulation": "Stone/solid brick with 50 mm external or internal "
"insulation",
"Sandstone, as built, no insulation": "Stone: sandstone or limestone as built",
"Sandstone or limestone, as built, insulated": "Stone/solid brick with 100 mm external or internal"
"insulation",
"Sandstone, as built, insulated": "Stone/solid brick with 100 mm external or internal insulation",
"Sandstone, with internal insulation": "Stone/solid brick with 100 mm external or internal insulation",
"Sandstone or limestone, with external insulation": "Stone/solid brick with 100 mm external or internal "
"insulation",
"Sandstone, with external insulation": "Stone/solid brick with 100 mm external or internal insulation",
"Sandstone, as built, partial insulation": "Stone/solid brick with 50 mm external or internal insulation",
############################
# Granite/whinstone wall mappings
############################
"Granite or whinstone, as built, no insulation": "Stone: granite or whinstone as built",
"Granite or whinstone, with internal insulation": "Stone/solid brick with 100 mm external or internal "
"insulation",
"Granite or whinstone, as built, partial insulation": "Stone/solid brick with 50 mm external or internal "
"insulation",
"Granite or whinstone, as built, insulated": "Stone/solid brick with 100 mm external or internal "
"insulation",
"Granite or whinstone, with external insulation": "Stone/solid brick with 100 mm external or internal "
"insulation",
############################
# System built wall mappings
############################
"System built, as built, no insulation": "System build as built",
"System built, as built, partial insulation": "System build with 50 mm external or internal insulation",
"System built, with internal insulation": "System build with 100 mm external or internal insulation",
"System built, with external insulation": "System build with 100 mm external or internal insulation",
"System built, as built, insulated": "System build with 100 mm external or internal insulation",
############################
# Cob wall mappings
############################
"Cob, as built": "Cob as built",
"Cob, with external insulation": "Cob with 100 mm external or internal insulation",
"Cob, with internal insulation": "Cob with 100 mm external or internal insulation",
############################
# Park home mappings
############################
"Park home wall, as built": "Park home as built",
"Park home wall, with external insulation": "Park home with additional insulation",
"Park home wall, with internal insulation": "Park home with additional insulation",
}
from recommendations.rdsap_tables import default_wall_thickness
def apply_formula_s_5_1_1(is_granite_or_whinstone, is_sandstone_or_limestone, age_band):
"""
As the u-value table in https://bregroup.com/wp-content/uploads/2019/09/RdSAP_2012_9.94-20-09-2019.pdf
on page 19, certain u-values as indicated by an "a", should be populated using a formula as defined in section
S.5.1.1
:param wall_type:
:return:
"""
stone_wall_thickness = [x for x in default_wall_thickness if x["type"] == "stone"][0]
thickness = stone_wall_thickness["J_K_L"] if age_band in ["J", "L", "L"] else stone_wall_thickness[age_band]
if is_granite_or_whinstone:
return 3.3 - 0.002 * thickness
if is_sandstone_or_limestone:
return 3 - 0.002 * thickness
for wall in cleaned_data["walls-description"]:
if wall["thermal_transmittance"]:
continue
description = wall["clean_description"]
# Remove (assumed)
description = description.replace("(assumed)", "").rstrip()
mapped_description = epc_wall_description_map[description]
# Get the u-value
for ab in age_bands:
mapped_value = wall_uvalues_df[wall_uvalues_df["Wall_type"] == mapped_description][ab].values[0]
if mapped_value == "a":
# The rdSap documentation indicateswe should use a formula to calculate the u-value
uvalue = float(
apply_formula_s_5_1_1(
is_granite_or_whinstone=wall["is_granite_or_whinstone"],
is_sandstone_or_limestone=wall["is_sandstone_or_limestone"],
age_band=ab
)
)
elif "b" in mapped_value:
potential_uvalue = float(mapped_value.replace("b", ""))
formula_uvalue = float(apply_formula_s_5_1_1(
is_granite_or_whinstone=wall["is_granite_or_whinstone"],
is_sandstone_or_limestone=wall["is_sandstone_or_limestone"],
age_band=ab
))
uvalue = min(potential_uvalue, formula_uvalue)
else:
uvalue = float(mapped_value)
df = pd.DataFrame(cleaned_data["walls-description"])
df = df[pd.isnull(df["thermal_transmittance"])]
df["clean_description"].values
# We store a singular file however we could store the data under the following file path:
# cleaned_epc_data/{component}/{original_description}/cleaned.bson
# where component is one of the keys of cleaned_data. If we store it against the original data, this

View file

@ -66,11 +66,24 @@ class FloorRecommendations(Definitions):
# Compute average room size based on total floor area and number of rooms
avg_room_size = floor_area / num_rooms
# Estimate total side length for square layout
total_side_length = math.sqrt(avg_room_size * num_rooms)
# Estimate the side length of a square room with the average room size
avg_room_side_length = math.sqrt(avg_room_size)
# Compute the perimeter
perimeter = total_side_length * 4
# Estimate total side length assuming rooms are lined up in a row
total_side_length = avg_room_side_length * num_rooms
# Assuming the house is a square (which is a very rough approximation), compute the perimeter
perimeter = math.sqrt(total_side_length) * 4
return perimeter
@staticmethod
def _estimate_perimeter_2_rooms(floor_area):
# Assuming a square layout for the entire floor area to get a first-order approximation of the perimeter
side_length = math.sqrt(floor_area)
# Calculating the perimeter of the square layout
perimeter = 4 * side_length
return perimeter
@ -126,7 +139,10 @@ class FloorRecommendations(Definitions):
dg = defaults["w"] + defaults["lambda_g"] * (defaults["Rsi"] + defaults["Rse"])
# P is the exposed perimeter, which we estimate as we not have this data
p = self._estimate_perimeter(floor_area=floor_area, num_rooms=number_of_rooms)
if number_of_rooms <= 2:
p = self._estimate_perimeter_2_rooms(floor_area=floor_area)
else:
p = self._estimate_perimeter(floor_area=floor_area, num_rooms=number_of_rooms)
b = 2 * floor_area / p
u_g = 2 * defaults["lambda_g"] * math.log(math.pi * b / dg + 1) / (math.pi * b + dg)
u_x = (2 * defaults["h"] * defaults["Uw"] / b) + (1450 * defaults["E"] * defaults["v"] * defaults["fw"] / b)
@ -135,6 +151,49 @@ class FloorRecommendations(Definitions):
return u
def _estimate_solid_floor_u_value(self):
"""
1. dt =w + g × (Rsi + Rf + Rse)
2. B = 2 × A/P
3. if dt < B, U = 2 × g × ln( × B/dt + 1)/( × B + dt)
4. if dt >= B, U = g / (0.457 × B + dt)
:return:
"""
# TODO: complete
insulation_thickness = 0
age_band_letter = "E"
wall_type = "cavity"
floor_area = 37.26
perimeter = 16.2
# age_band_letter = [x for x in age_band_data if x[region] == age_band][0]["age_band"]
defaults = {
# We need width in meters
"w": [x[age_band_letter] for x in default_wall_thickness if x["type"] == wall_type][0] / 1000,
"lambda_g": 1.5,
"Rsi": 0.17,
"Rse": 0.04,
"Rf": 0.001 * insulation_thickness / 0.035,
"h": 0.3,
"v": 5,
"fw": 0.05,
"E": 0.003,
"Uw": 1.5,
}
dt = defaults["w"] + defaults["lambda_g"] * (defaults["Rsi"] + defaults["Rse"] + defaults["Rf"])
# perimeter = self._estimate_perimeter(floor_area, number_of_rooms)
B = 2 * floor_area / perimeter
if dt < B:
U = 2 * defaults["lambda_g"] * math.log(math.pi * B / dt + 1) / (math.pi * B + dt)
else:
print("implement me")
def recommend(self):
u_value = self.property.floor["thermal_transmittance"]
is_suspended = self.property.floor["is_suspended"]

View file

@ -91,6 +91,8 @@ age_band_data = [
},
]
# As defined in the rdsap documentation on page 9
# https://bregroup.com/wp-content/uploads/2019/09/RdSAP_2012_9.94-20-09-2019.pdf
default_wall_thickness = [
{
"type": "stone", "A": 500, "B": 500, "C": 500, "D": 500, "E": 450, "F": 420, "G": 420, "H": 420,
@ -117,7 +119,7 @@ default_wall_thickness = [
"I": 300, "J_K_L": 300
},
{
"type": "park home", "A": None, "B": None, "C": None, "D": None, "E": None, "F": 50, "G": None,
"H": None, "I": 50, "J_K_L": 100
"type": "park home", "A": None, "B": None, "C": None, "D": None, "E": None, "F": 50, "G": 50,
"H": None, "I": 75, "J_K_L": 100
},
]