mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
267 lines
12 KiB
Python
267 lines
12 KiB
Python
import os
|
|
|
|
import pandas as pd
|
|
import pytest
|
|
import pickle
|
|
import numpy as np
|
|
from unittest.mock import Mock, MagicMock
|
|
from model_data.recommendations.WallRecommendations import WallRecommendations
|
|
from model_data.analysis.UvalueEstimations import UvalueEstimations
|
|
from model_data.Property import Property
|
|
from model_data.recommendations.recommendation_utils import is_diminishing_returns
|
|
|
|
|
|
class TestWallRecommendations:
|
|
|
|
@pytest.fixture
|
|
def input_properties(self):
|
|
with open(
|
|
os.path.abspath(os.path.dirname(__file__)) + "/test_data/input_properties.pkl", "rb"
|
|
) as f:
|
|
return pickle.load(f)
|
|
|
|
@pytest.fixture
|
|
def uvalue_estimates(self):
|
|
with open(
|
|
os.path.abspath(os.path.dirname(__file__)) + "/test_data/uvalue_estimates.pkl", "rb"
|
|
) as f:
|
|
return pickle.load(f)
|
|
|
|
@pytest.fixture
|
|
def mock_wall_rec_instance(self):
|
|
# Creating a mock instance of WallRecommendations with the necessary attributes
|
|
property_mock = Mock()
|
|
property_mock.full_sap_epc = {"lodgement-date": "2000-01-01"} # or any date you want
|
|
property_mock.data = {"construction-age-band": "1950"} # or any other data that fits your tests
|
|
|
|
uvalue_estimates_mock = Mock()
|
|
|
|
mock_wall_rec_instance = WallRecommendations(property_mock, uvalue_estimates_mock)
|
|
return mock_wall_rec_instance
|
|
|
|
def test_init(self, input_properties, uvalue_estimates):
|
|
obj = WallRecommendations(property_instance=input_properties[0], uvalue_estimates=uvalue_estimates)
|
|
assert obj
|
|
assert obj.property
|
|
assert obj.uvalue_estimates
|
|
|
|
def test_uvalue_0_16(self, input_properties, uvalue_estimates):
|
|
"""
|
|
This tests the wall description Average thermal transmittance 0.16 W/m-¦K
|
|
The important data for this recommendation is:
|
|
- u value of 0.16
|
|
- property built in 2014
|
|
Since properties built after 1990 are typically built with insulation and this property
|
|
already has really good insulation, we do NOT recommend any measures for this property
|
|
"""
|
|
input_properties[0].year_built = 2014
|
|
recommender = WallRecommendations(property_instance=input_properties[0], uvalue_estimates=uvalue_estimates)
|
|
assert recommender.property.walls["original_description"] == "Average thermal transmittance 0.16 W/m-¦K"
|
|
recommender.recommend()
|
|
# This should be empty
|
|
assert recommender.recommendations == []
|
|
|
|
def test_solid_brick_no_insulation(self, input_properties, uvalue_estimates):
|
|
"""
|
|
This tests a property with a wall description of Solid brick, as built, no insulation (assumed)
|
|
The property was built in 1930, right on the threshold for when cavity walls were introduced
|
|
However, we're told this property is solid brik so we assume no cavity.
|
|
We're also told that it has no insulation, so we will recommend internal/external wall insulation
|
|
|
|
This property is not in a conservation area, however it's a flat so we don't recommend external wall insulation
|
|
"""
|
|
input_properties[1].year_built = 1930
|
|
recommender = WallRecommendations(property_instance=input_properties[1], uvalue_estimates=uvalue_estimates)
|
|
assert recommender.property.walls["original_description"] == "Solid brick, as built, no insulation (assumed)"
|
|
assert not recommender.ewi_valid
|
|
assert recommender.property.in_conservation_area == "not_in_conservation_area"
|
|
assert recommender.property.data["property-type"] == "Flat"
|
|
|
|
recommender.recommend()
|
|
# This should result in some recommendations, all of which should be internal insulation
|
|
assert recommender.recommendations
|
|
|
|
rec_types = {part["type"] for rec in recommender.recommendations for part in rec["parts"]}
|
|
assert rec_types == {"internal_wall_insulation"}
|
|
|
|
# Check the recommendations provide a u value below the minimum
|
|
assert all(
|
|
rec["new_u_value"] < WallRecommendations.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE for rec in
|
|
recommender.recommendations
|
|
)
|
|
|
|
def test_solid_brick_insulation(self, input_properties, uvalue_estimates):
|
|
"""
|
|
This tests a property with a wall description of Solid brick, as built, insulation (assumed)
|
|
The property was built in 1991, after cavity walls were introduced
|
|
However, we're told this property is solid brik so we assume no cavity.
|
|
We're also told that it has some insulation already
|
|
|
|
We'll need to estimate the u-value for this property since we don't know what the insulation is
|
|
and how thick it is. We'll estimate the u-value based on similar properties, by matching properties
|
|
with similar attributes such as the walls energy efficiency rating, the property type, the number of rooms,
|
|
etc
|
|
|
|
This property is not in a conservation area, however it's a flat so we don't recommend external wall insulation
|
|
"""
|
|
|
|
input_properties[6].year_built = 1991
|
|
recommender = WallRecommendations(property_instance=input_properties[6], uvalue_estimates=uvalue_estimates)
|
|
|
|
assert recommender.property.walls["original_description"] == "Solid brick, as built, insulated (assumed)"
|
|
assert not recommender.ewi_valid
|
|
assert recommender.property.in_conservation_area == "not_in_conservation_area"
|
|
assert recommender.property.data["property-type"] == "Flat"
|
|
assert recommender.estimated_u_value is None
|
|
|
|
recommender.recommend()
|
|
|
|
# We should result in some recommendations, all of which should be internal wall insulation
|
|
assert recommender.recommendations
|
|
assert recommender.estimated_u_value == 0.45
|
|
|
|
rec_types = {part["type"] for rec in recommender.recommendations for part in rec["parts"]}
|
|
assert rec_types == {"internal_wall_insulation"}
|
|
|
|
# Check the recommendations provide a u value below the minimum
|
|
assert all(
|
|
rec["new_u_value"] < WallRecommendations.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE for rec in
|
|
recommender.recommendations
|
|
)
|
|
|
|
for rec in recommender.recommendations:
|
|
assert rec["new_u_value"] < 0.3
|
|
|
|
def test_is_diminishing_returns_no_recommendations(self):
|
|
# We have no recommendations but the new u value is below the diminishing returns threshold
|
|
# however since there are no recommendaions yet, we should include it and say
|
|
# it is NOT diminishing
|
|
|
|
new_u_value = 0.24
|
|
assert new_u_value < WallRecommendations.DIMINISHING_RETURNS_U_VALUE
|
|
|
|
assert not is_diminishing_returns([], new_u_value, None, WallRecommendations.DIMINISHING_RETURNS_U_VALUE)
|
|
|
|
def test_is_diminishing_returns_diminishing(self):
|
|
# We have a recommendation already and the new u value falls below the diminishing returns
|
|
# threshold and is also lower than the lowest selected u value, so we should say this IS
|
|
# diminishing returns
|
|
|
|
new_u_value = 0.2
|
|
lowest_selected_u_value = 0.23
|
|
assert new_u_value < WallRecommendations.DIMINISHING_RETURNS_U_VALUE
|
|
|
|
assert is_diminishing_returns([Mock()], new_u_value, lowest_selected_u_value,
|
|
WallRecommendations.DIMINISHING_RETURNS_U_VALUE)
|
|
|
|
def test_is_diminishing_returns_is_diminishing(self):
|
|
# We have a recommendation already and the new u value falls below the diminishing returns
|
|
# threshold and it's lower than the lowest selected u value, so we should say this IS DIMINISHIN
|
|
|
|
new_u_value = 0.24
|
|
lowest_selected_u_value = 0.26
|
|
|
|
assert new_u_value < WallRecommendations.DIMINISHING_RETURNS_U_VALUE
|
|
|
|
assert is_diminishing_returns([Mock()], new_u_value, lowest_selected_u_value,
|
|
WallRecommendations.DIMINISHING_RETURNS_U_VALUE)
|
|
|
|
def test_is_diminishing_returns_not_diminishing(self):
|
|
# We have a recommendation already and the new u value falls below the diminishing returns
|
|
# threshold however it's higher than the lowest selected u value, so we should say this is NOT
|
|
# diminishing returns
|
|
|
|
new_u_value = 0.24
|
|
lowest_selected_u_value = 0.22
|
|
|
|
assert new_u_value < WallRecommendations.DIMINISHING_RETURNS_U_VALUE
|
|
|
|
assert not is_diminishing_returns([Mock()], new_u_value, lowest_selected_u_value,
|
|
WallRecommendations.DIMINISHING_RETURNS_U_VALUE)
|
|
|
|
def test_is_diminishing_returns_error_in_recommendations(self, mock_wall_rec_instance):
|
|
# Testing case where there's an error in recommendations
|
|
mock_wall_rec_instance.recommendations = []
|
|
with pytest.raises(ValueError):
|
|
is_diminishing_returns([], 0.2, 0.24, 0.25)
|
|
|
|
|
|
class TestWallRecommendationsBase:
|
|
@pytest.fixture
|
|
def property_mock(self):
|
|
property_mock = MagicMock(spec=Property)
|
|
property_mock.full_sap_epc = {"lodgement-date": "1999-12-31"}
|
|
property_mock.in_conservation_area = "not_in_conservation_area"
|
|
return property_mock
|
|
|
|
@pytest.fixture
|
|
def uvalue_estimations_mock(self):
|
|
uvalue_estimations_mock = MagicMock(spec=UvalueEstimations)
|
|
|
|
uvalue_estimations_mock.walls = pd.DataFrame([
|
|
{
|
|
'local-authority': 'E09000012',
|
|
'property-type': 'Bungalow',
|
|
'walls-energy-eff': 'Very Good',
|
|
'walls-env-eff': 'Very Good',
|
|
'built-form': 'End-Terrace',
|
|
'number-habitable-rooms': '', 'number-heated-rooms': '', 'total-floor-area_group': 'Decile 1',
|
|
'median_thermal_transmittance': 0.15, 'n_samples': 1
|
|
}
|
|
])
|
|
|
|
uvalue_estimations_mock.walls_decile_data = {
|
|
'decile_labels': ['Decile 1', 'Decile 2', 'Decile 3', 'Decile 4', 'Decile 5', 'Decile 6', 'Decile 7',
|
|
'Decile 8', 'Decile 9', 'Decile 10'],
|
|
'decile_boundaries': np.array([11., 49., 52., 56., 63., 70., 74., 79.,
|
|
90., 103.8, 1936.])}
|
|
|
|
uvalue_estimations_mock.classify_decile_newvalues.return_value = ["Decile 1"]
|
|
return uvalue_estimations_mock
|
|
|
|
@pytest.fixture
|
|
def wall_recommendations_instance(self, property_mock, uvalue_estimations_mock):
|
|
wall_recommendations_instance = WallRecommendations(property_mock, uvalue_estimations_mock)
|
|
wall_recommendations_instance.uvalue_estimates.walls_decile_data = {
|
|
"decile_labels": MagicMock(),
|
|
"decile_boundaries": MagicMock()
|
|
}
|
|
return wall_recommendations_instance
|
|
|
|
def test_ewi_valid_in_conservation_area(self, wall_recommendations_instance):
|
|
wall_recommendations_instance.property.in_conservation_area = "in_conversation_area"
|
|
assert wall_recommendations_instance.ewi_valid is False
|
|
|
|
def test_ewi_valid_is_flat(self, wall_recommendations_instance):
|
|
wall_recommendations_instance.property.data = {"property-type": "flat"}
|
|
assert wall_recommendations_instance.ewi_valid is False
|
|
|
|
def test_ewi_valid_not_in_conservation_area_and_not_flat(self, wall_recommendations_instance):
|
|
wall_recommendations_instance.property.in_conservation_area = "not_in_conversation_area"
|
|
wall_recommendations_instance.property.data = {"property-type": "house"}
|
|
assert wall_recommendations_instance.ewi_valid is True
|
|
|
|
def test_get_walls_uvalue_estimate(self, wall_recommendations_instance, uvalue_estimations_mock):
|
|
wall_recommendations_instance.uvalue_estimates = uvalue_estimations_mock
|
|
wall_recommendations_instance.property.data = {
|
|
"local-authority": "E09000012",
|
|
"property-type": "Bungalow",
|
|
"built-form": "End-Terrace",
|
|
"walls-energy-eff": "Very Good",
|
|
"walls-env-eff": "Very Good",
|
|
"total-floor-area": 10,
|
|
"number-habitable-rooms": "",
|
|
"number-heated-rooms": ""
|
|
}
|
|
|
|
assert wall_recommendations_instance._get_walls_uvalue_estimate() == 0.15
|
|
|
|
def test_recommend_without_u_value(self, wall_recommendations_instance):
|
|
wall_recommendations_instance.property.walls = {
|
|
"thermal_transmittance": None,
|
|
"is_solid_brick": False,
|
|
"is_cavity_wall": False,
|
|
"insulation_thickness": "none"
|
|
}
|
|
with pytest.raises(NotImplementedError):
|
|
wall_recommendations_instance.recommend()
|