mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
add check for float nan in recommendations
This commit is contained in:
parent
c3e04d2d00
commit
57477907cb
3 changed files with 130 additions and 60 deletions
|
|
@ -229,7 +229,9 @@ class TrainingDataset(BaseDataset):
|
|||
"""
|
||||
# TODO: move into EPCRecord record
|
||||
uvalue_columns = [
|
||||
col for col in self.df.columns if "thermal_transmittance" in col
|
||||
col
|
||||
for col in self.df.columns
|
||||
if "thermal_transmittance" in col and "_unit" not in col
|
||||
]
|
||||
for uvalue_col in uvalue_columns:
|
||||
self.df[uvalue_col] = pd.to_numeric(self.df[uvalue_col])
|
||||
|
|
@ -703,6 +705,8 @@ class TrainingDataset(BaseDataset):
|
|||
"insulation_thickness_ending": f"{component}_insulation_thickness_ending",
|
||||
"thermal_transmittance": f"{component}_thermal_transmittance",
|
||||
"thermal_transmittance_ending": f"{component}_thermal_transmittance_ending",
|
||||
"thermal_transmittance_unit": f"{component}_thermal_transmittance_unit",
|
||||
"thermal_transmittance_unit_ending": f"{component}_thermal_transmittance_unit_ending",
|
||||
"tariff_type": f"{component}_tariff_type",
|
||||
"tariff_type_ending": f"{component}_tariff_type_ending",
|
||||
"clean_description": f"{component}_clean_description",
|
||||
|
|
|
|||
|
|
@ -7,8 +7,13 @@ from datatypes.enums import QuantityUnits
|
|||
from backend.Property import Property
|
||||
from BaseUtility import Definitions
|
||||
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, get_wall_u_value, override_costs
|
||||
r_value_per_mm_to_u_value,
|
||||
calculate_u_value_uplift,
|
||||
is_diminishing_returns,
|
||||
update_lowest_selected_u_value,
|
||||
get_recommended_part,
|
||||
get_wall_u_value,
|
||||
override_costs,
|
||||
)
|
||||
from recommendations.config import PARTIALLY_FILLED_PERCENTAGE_ASSUMPTION
|
||||
from recommendations.Costs import Costs
|
||||
|
|
@ -22,7 +27,7 @@ class WallRecommendations(Definitions):
|
|||
# After 1930, Solid brick walls became less populate and instead, cavity walls became a
|
||||
# more popular choice
|
||||
YEARS_CAVITY_WALLS_BEGAN = 1930
|
||||
U_VALUE_UNIT = 'w/m-¦k'
|
||||
U_VALUE_UNIT = "w/m-¦k"
|
||||
|
||||
# part L building regulations indicate that any rennovations on an existing property's walls should
|
||||
# achieve a U-value of no higher than 0.3
|
||||
|
|
@ -53,11 +58,7 @@ class WallRecommendations(Definitions):
|
|||
# threshold
|
||||
NEW_BUILD_INSULATED = 0.75
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
property_instance: Property,
|
||||
materials: List
|
||||
):
|
||||
def __init__(self, property_instance: Property, materials: List):
|
||||
self.property = property_instance
|
||||
self.costs = Costs(self.property)
|
||||
# For audit purposes, when estimating u values we'll store it
|
||||
|
|
@ -75,9 +76,10 @@ class WallRecommendations(Definitions):
|
|||
]
|
||||
|
||||
self.internal_wall_non_insulation_materials = [
|
||||
part for part in materials if part["type"] in [
|
||||
"iwi_wall_demolition", "iwi_vapour_barrier", "iwi_redecoration"
|
||||
]
|
||||
part
|
||||
for part in materials
|
||||
if part["type"]
|
||||
in ["iwi_wall_demolition", "iwi_vapour_barrier", "iwi_redecoration"]
|
||||
]
|
||||
|
||||
self.external_wall_insulation_materials = [
|
||||
|
|
@ -85,9 +87,10 @@ class WallRecommendations(Definitions):
|
|||
]
|
||||
|
||||
self.external_wall_non_insulation_materials = [
|
||||
part for part in materials if part["type"] in [
|
||||
"ewi_wall_demolition", "ewi_wall_preparation", "ewi_wall_redecoration"
|
||||
]
|
||||
part
|
||||
for part in materials
|
||||
if part["type"]
|
||||
in ["ewi_wall_demolition", "ewi_wall_preparation", "ewi_wall_redecoration"]
|
||||
]
|
||||
|
||||
@property
|
||||
|
|
@ -98,7 +101,9 @@ class WallRecommendations(Definitions):
|
|||
|
||||
# Current logic: If the property is in a conservation area/heritage building/listed building or a flat,
|
||||
# it is not suitable for EWI
|
||||
if self.property.restricted_measures or (self.property.data["property-type"].lower() == "flat"):
|
||||
if self.property.restricted_measures or (
|
||||
self.property.data["property-type"].lower() == "flat"
|
||||
):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
@ -109,31 +114,43 @@ class WallRecommendations(Definitions):
|
|||
# recommend internal wall insulation as a possible measure
|
||||
|
||||
u_value = self.property.walls["thermal_transmittance"]
|
||||
u_value = None if math.isnan(u_value) else u_value
|
||||
|
||||
is_cavity_wall = self.property.walls["is_cavity_wall"]
|
||||
insulation_thickness = self.property.walls["insulation_thickness"]
|
||||
|
||||
# We check if the wall is already insulated and if so, we exit
|
||||
if ((insulation_thickness in ["average", "above average"]) or self.property.walls["is_filled_cavity"]) and (
|
||||
"cavity_extract_and_refill" not in self.property.non_invasive_recommendations
|
||||
if (
|
||||
(insulation_thickness in ["average", "above average"])
|
||||
or self.property.walls["is_filled_cavity"]
|
||||
) and (
|
||||
"cavity_extract_and_refill"
|
||||
not in self.property.non_invasive_recommendations
|
||||
):
|
||||
return
|
||||
|
||||
if u_value:
|
||||
|
||||
if self.property.walls["thermal_transmittance_unit"] != self.U_VALUE_UNIT:
|
||||
raise NotImplementedError("Haven't handled the case of other u value units yet")
|
||||
raise NotImplementedError(
|
||||
"Haven't handled the case of other u value units yet"
|
||||
)
|
||||
|
||||
# If the property is a new build and the U-value is below 0.75, we don't recommend insulation because it's
|
||||
# not practical
|
||||
if (self.property.data["transaction-type"] == "new dwelling") and (u_value <= self.NEW_BUILD_INSULATED):
|
||||
if (self.property.data["transaction-type"] == "new dwelling") and (
|
||||
u_value <= self.NEW_BUILD_INSULATED
|
||||
):
|
||||
# Recommend nothing
|
||||
return
|
||||
|
||||
# We can't detect it's a cavity wall, but it was built after 1990 so likely built with insulation already
|
||||
# + it already has a U-value WORSE than the building regulations, so we recommend either internal or
|
||||
# external wall insulation
|
||||
if (not is_cavity_wall) and (self.property.year_built >= self.YEAR_WALLS_BUILT_WITH_INSULATION) and (
|
||||
u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE
|
||||
if (
|
||||
(not is_cavity_wall)
|
||||
and (self.property.year_built >= self.YEAR_WALLS_BUILT_WITH_INSULATION)
|
||||
and (u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE)
|
||||
):
|
||||
# Recommend insulation
|
||||
self.find_insulation(u_value, phase)
|
||||
|
|
@ -141,8 +158,10 @@ class WallRecommendations(Definitions):
|
|||
|
||||
# We can't detect it's a cavity wall, but it was built after 1990 so likely built with insulation already
|
||||
# + it already has a U-value better than the building regulations, so we don't need to recommend anything
|
||||
if (not is_cavity_wall) and (self.property.year_built >= self.YEAR_WALLS_BUILT_WITH_INSULATION) and (
|
||||
u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE
|
||||
if (
|
||||
(not is_cavity_wall)
|
||||
and (self.property.year_built >= self.YEAR_WALLS_BUILT_WITH_INSULATION)
|
||||
and (u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE)
|
||||
):
|
||||
# Recommend nothing
|
||||
return
|
||||
|
|
@ -205,28 +224,40 @@ class WallRecommendations(Definitions):
|
|||
recommendations = []
|
||||
for _, material in insulation_materials.iterrows():
|
||||
|
||||
part_u_value = r_value_per_mm_to_u_value(cavity_width, material["r_value_per_mm"])
|
||||
part_u_value = r_value_per_mm_to_u_value(
|
||||
cavity_width, material["r_value_per_mm"]
|
||||
)
|
||||
|
||||
_, new_u_value = calculate_u_value_uplift(u_value, part_u_value)
|
||||
new_u_value = math.ceil(new_u_value * 100.0) / 100.0
|
||||
|
||||
if is_diminishing_returns(
|
||||
recommendations, new_u_value, lowest_selected_u_value, self.DIMINISHING_RETURNS_U_VALUE
|
||||
recommendations,
|
||||
new_u_value,
|
||||
lowest_selected_u_value,
|
||||
self.DIMINISHING_RETURNS_U_VALUE,
|
||||
):
|
||||
continue
|
||||
|
||||
if new_u_value <= self.BUILDING_REGULATIONS_PART_L_CAVITY_WALL_MAX_U_VALUE:
|
||||
lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value)
|
||||
lowest_selected_u_value = update_lowest_selected_u_value(
|
||||
lowest_selected_u_value, new_u_value
|
||||
)
|
||||
|
||||
is_extraction_and_refill = "cavity_extract_and_refill" in self.property.non_invasive_recommendations
|
||||
is_extraction_and_refill = (
|
||||
"cavity_extract_and_refill"
|
||||
in self.property.non_invasive_recommendations
|
||||
)
|
||||
|
||||
cost_result = self.costs.cavity_wall_insulation(
|
||||
wall_area=self.property.insulation_wall_area,
|
||||
material=material.to_dict(),
|
||||
is_extraction_and_refill=is_extraction_and_refill
|
||||
is_extraction_and_refill=is_extraction_and_refill,
|
||||
)
|
||||
|
||||
already_installed = "cavity_wall_insulation" in self.property.already_installed
|
||||
already_installed = (
|
||||
"cavity_wall_insulation" in self.property.already_installed
|
||||
)
|
||||
if already_installed:
|
||||
cost_result = override_costs(cost_result)
|
||||
|
||||
|
|
@ -246,7 +277,7 @@ class WallRecommendations(Definitions):
|
|||
part=material.to_dict(),
|
||||
quantity=self.property.insulation_wall_area,
|
||||
quantity_unit=QuantityUnits.m2.value,
|
||||
cost_result=cost_result
|
||||
cost_result=cost_result,
|
||||
)
|
||||
],
|
||||
"type": "cavity_wall_insulation",
|
||||
|
|
@ -255,13 +286,15 @@ class WallRecommendations(Definitions):
|
|||
"new_u_value": new_u_value,
|
||||
"sap_points": None,
|
||||
"already_installed": already_installed,
|
||||
**cost_result
|
||||
**cost_result,
|
||||
}
|
||||
)
|
||||
|
||||
self.recommendations = recommendations
|
||||
|
||||
def _find_insulation(self, u_value, insulation_materials, non_insulation_materials, phase):
|
||||
def _find_insulation(
|
||||
self, u_value, insulation_materials, non_insulation_materials, phase
|
||||
):
|
||||
|
||||
lowest_selected_u_value = None
|
||||
recommendations = []
|
||||
|
|
@ -269,7 +302,9 @@ class WallRecommendations(Definitions):
|
|||
|
||||
for _, material in insulation_material_group.iterrows():
|
||||
|
||||
part_u_value = r_value_per_mm_to_u_value(material["depth"], material["r_value_per_mm"])
|
||||
part_u_value = r_value_per_mm_to_u_value(
|
||||
material["depth"], material["r_value_per_mm"]
|
||||
)
|
||||
_, new_u_value = calculate_u_value_uplift(u_value, part_u_value)
|
||||
new_u_value = math.ceil(new_u_value * 100.0) / 100.0
|
||||
|
||||
|
|
@ -280,22 +315,30 @@ class WallRecommendations(Definitions):
|
|||
# further into the diminishing returns threshold and can shouldn't be
|
||||
|
||||
if is_diminishing_returns(
|
||||
recommendations, new_u_value, lowest_selected_u_value, self.DIMINISHING_RETURNS_U_VALUE
|
||||
recommendations,
|
||||
new_u_value,
|
||||
lowest_selected_u_value,
|
||||
self.DIMINISHING_RETURNS_U_VALUE,
|
||||
):
|
||||
continue
|
||||
|
||||
# We allow a small tolerance for error so we don't discount the recommendation entirely
|
||||
if new_u_value <= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
||||
|
||||
lowest_selected_u_value = update_lowest_selected_u_value(lowest_selected_u_value, new_u_value)
|
||||
lowest_selected_u_value = update_lowest_selected_u_value(
|
||||
lowest_selected_u_value, new_u_value
|
||||
)
|
||||
|
||||
if material["type"] == "internal_wall_insulation":
|
||||
cost_result = self.costs.internal_wall_insulation(
|
||||
wall_area=self.property.insulation_wall_area,
|
||||
material=material.to_dict(),
|
||||
non_insulation_materials=non_insulation_materials
|
||||
non_insulation_materials=non_insulation_materials,
|
||||
)
|
||||
already_installed = (
|
||||
"internal_wall_insulation"
|
||||
in self.property.already_installed
|
||||
)
|
||||
already_installed = "internal_wall_insulation" in self.property.already_installed
|
||||
if already_installed:
|
||||
cost_result = override_costs(cost_result)
|
||||
|
||||
|
|
@ -303,9 +346,12 @@ class WallRecommendations(Definitions):
|
|||
cost_result = self.costs.external_wall_insulation(
|
||||
wall_area=self.property.insulation_wall_area,
|
||||
material=material.to_dict(),
|
||||
non_insulation_materials=non_insulation_materials
|
||||
non_insulation_materials=non_insulation_materials,
|
||||
)
|
||||
already_installed = (
|
||||
"external_wall_insulation"
|
||||
in self.property.already_installed
|
||||
)
|
||||
already_installed = "external_wall_insulation" in self.property.already_installed
|
||||
if already_installed:
|
||||
cost_result = override_costs(cost_result)
|
||||
else:
|
||||
|
|
@ -319,7 +365,7 @@ class WallRecommendations(Definitions):
|
|||
part=material.to_dict(),
|
||||
quantity=self.property.insulation_wall_area,
|
||||
quantity_unit=QuantityUnits.m2.value,
|
||||
cost_result=cost_result
|
||||
cost_result=cost_result,
|
||||
)
|
||||
],
|
||||
"type": material["type"],
|
||||
|
|
@ -328,7 +374,7 @@ class WallRecommendations(Definitions):
|
|||
"new_u_value": new_u_value,
|
||||
"already_installed": already_installed,
|
||||
"sap_points": None,
|
||||
**cost_result
|
||||
**cost_result,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -350,16 +396,18 @@ class WallRecommendations(Definitions):
|
|||
if self.ewi_valid:
|
||||
ewi_recommendations = self._find_insulation(
|
||||
u_value=u_value,
|
||||
insulation_materials=pd.DataFrame(self.external_wall_insulation_materials),
|
||||
insulation_materials=pd.DataFrame(
|
||||
self.external_wall_insulation_materials
|
||||
),
|
||||
non_insulation_materials=self.external_wall_non_insulation_materials,
|
||||
phase=phase
|
||||
phase=phase,
|
||||
)
|
||||
|
||||
iwi_recommendations = self._find_insulation(
|
||||
u_value=u_value,
|
||||
insulation_materials=pd.DataFrame(self.internal_wall_insulation_materials),
|
||||
non_insulation_materials=self.internal_wall_non_insulation_materials,
|
||||
phase=phase
|
||||
phase=phase,
|
||||
)
|
||||
|
||||
self.recommendations += ewi_recommendations + iwi_recommendations
|
||||
|
|
@ -367,12 +415,16 @@ class WallRecommendations(Definitions):
|
|||
@staticmethod
|
||||
def _make_description(material):
|
||||
if material["type"] == "internal_wall_insulation":
|
||||
return (f"Install {int(material['depth'])}{material['depth_unit']} {material['description']} on internal "
|
||||
f"walls")
|
||||
return (
|
||||
f"Install {int(material['depth'])}{material['depth_unit']} {material['description']} on internal "
|
||||
f"walls"
|
||||
)
|
||||
|
||||
if material["type"] == "external_wall_insulation":
|
||||
return (f"Install {int(material['depth'])}{material['depth_unit']} {material['description']} on external "
|
||||
f"walls")
|
||||
return (
|
||||
f"Install {int(material['depth'])}{material['depth_unit']} {material['description']} on external "
|
||||
f"walls"
|
||||
)
|
||||
|
||||
if material["type"] == "cavity_wall_insulation":
|
||||
return f"Fill cavity with {material['description']}"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import numpy as np
|
|||
|
||||
from backend.Property import Property
|
||||
from recommendations.Costs import Costs
|
||||
from recommendation_utils import override_costs
|
||||
from recommendations.recommendation_utils import override_costs
|
||||
|
||||
|
||||
class WindowsRecommendations:
|
||||
|
|
@ -14,7 +14,7 @@ class WindowsRecommendations:
|
|||
# glazed
|
||||
"most": 0.33,
|
||||
# If glazing is partial, we assume 50/50 split between glazed and unglazed
|
||||
"partial": 0.5
|
||||
"partial": 0.5,
|
||||
}
|
||||
|
||||
def __init__(self, property_instance: Property, materials: List):
|
||||
|
|
@ -52,14 +52,20 @@ class WindowsRecommendations:
|
|||
if not number_of_windows:
|
||||
raise ValueError("Number of windows not specified")
|
||||
|
||||
if self.property.windows["has_glazing"] & (self.property.windows["glazing_coverage"] == "full"):
|
||||
if self.property.windows["has_glazing"] & (
|
||||
self.property.windows["glazing_coverage"] == "full"
|
||||
):
|
||||
return
|
||||
|
||||
# We scale the number of windows based on the proportion of existing glazing
|
||||
if self.property.data["multi-glaze-proportion"] != "":
|
||||
n_windows_scalar = 1 - (int(self.property.data["multi-glaze-proportion"]) / 100)
|
||||
n_windows_scalar = 1 - (
|
||||
int(self.property.data["multi-glaze-proportion"]) / 100
|
||||
)
|
||||
else:
|
||||
n_windows_scalar = self.COVERAGE_MAP.get(self.property.windows["glazing_coverage"], 1)
|
||||
n_windows_scalar = self.COVERAGE_MAP.get(
|
||||
self.property.windows["glazing_coverage"], 1
|
||||
)
|
||||
|
||||
number_of_windows *= n_windows_scalar
|
||||
number_of_windows = np.ceil(number_of_windows)
|
||||
|
|
@ -68,7 +74,7 @@ class WindowsRecommendations:
|
|||
cost_result = self.costs.window_glazing(
|
||||
number_of_windows=number_of_windows,
|
||||
material=self.glazing_material,
|
||||
is_secondary_glazing=is_secondary_glazing
|
||||
is_secondary_glazing=is_secondary_glazing,
|
||||
)
|
||||
|
||||
already_installed = "windows_glazing" in self.property.already_installed
|
||||
|
|
@ -76,18 +82,26 @@ class WindowsRecommendations:
|
|||
cost_result = override_costs(cost_result)
|
||||
description = "The property already has double glazing installed. No further action is required."
|
||||
else:
|
||||
glazing_type = "secondary glazing" if is_secondary_glazing else "double glazing"
|
||||
glazing_type = (
|
||||
"secondary glazing" if is_secondary_glazing else "double glazing"
|
||||
)
|
||||
if self.property.windows["glazing_coverage"] in ["partial", "most"]:
|
||||
description = f"Install {glazing_type} to the remaining windows"
|
||||
else:
|
||||
description = f"Install {glazing_type} to all windows"
|
||||
|
||||
if self.property.is_listed:
|
||||
description += ". Secondary glazing recommended due to listed building status"
|
||||
description += (
|
||||
". Secondary glazing recommended due to listed building status"
|
||||
)
|
||||
elif self.property.is_heritage:
|
||||
description += ". Secondary glazing recommended due to herigate building status"
|
||||
description += (
|
||||
". Secondary glazing recommended due to herigate building status"
|
||||
)
|
||||
elif self.property.in_conservation_area:
|
||||
description += ". Secondary glazing recommended due to conservation area status"
|
||||
description += (
|
||||
". Secondary glazing recommended due to conservation area status"
|
||||
)
|
||||
|
||||
self.recommendation = [
|
||||
{
|
||||
|
|
@ -100,6 +114,6 @@ class WindowsRecommendations:
|
|||
"sap_points": None,
|
||||
"already_installed": already_installed,
|
||||
**cost_result,
|
||||
"is_secondary_glazing": is_secondary_glazing
|
||||
"is_secondary_glazing": is_secondary_glazing,
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue