mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Refactored the WallRecommendations code
This commit is contained in:
parent
6afc8d2e67
commit
8a10cec739
2 changed files with 113 additions and 90 deletions
|
|
@ -126,35 +126,9 @@ def handler():
|
|||
input_properties[6].data["postcode"]
|
||||
walls_df["address1"].values[6]
|
||||
walls_df["original_description"].values[6]
|
||||
# Walls
|
||||
# Property 0
|
||||
# '28 Distillery Wharf', 'Average thermal transmittance 0.16 W/m-¦K'
|
||||
# Because the insulation is already within the max threshold for new builds
|
||||
# Also, I know that the property was built after 1990 so was built with insulation (let's get land registry data)
|
||||
# It's likely not worth doing an insulation upgrade, however if anything, we would do internal wall insulation
|
||||
# logic:
|
||||
# if building built after 1990 + we're able to identify U-value + U-value less than 0.18
|
||||
# and if in or close to a conversation area, recommend internal wall insulation
|
||||
# Property 1
|
||||
# 'Flat 14 Godley V C House', Solid brick, as built, no insulation (assumed)
|
||||
# Since the wall is solid brick (therefore no cavity), we can recommend the following:
|
||||
# External wall insulation
|
||||
# Internal wall insulation
|
||||
# Property 2
|
||||
# '49, Elderfield Road', Solid brick, as built, no insulation (assumed)
|
||||
# Same as property 1
|
||||
# Property 3
|
||||
# 26, Stanhope Road', 'Average thermal transmittance 0.14 W/m-¦K'
|
||||
# Same as property 0
|
||||
# Property 4
|
||||
# 'Flat 3 Frederick Building' 'Solid brick, as built, no insulation (assumed)'
|
||||
# Same as property 1
|
||||
# 'Flat 4 Frederick Building' 'Solid brick, as built, no insulation (assumed)'
|
||||
# Same as property 1
|
||||
# 'Flat 28, 22 Adelina Grove' 'Solid brick, as built, insulated (assumed)'
|
||||
|
||||
from model_data.recommendations.WallRecommendations import WallRecommendations
|
||||
self = WallRecommendations(property_instance=input_properties[6], uvalue_estimates=uvalue_estimates)
|
||||
self = WallRecommendations(property_instance=input_properties[7], uvalue_estimates=uvalue_estimates)
|
||||
|
||||
# We need to deduce a U-value for "Good" energy effieciency
|
||||
|
||||
|
|
@ -171,3 +145,38 @@ def handler():
|
|||
|
||||
p = input_properties[6]
|
||||
df = pd.DataFrame(data)
|
||||
|
||||
res = []
|
||||
for p in input_properties:
|
||||
distances = []
|
||||
for borehole in tqdm(borehole_client.data, total=len(borehole_client.data)):
|
||||
dist_meeters, _ = borehole_client.distance_between_bng_coords(
|
||||
x1_bng=p.coordinates['x_coordinate'],
|
||||
y1_bng=p.coordinates['y_coordinate'],
|
||||
x2_bng=float(borehole['EASTING']),
|
||||
y2_bng=float(borehole['NORTHING'])
|
||||
)
|
||||
distances.append(dist_meeters)
|
||||
|
||||
res.append(
|
||||
{
|
||||
"uprn": int(p.data["uprn"]),
|
||||
"meters_to_nearest_borehole": min(distances)
|
||||
}
|
||||
|
||||
)
|
||||
res = pd.DataFrame(res)
|
||||
|
||||
properties_dataset = [
|
||||
{
|
||||
**p.data,
|
||||
"in_conservation_area": p.in_conservation_area,
|
||||
**p.coordinates,
|
||||
|
||||
} for p in input_properties
|
||||
]
|
||||
|
||||
properties_dataset = pd.DataFrame(properties_dataset)
|
||||
properties_dataset = properties_dataset.merge(res, on="uprn", how="left")
|
||||
|
||||
properties_dataset.to_csv("properties_dataset.csv")
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import itertools
|
|||
from model_data.Property import Property
|
||||
from model_data.ConservationAreaClient import ConservationAreaClient
|
||||
from model_data.analysis.UvalueEstimations import UvalueEstimations
|
||||
from model_data.BaseUtility import BaseUtility
|
||||
import pandas as pd
|
||||
from copy import deepcopy
|
||||
|
||||
|
|
@ -184,7 +185,7 @@ internal_wall_insulation_parts = [
|
|||
wall_parts = external_wall_insulation_parts + internal_wall_insulation_parts
|
||||
|
||||
|
||||
class WallRecommendations:
|
||||
class WallRecommendations(BaseUtility):
|
||||
YEAR_WALLS_BUILT_WITH_INSULATION = 1990
|
||||
U_VALUE_UNIT = 'w/m-¦k'
|
||||
BUILDING_REGULATIONS_PART_L_MAX_U_VALUE = 0.18
|
||||
|
|
@ -207,6 +208,19 @@ class WallRecommendations:
|
|||
# Will contains a list of recommended measures
|
||||
self.recommendations = []
|
||||
|
||||
@property
|
||||
def ewi_valid(self):
|
||||
"""
|
||||
This method check available data, to determine if a property is suitable for external wall insulation
|
||||
"""
|
||||
|
||||
# Current logic: If the property is in a conservation area or a flat, it is not suitable for EWI
|
||||
if (self.property.in_conservation_area in ["in_conversation_area"]) or \
|
||||
(self.property.data["property-type"].lower() == "flat"):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _year_property_was_built(self):
|
||||
"""
|
||||
Estimates when the property was built based on as much available data as possible.
|
||||
|
|
@ -216,13 +230,15 @@ class WallRecommendations:
|
|||
if self.property.full_sap_epc:
|
||||
return pd.to_datetime(self.property.full_sap_epc["lodgement-date"]).year
|
||||
|
||||
if self.property.data["construction-age-band"]:
|
||||
if self.property.data["construction-age-band"] not in self.DATA_ANOMALY_MATCHES:
|
||||
# Take the lower limit. If we're pessimistic about the age of the property, that at least means we have
|
||||
# more options for recommendations if that age falls before the year that insulation in walls became
|
||||
# common practice
|
||||
band = [int(x) for x in re.findall(r'\b\d{4}\b', self.property.data["construction-age-band"])]
|
||||
return band[0]
|
||||
|
||||
#
|
||||
|
||||
raise NotImplementedError("Implement me!")
|
||||
|
||||
def recommend(self):
|
||||
|
|
@ -243,20 +259,8 @@ class WallRecommendations:
|
|||
if (not is_cavity_wall) and (self.year_built >= self.YEAR_WALLS_BUILT_WITH_INSULATION) and (
|
||||
u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE
|
||||
):
|
||||
# Recommend internal wall insulation
|
||||
iwi_parts = [part for part in wall_parts if part["type"] == "internal_wall_insulation"]
|
||||
for part in iwi_parts:
|
||||
_, new_u_value = self.calculate_u_value_uplift(u_value, part["u_value"])
|
||||
new_u_value = round(new_u_value, 2)
|
||||
|
||||
# We allow a small tolerance for error so we don't discount the recommendation entirely
|
||||
# if it's close, since this is an estimated new u-value
|
||||
if new_u_value - self.U_VALUE_ERROR <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
||||
self.recommendations.append(
|
||||
{
|
||||
**part, "new_u_value": new_u_value,
|
||||
}
|
||||
)
|
||||
# Recommend insulation
|
||||
self.find_insulation(u_value)
|
||||
|
||||
if is_solid_brick and insulation_thickness == "none":
|
||||
|
||||
|
|
@ -266,62 +270,72 @@ class WallRecommendations:
|
|||
else:
|
||||
u_value = self._get_walls_uvalue_estimate()
|
||||
|
||||
ewi_parts = [
|
||||
part for part in wall_parts if part["type"] == "external_wall_insulation"
|
||||
] if self.property.in_conservation_area == ConservationAreaClient.IN_CONSERVATION_AREA else []
|
||||
if u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
||||
self.find_insulation(u_value)
|
||||
|
||||
iwi_parts = [part for part in wall_parts if part["type"] == "internal_wall_insulation"]
|
||||
raise NotImplementedError("Not implemented yet")
|
||||
|
||||
# Recommend external and internal wall insulation separately
|
||||
for part in ewi_parts + iwi_parts:
|
||||
def find_insulation(self, u_value):
|
||||
"""
|
||||
This function contains the logic for finding potential insulation measures for a property, depending
|
||||
on the parts available and whether the property can have external wall insulation installed
|
||||
:return:
|
||||
"""
|
||||
|
||||
for depth in part["depths"]:
|
||||
part_u_value = self.r_value_per_mm_to_u_value(depth, part["r_value_per_mm"])
|
||||
ewi_parts = [
|
||||
part for part in wall_parts if part["type"] == "external_wall_insulation"
|
||||
] if self.ewi_valid else []
|
||||
|
||||
_, new_u_value = self.calculate_u_value_uplift(u_value, part_u_value)
|
||||
new_u_value = round(new_u_value, 2)
|
||||
iwi_parts = [part for part in wall_parts if part["type"] == "internal_wall_insulation"]
|
||||
|
||||
if new_u_value < self.DIMINISHING_RETURNS_U_VALUE:
|
||||
# Recommend external and internal wall insulation separately
|
||||
for part in ewi_parts + iwi_parts:
|
||||
|
||||
for depth in part["depths"]:
|
||||
part_u_value = self.r_value_per_mm_to_u_value(depth, part["r_value_per_mm"])
|
||||
|
||||
_, new_u_value = self.calculate_u_value_uplift(u_value, part_u_value)
|
||||
new_u_value = round(new_u_value, 2)
|
||||
|
||||
if new_u_value < self.DIMINISHING_RETURNS_U_VALUE:
|
||||
# We don't recommend an overkill solution
|
||||
continue
|
||||
|
||||
# We allow a small tolerance for error so we don't discount the recommendation entirely
|
||||
# if it's close, since this is an estimated new u-value
|
||||
if new_u_value - self.U_VALUE_ERROR <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
||||
self.recommendations.append(
|
||||
self._get_recommended_part(part, depth, new_u_value)
|
||||
)
|
||||
|
||||
# We also can recommend both internal and external wall insulation together
|
||||
# By looping through ewi first, if there is nothing there, that ensures not combinations are tested
|
||||
for ewi_part in ewi_parts:
|
||||
for iwi_part in iwi_parts:
|
||||
for ewi_depth, iwi_depth in itertools.product(ewi_part["depths"], iwi_part["depths"]):
|
||||
ewi_part_u_value = self.r_value_per_mm_to_u_value(ewi_depth, ewi_part["r_value_per_mm"])
|
||||
iwi_part_u_value = self.r_value_per_mm_to_u_value(iwi_depth, iwi_part["r_value_per_mm"])
|
||||
|
||||
# First calculate the new U-value after applying external wall insulation
|
||||
_, ewi_new_u_value = self.calculate_u_value_uplift(u_value, ewi_part_u_value)
|
||||
# Then calculate the new U-value after applying internal wall insulation
|
||||
_, combined_new_u_value = self.calculate_u_value_uplift(ewi_new_u_value, iwi_part_u_value)
|
||||
combined_new_u_value = round(combined_new_u_value, 2)
|
||||
|
||||
if combined_new_u_value < self.DIMINISHING_RETURNS_U_VALUE:
|
||||
# We don't recommend an overkill solution
|
||||
continue
|
||||
|
||||
# We allow a small tolerance for error so we don't discount the recommendation entirely
|
||||
# if it's close, since this is an estimated new u-value
|
||||
if new_u_value - self.U_VALUE_ERROR <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
||||
self.recommendations.append(
|
||||
self._get_recommended_part(part, depth, new_u_value)
|
||||
)
|
||||
# Check if the combined new U-value meets the requirement
|
||||
if combined_new_u_value - self.U_VALUE_ERROR <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
||||
# Here you might want to define a way to add both recommendations together.
|
||||
# For now, I'm adding them as separate items in the list
|
||||
|
||||
# We also can recommend both internal and external wall insulation together
|
||||
# By looping through ewi first, if there is nothing there, that ensures not combinations are tested
|
||||
for ewi_part in ewi_parts:
|
||||
for iwi_part in iwi_parts:
|
||||
for ewi_depth, iwi_depth in itertools.product(ewi_part["depths"], iwi_part["depths"]):
|
||||
ewi_part_u_value = self.r_value_per_mm_to_u_value(ewi_depth, ewi_part["r_value_per_mm"])
|
||||
iwi_part_u_value = self.r_value_per_mm_to_u_value(iwi_depth, iwi_part["r_value_per_mm"])
|
||||
|
||||
# First calculate the new U-value after applying external wall insulation
|
||||
_, ewi_new_u_value = self.calculate_u_value_uplift(u_value, ewi_part_u_value)
|
||||
# Then calculate the new U-value after applying internal wall insulation
|
||||
_, combined_new_u_value = self.calculate_u_value_uplift(ewi_new_u_value, iwi_part_u_value)
|
||||
combined_new_u_value = round(combined_new_u_value, 2)
|
||||
|
||||
if combined_new_u_value < self.DIMINISHING_RETURNS_U_VALUE:
|
||||
# We don't recommend an overkill solution
|
||||
continue
|
||||
|
||||
# Check if the combined new U-value meets the requirement
|
||||
if combined_new_u_value - self.U_VALUE_ERROR <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
||||
# Here you might want to define a way to add both recommendations together.
|
||||
# For now, I'm adding them as separate items in the list
|
||||
|
||||
recommendation = [
|
||||
self._get_recommended_part(ewi_part, ewi_depth, combined_new_u_value),
|
||||
self._get_recommended_part(iwi_part, iwi_depth, combined_new_u_value)
|
||||
]
|
||||
self.recommendations.append(recommendation)
|
||||
|
||||
raise NotImplementedError("Not implemented yet")
|
||||
recommendation = [
|
||||
self._get_recommended_part(ewi_part, ewi_depth, combined_new_u_value),
|
||||
self._get_recommended_part(iwi_part, iwi_depth, combined_new_u_value)
|
||||
]
|
||||
self.recommendations.append(recommendation)
|
||||
|
||||
def _get_walls_uvalue_estimate(self):
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue