added floor uvalue unit tests

This commit is contained in:
Khalim Conn-Kowlessar 2023-09-22 23:02:25 +01:00
parent dcb2f7caba
commit 87e7cce075
5 changed files with 122 additions and 18 deletions

View file

@ -5,7 +5,7 @@ from datatypes.enums import QuantityUnits
from backend.Property import Property
from recommendations.recommendation_utils import (
r_value_per_mm_to_u_value, calculate_u_value_uplift, is_diminishing_returns, update_lowest_selected_u_value,
get_recommended_part, estimate_perimeter, estimate_perimeter_2_rooms, get_wall_type,
get_recommended_part, estimate_perimeter, get_wall_type,
get_floor_u_value
)
@ -103,12 +103,7 @@ class FloorRecommendations(Definitions):
else:
raise NotImplementedError("Implement me")
scaled_num_rooms = number_of_rooms / num_floors
if scaled_num_rooms <= 2.5:
estimated_perimeter = estimate_perimeter_2_rooms(total_floor_area / num_floors)
else:
estimated_perimeter = estimate_perimeter(total_floor_area / num_floors, scaled_num_rooms)
estimated_perimeter = estimate_perimeter(total_floor_area / num_floors, number_of_rooms / num_floors)
wall_type = get_wall_type(**self.property.walls)

View file

@ -300,6 +300,16 @@ def get_roof_u_value(
def estimate_perimeter(floor_area, num_rooms):
"""
Uses a basic methodology to attempt to estimate perimeter. Works better for
:param floor_area: floor area of the home
:param num_rooms: number of rooms in the home
:return: estimated perimeter
"""
if floor_area < 0:
raise ValueError("Floor area cannot be negative.")
if num_rooms <= 0:
raise ValueError("Number of rooms must be greater than zero.")
# Compute average room size based on total floor area and number of rooms
avg_room_size = floor_area / num_rooms
@ -319,16 +329,6 @@ def estimate_perimeter(floor_area, num_rooms):
return perimeter
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
def get_floor_u_value(floor_type, area, perimeter, age_band, wall_type, insulation_thickness=None):
"""
Estimate the u-value of a suspended floor, based on RdSap methodology

View file

@ -0,0 +1,32 @@
floor_uvalue_test_cases = [
# Test with solid floor, no insulation
{
"floor_type": "solid",
"area": 100,
"perimeter": 40,
"age_band": "A",
"wall_type": "cavity",
"insulation_thickness": None,
"expected": 0.62,
},
# Test with suspended floor, with insulation
{
"floor_type": "suspended",
"area": 120,
"perimeter": 44,
"age_band": "B",
"wall_type": "solid brick",
"insulation_thickness": "50mm",
"expected": 0.33,
},
# Test with invalid floor type
{
"floor_type": "invalid",
"area": 100,
"perimeter": 40,
"age_band": "A",
"wall_type": "cavity",
"insulation_thickness": None,
"expected": ValueError,
},
]

View file

@ -128,7 +128,7 @@ class TestWallRecommendations:
assert recommender.estimated_u_value is None
recommender.recommend()
assert recommender.property.floor["is_suspended"]
assert recommender.estimated_u_value == 0.51
assert recommender.estimated_u_value == 0.52
assert recommender.recommendations
types = {part["type"] for x in recommender.recommendations for part in x["parts"]}

View file

@ -1,8 +1,10 @@
import pytest
import math
from unittest.mock import MagicMock
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
class TestRecommendationUtils:
@ -227,3 +229,78 @@ class TestRecommendationUtils:
del inputs["uvalue"]
uvalue = recommendation_utils.get_wall_u_value(**inputs)
assert expected_uvalue == uvalue, f"Expected u value {expected_uvalue}, recieved {uvalue}"
@pytest.mark.parametrize("test_input", floor_uvalue_test_cases)
def test_get_floor_u_value(self, test_input):
if not isinstance(test_input["expected"], float):
with pytest.raises(test_input["expected"]):
recommendation_utils.get_floor_u_value(
test_input["floor_type"],
test_input["area"],
test_input["perimeter"],
test_input["age_band"],
test_input["wall_type"],
test_input["insulation_thickness"],
)
else:
result = recommendation_utils.get_floor_u_value(
floor_type=test_input["floor_type"],
area=test_input["area"],
perimeter=test_input["perimeter"],
age_band=test_input["age_band"],
wall_type=test_input["wall_type"],
insulation_thickness=test_input["insulation_thickness"],
)
assert result == pytest.approx(test_input["expected"], abs=1e-2)
# Test with wall_type not in default_wall_thickness
def test_wall_type_not_in_default_wall_thickness(self):
with pytest.raises(IndexError):
recommendation_utils.get_floor_u_value(
floor_type="solid",
area=100,
perimeter=40,
age_band="A",
wall_type="InvalidWallType",
insulation_thickness=None,
)
# Test with age_band not in s11
def test_age_band_not_in_s11(self):
with pytest.raises(IndexError):
recommendation_utils.get_floor_u_value(
floor_type="solid",
area=100,
perimeter=40,
age_band="Z",
wall_type="Cavity",
insulation_thickness=None,
)
def test_estimate_perimeter_regular_inputs():
assert math.isclose(
recommendation_utils.estimate_perimeter(100, 5), 40.24922359499622,
rel_tol=1e-2
)
assert math.isclose(
recommendation_utils.estimate_perimeter(123, 5), 44.63854836349408,
rel_tol=1e-2
)
def test_estimate_perimeter_zero_floor_area():
with pytest.raises(ZeroDivisionError):
recommendation_utils.estimate_perimeter(0, 5)
with pytest.raises(ValueError):
assert recommendation_utils.estimate_perimeter(0, 0) == 0
def test_estimate_perimeter_invalid_inputs():
with pytest.raises(ValueError):
recommendation_utils.estimate_perimeter(100, 0)
with pytest.raises(ValueError):
recommendation_utils.estimate_perimeter(-100, 5)
with pytest.raises(ValueError):
recommendation_utils.estimate_perimeter(100, -5)