mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
Adding in condition checks for various measures
This commit is contained in:
parent
1c6ffd6c05
commit
a5876d40db
4 changed files with 94 additions and 45 deletions
|
|
@ -8,6 +8,11 @@ from recommendations.HeatingControlRecommender import HeatingControlRecommender
|
||||||
|
|
||||||
|
|
||||||
class HeatingRecommender:
|
class HeatingRecommender:
|
||||||
|
ELECTRIC_HEATING_DESCRIPTIONS = [
|
||||||
|
"Room heaters, electric",
|
||||||
|
"Electric storage heaters",
|
||||||
|
"Electric storage heaters, radiators"
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, property_instance: Property):
|
def __init__(self, property_instance: Property):
|
||||||
self.property = property_instance
|
self.property = property_instance
|
||||||
|
|
@ -16,6 +21,23 @@ class HeatingRecommender:
|
||||||
self.heating_recommendations = []
|
self.heating_recommendations = []
|
||||||
self.heating_control_recommendations = []
|
self.heating_control_recommendations = []
|
||||||
|
|
||||||
|
self.has_electric_heating_description = (
|
||||||
|
self.property.main_heating["clean_description"] in self.ELECTRIC_HEATING_DESCRIPTIONS
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_high_heat_retention_valid(self):
|
||||||
|
"""
|
||||||
|
Check conditions if high heat retention storage is valid
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
no_heating_no_mains = (
|
||||||
|
self.property.main_heating["clean_description"] in ["No system present, electric heaters assumed"] and
|
||||||
|
not self.property.data["mains-gas-flag"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.has_electric_heating_description or no_heating_no_mains
|
||||||
|
|
||||||
def recommend(self, has_cavity_or_loft_recommendations, phase=0):
|
def recommend(self, has_cavity_or_loft_recommendations, phase=0):
|
||||||
"""
|
"""
|
||||||
Produces heating recommendations
|
Produces heating recommendations
|
||||||
|
|
@ -34,16 +56,7 @@ class HeatingRecommender:
|
||||||
# This first iteration of the recommender will provide very basic recommendation
|
# This first iteration of the recommender will provide very basic recommendation
|
||||||
# We recommend heating controls based on the main heating system
|
# We recommend heating controls based on the main heating system
|
||||||
|
|
||||||
has_electric_heating_description = self.property.main_heating["clean_description"] in [
|
if self.is_high_heat_retention_valid():
|
||||||
"Room heaters, electric", "Electric storage heaters", "Electric storage heaters, radiators"
|
|
||||||
]
|
|
||||||
|
|
||||||
no_heating_no_mains = (
|
|
||||||
self.property.main_heating["clean_description"] in ["No system present, electric heaters assumed"] and
|
|
||||||
not self.property.data["mains-gas-flag"]
|
|
||||||
)
|
|
||||||
|
|
||||||
if has_electric_heating_description or no_heating_no_mains:
|
|
||||||
# Recommend high heat retention storage heaters
|
# Recommend high heat retention storage heaters
|
||||||
self.recommend_hhr_storage_heaters(phase=phase, system_change=True, heating_controls_only=False)
|
self.recommend_hhr_storage_heaters(phase=phase, system_change=True, heating_controls_only=False)
|
||||||
|
|
||||||
|
|
@ -61,7 +74,7 @@ class HeatingRecommender:
|
||||||
)
|
)
|
||||||
|
|
||||||
# We also check if the property has electric heating, but it has access to the mains gas
|
# We also check if the property has electric heating, but it has access to the mains gas
|
||||||
electic_heating_has_mains = has_electric_heating_description and self.property.data["mains-gas-flag"]
|
electic_heating_has_mains = self.has_electric_heating_description and self.property.data["mains-gas-flag"]
|
||||||
|
|
||||||
portable_heaters_has_mains = (
|
portable_heaters_has_mains = (
|
||||||
self.property.main_heating["clean_description"] in ["Portable electric heaters assumed for most rooms"] and
|
self.property.main_heating["clean_description"] in ["Portable electric heaters assumed for most rooms"] and
|
||||||
|
|
@ -93,16 +106,19 @@ class HeatingRecommender:
|
||||||
# In the future, we'll allow overrides, so that non-intrusive surveys can contradict these conditions
|
# In the future, we'll allow overrides, so that non-intrusive surveys can contradict these conditions
|
||||||
# and either allow or prevent the recommendation of an air source heat pump
|
# and either allow or prevent the recommendation of an air source heat pump
|
||||||
|
|
||||||
suitable_property_type = self.property.data["property-type"] in ["House", "Bungalow"]
|
if self.is_ashp_valid():
|
||||||
has_air_source_heat_pump = self.property.main_heating["has_air_source_heat_pump"]
|
|
||||||
|
|
||||||
if suitable_property_type and not has_air_source_heat_pump:
|
|
||||||
self.recommend_air_source_heat_pump(
|
self.recommend_air_source_heat_pump(
|
||||||
phase=phase, has_cavity_or_loft_recommendations=has_cavity_or_loft_recommendations
|
phase=phase, has_cavity_or_loft_recommendations=has_cavity_or_loft_recommendations
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def is_ashp_valid(self):
|
||||||
|
suitable_property_type = self.property.data["property-type"] in ["House", "Bungalow"]
|
||||||
|
has_air_source_heat_pump = self.property.main_heating["has_air_source_heat_pump"]
|
||||||
|
|
||||||
|
return suitable_property_type and not has_air_source_heat_pump
|
||||||
|
|
||||||
def recommend_air_source_heat_pump(self, phase, has_cavity_or_loft_recommendations, _return=False):
|
def recommend_air_source_heat_pump(self, phase, has_cavity_or_loft_recommendations, _return=False):
|
||||||
"""
|
"""
|
||||||
This method will implement the recommendation for an air source heat pump
|
This method will implement the recommendation for an air source heat pump
|
||||||
|
|
|
||||||
|
|
@ -123,36 +123,58 @@ class Mds:
|
||||||
continue
|
continue
|
||||||
# There are certain measures where we need to
|
# There are certain measures where we need to
|
||||||
if measure == "external_wall_insulation":
|
if measure == "external_wall_insulation":
|
||||||
# Check if the wall is solid
|
# Check if the wall is not cavity since the other wall types can take external wall insulation
|
||||||
if self.property_instance.walls['is_solid_brick']:
|
if self.wall_recommender.ewi_valid():
|
||||||
pruned_measures.append(measure)
|
pruned_measures.append(measure)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if measure == "cavity_wall_insulation":
|
if measure == "cavity_wall_insulation":
|
||||||
# Check if the wall is cavity
|
# Check if the wall is cavity
|
||||||
if self.property_instance.walls['is_cavity_wall']:
|
if (
|
||||||
|
self.property_instance.walls['is_cavity_wall'] and
|
||||||
|
not self.property_instance.walls['is_filled_cavity']
|
||||||
|
):
|
||||||
pruned_measures.append(measure)
|
pruned_measures.append(measure)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if measure == "loft_insulation":
|
if measure == "loft_insulation":
|
||||||
# Check if the roof is suitable for loft insulation
|
# Check if the roof is suitable for loft insulation and the loft isn't already done
|
||||||
if self.property_instance.roof["is_pitched"]:
|
if (
|
||||||
|
self.property_instance.roof["is_pitched"] and
|
||||||
|
not self.roof_recommender.is_loft_already_insulated()
|
||||||
|
):
|
||||||
pruned_measures.append(measure)
|
pruned_measures.append(measure)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if measure == "solid_floor_insulation":
|
if measure == "solid_floor_insulation":
|
||||||
# Check if the floor is solid
|
# Check if the floor is solid
|
||||||
if self.property_instance.floor["is_solid"]:
|
if (
|
||||||
|
self.property_instance.floor["is_solid"] and
|
||||||
|
self.property_instance.floor["insulation_thickness"] not in ["average", "above average"]
|
||||||
|
):
|
||||||
pruned_measures.append(measure)
|
pruned_measures.append(measure)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if measure == "suspended_floor_insulation":
|
if measure == "suspended_floor_insulation":
|
||||||
# Check if the floor is suspended
|
# Check if the floor is suspended
|
||||||
if self.property_instance.floor["is_suspended"]:
|
if (
|
||||||
|
self.property_instance.floor["is_suspended"] and
|
||||||
|
self.property_instance.floor["insulation_thickness"] not in ["average", "above average"]
|
||||||
|
):
|
||||||
pruned_measures.append(measure)
|
pruned_measures.append(measure)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pruned_measures.append(measure)
|
if measure == "high_heat_retention_storage_heaters":
|
||||||
|
if self.heating_recommender.is_high_heat_retention_valid():
|
||||||
|
pruned_measures.append(measure)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if measure == "air_source_heat_pump":
|
||||||
|
if self.heating_recommender.is_ashp_valid():
|
||||||
|
pruned_measures.append(measure)
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise NotImplementedError("Implement me")
|
||||||
|
|
||||||
pruned_measures_formatted = []
|
pruned_measures_formatted = []
|
||||||
for pm in pruned_measures:
|
for pm in pruned_measures:
|
||||||
|
|
@ -311,7 +333,6 @@ class Mds:
|
||||||
|
|
||||||
if self.optimise_measures:
|
if self.optimise_measures:
|
||||||
measures_set = self.select_optimal_measure_set(self.property_instance.measures)
|
measures_set = self.select_optimal_measure_set(self.property_instance.measures)
|
||||||
logger.info(f"Building recommendations for {len(measures_set)} combinations of measures")
|
|
||||||
mds_recommendations_map = {}
|
mds_recommendations_map = {}
|
||||||
representative_recommendations_map = {}
|
representative_recommendations_map = {}
|
||||||
errors_map = {}
|
errors_map = {}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,13 @@ class RoofRecommendations:
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Extract the insulation thickness from the roof, which is used throughout this method
|
||||||
|
self.insulation_thickness = convert_thickness_to_numeric(
|
||||||
|
self.property.roof["insulation_thickness"],
|
||||||
|
self.property.roof["is_pitched"],
|
||||||
|
self.property.roof["is_flat"]
|
||||||
|
)
|
||||||
|
|
||||||
def mds_loft_insulation(self, phase):
|
def mds_loft_insulation(self, phase):
|
||||||
"""
|
"""
|
||||||
For usages within the mds report
|
For usages within the mds report
|
||||||
|
|
@ -62,18 +69,18 @@ class RoofRecommendations:
|
||||||
"""
|
"""
|
||||||
self.recommendations = []
|
self.recommendations = []
|
||||||
|
|
||||||
insulation_thickness = convert_thickness_to_numeric(
|
|
||||||
self.property.roof["insulation_thickness"],
|
|
||||||
self.property.roof["is_pitched"],
|
|
||||||
self.property.roof["is_flat"]
|
|
||||||
)
|
|
||||||
|
|
||||||
u_value = get_roof_u_value(**{**self.property.roof, "age_band": self.property.age_band})
|
u_value = get_roof_u_value(**{**self.property.roof, "age_band": self.property.age_band})
|
||||||
|
|
||||||
self.recommend_roof_insulation(u_value, insulation_thickness, self.property.roof, phase)
|
self.recommend_roof_insulation(u_value, self.insulation_thickness, self.property.roof, phase)
|
||||||
|
|
||||||
return self.recommendations
|
return self.recommendations
|
||||||
|
|
||||||
|
def is_loft_already_insulated(self):
|
||||||
|
"""
|
||||||
|
Check if the loft is already insulated
|
||||||
|
"""
|
||||||
|
return (self.insulation_thickness > self.MINIMUM_LOFT_ISULATION_MM) and self.property.roof["is_pitched"]
|
||||||
|
|
||||||
def recommend(self, phase):
|
def recommend(self, phase):
|
||||||
|
|
||||||
if self.property.roof["has_dwelling_above"]:
|
if self.property.roof["has_dwelling_above"]:
|
||||||
|
|
@ -81,21 +88,15 @@ class RoofRecommendations:
|
||||||
|
|
||||||
u_value = self.property.roof["thermal_transmittance"]
|
u_value = self.property.roof["thermal_transmittance"]
|
||||||
|
|
||||||
insulation_thickness = convert_thickness_to_numeric(
|
|
||||||
self.property.roof["insulation_thickness"],
|
|
||||||
self.property.roof["is_pitched"],
|
|
||||||
self.property.roof["is_flat"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# We check if the roof is already insulated and if so, we exit
|
# We check if the roof is already insulated and if so, we exit
|
||||||
|
|
||||||
# Building regulations part L recommend installing at least 270mm of insulation, however generally we
|
# Building regulations part L recommend installing at least 270mm of insulation, however generally we
|
||||||
# experience diminishing returns in terms of SAP once we go beyond around 150mm of insulation
|
# experience diminishing returns in terms of SAP once we go beyond around 150mm of insulation
|
||||||
# This only holds true for pitched roofs.
|
# This only holds true for pitched roofs.
|
||||||
if (insulation_thickness > self.MINIMUM_LOFT_ISULATION_MM) and self.property.roof["is_pitched"]:
|
if self.is_loft_already_insulated():
|
||||||
return
|
return
|
||||||
|
|
||||||
if (insulation_thickness >= self.MINIMUM_FLAT_ROOF_ISULATION_MM) and self.property.roof["is_flat"]:
|
if (self.insulation_thickness >= self.MINIMUM_FLAT_ROOF_ISULATION_MM) and self.property.roof["is_flat"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.property.roof["is_roof_room"]:
|
if self.property.roof["is_roof_room"]:
|
||||||
|
|
@ -119,7 +120,7 @@ class RoofRecommendations:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.property.roof["is_pitched"] or self.property.roof["is_flat"]:
|
if self.property.roof["is_pitched"] or self.property.roof["is_flat"]:
|
||||||
self.recommend_roof_insulation(u_value, insulation_thickness, self.property.roof, phase)
|
self.recommend_roof_insulation(u_value, self.insulation_thickness, self.property.roof, phase)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.property.roof["is_roof_room"]:
|
if self.property.roof["is_roof_room"]:
|
||||||
|
|
|
||||||
|
|
@ -112,11 +112,9 @@ class WallRecommendations(Definitions):
|
||||||
self.external_wall_non_insulation_materials = [
|
self.external_wall_non_insulation_materials = [
|
||||||
part
|
part
|
||||||
for part in materials
|
for part in materials
|
||||||
if part["type"]
|
if part["type"] in ["ewi_wall_demolition", "ewi_wall_preparation", "ewi_wall_redecoration"]
|
||||||
in ["ewi_wall_demolition", "ewi_wall_preparation", "ewi_wall_redecoration"]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@property
|
|
||||||
def ewi_valid(self):
|
def ewi_valid(self):
|
||||||
"""
|
"""
|
||||||
This method check available data, to determine if a property is suitable for external wall insulation
|
This method check available data, to determine if a property is suitable for external wall insulation
|
||||||
|
|
@ -126,11 +124,24 @@ class WallRecommendations(Definitions):
|
||||||
# it is not suitable for EWI
|
# it is not suitable for EWI
|
||||||
if self.property.restricted_measures or (
|
if self.property.restricted_measures or (
|
||||||
self.property.data["property-type"].lower() == "flat"
|
self.property.data["property-type"].lower() == "flat"
|
||||||
|
) or (
|
||||||
|
self.property.walls['is_cob'] or
|
||||||
|
self.property.walls['is_sandstone_or_limestone'] or
|
||||||
|
self.property.walls["is_cavity_wall"]
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def is_suitable_for_solid_insulation(self):
|
||||||
|
"""
|
||||||
|
Checks if the wall is of a suitable type for internal/external wall insulation
|
||||||
|
"""
|
||||||
|
if self.property.walls["is_cavity_wall"] or self.property.walls["is_cob"]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def mds_recommend_cavity_wall_insulation(self, phase=None):
|
def mds_recommend_cavity_wall_insulation(self, phase=None):
|
||||||
# Function specifically for cavity wall insulation, for usage in the mds report
|
# Function specifically for cavity wall insulation, for usage in the mds report
|
||||||
self.recommendations = []
|
self.recommendations = []
|
||||||
|
|
@ -249,7 +260,7 @@ class WallRecommendations(Definitions):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Remaining wall types are treated with IWI or EWI
|
# Remaining wall types are treated with IWI or EWI
|
||||||
if u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE:
|
if (u_value >= self.BUILDING_REGULATIONS_PART_L_MAX_U_VALUE) and self.is_suitable_for_solid_insulation():
|
||||||
self.find_insulation(u_value, phase)
|
self.find_insulation(u_value, phase)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -528,7 +539,7 @@ class WallRecommendations(Definitions):
|
||||||
# consider diminishing returns between the two as they are considered to be separate measures
|
# consider diminishing returns between the two as they are considered to be separate measures
|
||||||
|
|
||||||
ewi_recommendations = []
|
ewi_recommendations = []
|
||||||
if self.ewi_valid:
|
if self.ewi_valid():
|
||||||
ewi_recommendations = self._find_insulation(
|
ewi_recommendations = self._find_insulation(
|
||||||
u_value=u_value,
|
u_value=u_value,
|
||||||
insulation_materials=pd.DataFrame(
|
insulation_materials=pd.DataFrame(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue