mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
added test cases for roof u-values
This commit is contained in:
parent
98a91a5a35
commit
35911c0db7
6 changed files with 124 additions and 16 deletions
|
|
@ -128,7 +128,7 @@ class Recommendations:
|
|||
property_recommendations.append(self.wall_recomender.recommendations)
|
||||
phase += 1
|
||||
|
||||
self.roof_recommender.recommend(phase=phase, measures=measures)
|
||||
self.roof_recommender.recommend(phase=phase, measures=measures, default_u_values=self.default_u_values)
|
||||
if self.roof_recommender.recommendations:
|
||||
property_recommendations.append(self.roof_recommender.recommendations)
|
||||
phase += 1
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ class RoofRecommendations:
|
|||
|
||||
return full_insulated_room_roof or room_roof_insulated_at_rafters
|
||||
|
||||
def recommend(self, phase, measures=None):
|
||||
def recommend(self, phase, measures=None, default_u_values=False):
|
||||
|
||||
if self.property.roof["has_dwelling_above"]:
|
||||
return
|
||||
|
|
@ -171,7 +171,8 @@ class RoofRecommendations:
|
|||
insulation_thickness=self.insulation_thickness,
|
||||
phase=phase,
|
||||
is_flat=False,
|
||||
is_pitched=True
|
||||
is_pitched=True,
|
||||
default_u_values=default_u_values
|
||||
)
|
||||
return
|
||||
|
||||
|
|
@ -184,7 +185,8 @@ class RoofRecommendations:
|
|||
insulation_thickness=0,
|
||||
phase=phase,
|
||||
is_flat=True,
|
||||
is_pitched=False
|
||||
is_pitched=False,
|
||||
default_u_values=default_u_values
|
||||
)
|
||||
return
|
||||
|
||||
|
|
@ -193,7 +195,7 @@ class RoofRecommendations:
|
|||
if self.property.roof["is_roof_room"] and ("room_roof_insulation" in measures) or (
|
||||
"room_roof_insulation" in [x["type"] for x in non_invasive_recommendations]
|
||||
):
|
||||
self.recommend_room_roof_insulation(u_value, phase)
|
||||
self.recommend_room_roof_insulation(u_value, phase, default_u_values)
|
||||
return
|
||||
|
||||
raise NotImplementedError("Implement me")
|
||||
|
|
@ -215,7 +217,7 @@ class RoofRecommendations:
|
|||
raise ValueError("Invalid material type")
|
||||
|
||||
def recommend_roof_insulation(
|
||||
self, u_value, insulation_thickness, phase, is_pitched, is_flat
|
||||
self, u_value, insulation_thickness, phase, is_pitched, is_flat, default_u_values
|
||||
):
|
||||
|
||||
"""
|
||||
|
|
@ -241,6 +243,7 @@ class RoofRecommendations:
|
|||
:param phase: Phase of the recommendation
|
||||
:param is_pitched: Is the roof pitched
|
||||
:param is_flat: Is the roof flat
|
||||
:param default_u_values: Use default u-values
|
||||
:return:
|
||||
"""
|
||||
|
||||
|
|
@ -266,7 +269,6 @@ class RoofRecommendations:
|
|||
recommendations = []
|
||||
for _, insulation_material_group in insulation_materials.groupby("description"):
|
||||
for _, material in insulation_material_group.iterrows():
|
||||
|
||||
# We make sure we hit a depth of 270mm. We should factor in any existing insulation if the
|
||||
# loft is already partially insulated.
|
||||
# Note: This requirement is only for loft insulation
|
||||
|
|
@ -340,9 +342,35 @@ class RoofRecommendations:
|
|||
|
||||
new_description = f"Pitched, {int(proposed_depth)}mm loft insulation"
|
||||
|
||||
if default_u_values:
|
||||
# We update the u-value with the default if we're using default u-values
|
||||
new_u_value = get_roof_u_value(
|
||||
insulation_thickness=str(int(new_thickness)),
|
||||
has_dwelling_above=self.property.roof["has_dwelling_above"],
|
||||
is_loft=self.property.roof["is_loft"],
|
||||
is_roof_room=self.property.roof["is_roof_room"],
|
||||
is_thatched=self.property.roof["is_thatched"],
|
||||
age_band=self.property.age_band,
|
||||
is_flat=self.property.roof["is_flat"],
|
||||
is_pitched=self.property.roof["is_pitched"],
|
||||
is_at_rafters=self.property.roof["is_at_rafters"],
|
||||
)
|
||||
|
||||
elif material["type"] == "flat_roof_insulation":
|
||||
new_description = "Flat, insulated"
|
||||
new_efficiency = "Good"
|
||||
if default_u_values:
|
||||
new_u_value = get_roof_u_value(
|
||||
insulation_thickness="100",
|
||||
has_dwelling_above=self.property.roof["has_dwelling_above"],
|
||||
is_loft=self.property.roof["is_loft"],
|
||||
is_roof_room=self.property.roof["is_roof_room"],
|
||||
is_thatched=self.property.roof["is_thatched"],
|
||||
age_band=self.property.age_band,
|
||||
is_flat=self.property.roof["is_flat"],
|
||||
is_pitched=self.property.roof["is_pitched"],
|
||||
is_at_rafters=self.property.roof["is_at_rafters"],
|
||||
)
|
||||
else:
|
||||
raise ValueError("Invalid material type")
|
||||
|
||||
|
|
|
|||
|
|
@ -544,6 +544,7 @@ class WallRecommendations(Definitions):
|
|||
}
|
||||
|
||||
if default_u_values:
|
||||
# If we're using default U-values, we overwrite new_u_value
|
||||
new_u_value = get_wall_u_value(
|
||||
clean_description=new_description,
|
||||
age_band=self.property.age_band,
|
||||
|
|
|
|||
|
|
@ -340,6 +340,7 @@ s9_list = [
|
|||
s10_list = [
|
||||
{
|
||||
"Age_band": "A, B, C, D",
|
||||
"Insulation_Thickness": "none",
|
||||
"Pitched_slates_or_tiles_insulation_between_joists_or_unknown": 2.3,
|
||||
"Pitched_slates_or_tiles_insulation_at_rafters": 2.3,
|
||||
"Flat_roof": 2.3,
|
||||
|
|
@ -350,6 +351,7 @@ s10_list = [
|
|||
},
|
||||
{
|
||||
"Age_band": "E",
|
||||
"Insulation_Thickness": 12,
|
||||
"Pitched_slates_or_tiles_insulation_between_joists_or_unknown": 1.5,
|
||||
"Pitched_slates_or_tiles_insulation_at_rafters": 1.5,
|
||||
"Flat_roof": 1.5,
|
||||
|
|
@ -360,6 +362,7 @@ s10_list = [
|
|||
},
|
||||
{
|
||||
"Age_band": "F",
|
||||
"Insulation_Thickness": 50,
|
||||
"Pitched_slates_or_tiles_insulation_between_joists_or_unknown": 0.68,
|
||||
"Pitched_slates_or_tiles_insulation_at_rafters": 0.68,
|
||||
"Flat_roof": 0.68,
|
||||
|
|
@ -370,6 +373,7 @@ s10_list = [
|
|||
},
|
||||
{
|
||||
"Age_band": "G",
|
||||
"Insulation_Thickness": 100,
|
||||
"Pitched_slates_or_tiles_insulation_between_joists_or_unknown": 0.40,
|
||||
"Pitched_slates_or_tiles_insulation_at_rafters": 0.40,
|
||||
"Flat_roof": 0.40,
|
||||
|
|
@ -380,6 +384,7 @@ s10_list = [
|
|||
},
|
||||
{
|
||||
"Age_band": "H",
|
||||
"Insulation_Thickness": 150,
|
||||
"Pitched_slates_or_tiles_insulation_between_joists_or_unknown": 0.30,
|
||||
"Pitched_slates_or_tiles_insulation_at_rafters": 0.35,
|
||||
"Flat_roof": 0.35,
|
||||
|
|
@ -390,6 +395,7 @@ s10_list = [
|
|||
},
|
||||
{
|
||||
"Age_band": "I",
|
||||
"Insulation_Thickness": 150,
|
||||
"Pitched_slates_or_tiles_insulation_between_joists_or_unknown": 0.26,
|
||||
"Pitched_slates_or_tiles_insulation_at_rafters": 0.35,
|
||||
"Flat_roof": 0.35,
|
||||
|
|
@ -400,6 +406,7 @@ s10_list = [
|
|||
},
|
||||
{
|
||||
"Age_band": "J",
|
||||
"Insulation_Thickness": 270,
|
||||
"Pitched_slates_or_tiles_insulation_between_joists_or_unknown": 0.16,
|
||||
"Pitched_slates_or_tiles_insulation_at_rafters": 0.20,
|
||||
"Flat_roof": 0.25,
|
||||
|
|
@ -410,6 +417,7 @@ s10_list = [
|
|||
},
|
||||
{
|
||||
"Age_band": "K",
|
||||
"Insulation_Thickness": 270,
|
||||
"Pitched_slates_or_tiles_insulation_between_joists_or_unknown": 0.16,
|
||||
"Pitched_slates_or_tiles_insulation_at_rafters": 0.20,
|
||||
"Flat_roof": 0.25,
|
||||
|
|
@ -420,6 +428,7 @@ s10_list = [
|
|||
},
|
||||
{
|
||||
"Age_band": "L",
|
||||
"Insulation_Thickness": 270,
|
||||
"Pitched_slates_or_tiles_insulation_between_joists_or_unknown": 0.16,
|
||||
"Pitched_slates_or_tiles_insulation_at_rafters": 0.18,
|
||||
"Flat_roof": 0.18,
|
||||
|
|
|
|||
|
|
@ -239,12 +239,7 @@ def get_wall_u_value(
|
|||
return float(mapped_value)
|
||||
|
||||
|
||||
def get_u_value_from_s9(
|
||||
thickness, s9, is_loft, is_roof_room, is_thatched, is_at_rafters
|
||||
):
|
||||
"""Get the U-value from table S9 based on the insulation thickness."""
|
||||
|
||||
# If the roof as pitched & insulated at the rafters, it's a room roof
|
||||
def extract_thickness(thickness, is_roof_room, is_at_rafters, is_loft, is_flat):
|
||||
if is_roof_room or is_at_rafters:
|
||||
# TODO: We get None instead of a string none, this should be fixed
|
||||
if thickness is None:
|
||||
|
|
@ -258,19 +253,40 @@ def get_u_value_from_s9(
|
|||
}
|
||||
thickness = thickness_map[thickness]
|
||||
|
||||
if is_flat:
|
||||
try:
|
||||
thickness = int(thickness)
|
||||
return thickness
|
||||
except ValueError:
|
||||
# If thickness is not a valid number (could be a string or None), return None
|
||||
return None
|
||||
|
||||
if thickness in ["below average", "average", "above average", "none", None] or (
|
||||
not is_loft and not is_roof_room and not is_at_rafters
|
||||
):
|
||||
return None
|
||||
elif thickness.endswith("+"):
|
||||
thickness = int(thickness[:-1])
|
||||
return thickness
|
||||
else:
|
||||
try:
|
||||
thickness = int(thickness)
|
||||
return thickness
|
||||
except ValueError:
|
||||
# If thickness is not a valid number (could be a string or None), return None
|
||||
return None
|
||||
|
||||
|
||||
def get_u_value_from_s9(
|
||||
thickness, s9, is_loft, is_roof_room, is_thatched, is_at_rafters
|
||||
):
|
||||
"""Get the U-value from table S9 based on the insulation thickness."""
|
||||
|
||||
if thickness in ["below average", "average", "above average", "none", None, "0", 0] or (
|
||||
not is_loft and not is_roof_room and not is_at_rafters
|
||||
):
|
||||
return None
|
||||
|
||||
# Determine the column to refer based on the roof type
|
||||
column = (
|
||||
"Thatched_roof_U_value_W_m2K"
|
||||
|
|
@ -323,6 +339,14 @@ def get_roof_u_value(
|
|||
if has_dwelling_above:
|
||||
return 0.0
|
||||
|
||||
thickness = extract_thickness(
|
||||
thickness=insulation_thickness,
|
||||
is_roof_room=is_roof_room,
|
||||
is_at_rafters=is_at_rafters,
|
||||
is_loft=is_loft,
|
||||
is_flat=is_flat
|
||||
)
|
||||
|
||||
# Step 1: Try to get the U-value from table S9 based on the insulation thickness
|
||||
# The conditions for using table S9 are:
|
||||
# - The insulation thickness is known
|
||||
|
|
@ -330,7 +354,7 @@ def get_roof_u_value(
|
|||
# The criteria for using this table is predominately defined by insulation around joists which is predominately
|
||||
# a feature of lofts and roof rooms
|
||||
u_value = get_u_value_from_s9(
|
||||
thickness=insulation_thickness,
|
||||
thickness=thickness,
|
||||
s9=s9,
|
||||
is_loft=is_loft,
|
||||
is_roof_room=is_roof_room,
|
||||
|
|
@ -363,9 +387,34 @@ def get_roof_u_value(
|
|||
column = "Pitched_slates_or_tiles_insulation_between_joists_or_unknown"
|
||||
|
||||
# Get the U-value from table S10 based on the age band and the determined column
|
||||
u_value = s10.loc[s10["Age_band"].str.contains(age_band), column].values[0]
|
||||
if is_flat and thickness is not None:
|
||||
u_value = s10.loc[
|
||||
(s10["Insulation_Thickness"] == thickness) |
|
||||
s10["Age_band"].str.contains(age_band),
|
||||
column
|
||||
].values.min()
|
||||
else:
|
||||
u_value = s10.loc[s10["Age_band"].str.contains(age_band), column].values[0]
|
||||
|
||||
return float(u_value)
|
||||
u_value = float(u_value)
|
||||
|
||||
# As per the documentation here: https://bregroup.com/documents/d/bre-group/rdsap_2012_9-94-20-09-2019
|
||||
# Table s.10
|
||||
# "The value from the table applies for unknown and as built. If the roof is known to have more insulation than
|
||||
# would normally be expected for the age band, either observed or on the basis of documentary evidence, use the
|
||||
# lower of the value in the table and:
|
||||
# 50 mm insulation 0.68
|
||||
# 100 mm insulation: 0.40
|
||||
# 150 mm or more insulation: 0.30"
|
||||
if thickness is not None:
|
||||
if thickness == 50:
|
||||
u_value = min(u_value, 0.68)
|
||||
if thickness == 100:
|
||||
u_value = min(u_value, 0.40)
|
||||
if thickness >= 150:
|
||||
u_value = min(u_value, 0.30)
|
||||
|
||||
return u_value
|
||||
|
||||
|
||||
def estimate_number_of_floors(property_type):
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from recommendations import recommendation_utils
|
|||
from datatypes.enums import QuantityUnits
|
||||
from recommendations.tests.test_data.wall_uvalue_test_cases import wall_uvalue_test_cases
|
||||
from recommendations.tests.test_data.floor_uvalue_test_cases import floor_uvalue_test_cases
|
||||
from recommendations.tests.test_data.roof_uvalue_test_cases import roof_uvalue_test_cases
|
||||
|
||||
|
||||
class TestRecommendationUtils:
|
||||
|
|
@ -222,6 +223,26 @@ class TestRecommendationUtils:
|
|||
u_value = recommendation_utils.get_roof_u_value(**inputs)
|
||||
assert u_value == 0.0, f"Expected 0.0, but got {u_value}"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_case",
|
||||
roof_uvalue_test_cases
|
||||
)
|
||||
def test_roof_uvalues(self, test_case):
|
||||
expected_uvalue = test_case["uvalue"]
|
||||
inputs = test_case.copy()
|
||||
del inputs["uvalue"]
|
||||
# insulation_thickness = inputs["insulation_thickness"]
|
||||
# has_dwelling_above = inputs["has_dwelling_above"]
|
||||
# is_loft = inputs["is_loft"]
|
||||
# is_roof_room = inputs["is_roof_room"]
|
||||
# is_thatched = inputs["is_thatched"]
|
||||
# age_band = inputs["age_band"]
|
||||
# is_flat = inputs["is_flat"]
|
||||
# is_pitched = inputs["is_pitched"]
|
||||
# is_at_rafters = inputs["is_at_rafters"]
|
||||
uvalue = recommendation_utils.get_roof_u_value(**inputs)
|
||||
assert expected_uvalue == uvalue, f"Expected u value {expected_uvalue}, recieved {uvalue}"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_case",
|
||||
wall_uvalue_test_cases
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue