mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Added dual heating combined recommendation
This commit is contained in:
parent
76154c5172
commit
68fc8b8cbb
5 changed files with 360 additions and 42 deletions
|
|
@ -12,8 +12,11 @@ class HeatingControlRecommender:
|
|||
|
||||
self.recommendation = []
|
||||
|
||||
def recommend(self, heating_description):
|
||||
def recommend(self, heating_description, description_prefix="", description_suffix=""):
|
||||
|
||||
# TODO: Many of these functions are quite similar. We can possibly create a single wrapper function that
|
||||
# takes in the heating description and the description prefix/suffix, and then creates the appropriate
|
||||
# output
|
||||
# Reset the recommendations
|
||||
self.recommendation = []
|
||||
|
||||
|
|
@ -24,14 +27,14 @@ class HeatingControlRecommender:
|
|||
return
|
||||
|
||||
if heating_description in ["Electric storage heaters", "Electric storage heaters, radiators"]:
|
||||
self.recommend_high_heat_retention_controls()
|
||||
self.recommend_high_heat_retention_controls(description_prefix=description_prefix)
|
||||
return
|
||||
|
||||
if heating_description in ["Boiler and radiators, mains gas"]:
|
||||
# We can recommend roomstat programmer trvs
|
||||
self.recommend_roomstat_programmer_trvs()
|
||||
self.recommend_roomstat_programmer_trvs(description_suffix=description_suffix)
|
||||
# We can also recommend time and temperature zone controls
|
||||
self.recommend_time_temperature_zone_controls()
|
||||
self.recommend_time_temperature_zone_controls(description_suffix=description_suffix)
|
||||
|
||||
return
|
||||
|
||||
|
|
@ -94,16 +97,22 @@ class HeatingControlRecommender:
|
|||
# We don't implement any other recommendations right now
|
||||
return
|
||||
|
||||
def recommend_high_heat_retention_controls(self):
|
||||
def recommend_high_heat_retention_controls(self, description_prefix=""):
|
||||
"""
|
||||
When applicable, we recommend upgrading the heating controls to high heat retention controls. This is a
|
||||
specific type of control system that is designed to work with electric storage heaters. It is a more
|
||||
efficient control system than the standard controls that come with electric storage heaters.
|
||||
|
||||
We can then consider the heating system itself
|
||||
|
||||
If there is a description prefix, this means there is a dual heating system and so we need to add this to the
|
||||
description
|
||||
|
||||
:return:
|
||||
"""
|
||||
new_description = "Controls for high heat retention storage heaters"
|
||||
if description_prefix:
|
||||
new_description = f"{description_prefix}, {new_description}"
|
||||
|
||||
# We recommend upgrading to Celect type controls
|
||||
ending_config = MainheatControlAttributes(new_description).process()
|
||||
|
|
@ -112,7 +121,10 @@ class HeatingControlRecommender:
|
|||
new_config=ending_config, old_config=self.property.main_heating_controls
|
||||
)
|
||||
# This upgrade will only take the heating system to average energy efficiency
|
||||
simulation_config["mainheatc_energy_eff_ending"] = "Good"
|
||||
if self.property.data["mainheatc-energy-eff"] in ["Poor", "Very Poor", "Average"]:
|
||||
simulation_config["mainheatc_energy_eff_ending"] = "Good"
|
||||
else:
|
||||
simulation_config["mainheatc_energy_eff_ending"] = self.property.data["mainheatc-energy-eff"]
|
||||
|
||||
description_simulation = {
|
||||
"mainheatcont-description": new_description,
|
||||
|
|
@ -131,7 +143,7 @@ class HeatingControlRecommender:
|
|||
# We don't implement any other recommendations right now
|
||||
return
|
||||
|
||||
def recommend_roomstat_programmer_trvs(self):
|
||||
def recommend_roomstat_programmer_trvs(self, description_suffix=""):
|
||||
"""
|
||||
If the home has a boiler and radiators, mains gas, we start by identifying potential heating controls that could
|
||||
be upgraded, that would provide a practical impact.
|
||||
|
|
@ -163,6 +175,8 @@ class HeatingControlRecommender:
|
|||
return
|
||||
|
||||
new_controls_description = "Programmer, room thermostat and TRVS"
|
||||
if description_suffix:
|
||||
new_controls_description = f"{new_controls_description}, {description_suffix}"
|
||||
|
||||
ending_config = MainheatControlAttributes(new_controls_description).process()
|
||||
# We use this to determine how we should be updating the config
|
||||
|
|
@ -216,7 +230,7 @@ class HeatingControlRecommender:
|
|||
|
||||
return
|
||||
|
||||
def recommend_time_temperature_zone_controls(self):
|
||||
def recommend_time_temperature_zone_controls(self, description_suffix=""):
|
||||
"""
|
||||
If the home has a boiler, we can recommend time and temperature zone controls. This is a more advanced
|
||||
and more efficient control system than the standard controls that come with a boiler. However, it may come
|
||||
|
|
@ -238,6 +252,8 @@ class HeatingControlRecommender:
|
|||
return
|
||||
|
||||
new_controls_description = "Time and temperature zone control"
|
||||
if description_suffix:
|
||||
new_controls_description = f"{new_controls_description}, {description_suffix}"
|
||||
|
||||
ending_config = MainheatControlAttributes(new_controls_description).process()
|
||||
|
||||
|
|
@ -260,8 +276,10 @@ class HeatingControlRecommender:
|
|||
number_heated_rooms=int(self.property.data["number-heated-rooms"])
|
||||
)
|
||||
|
||||
description = ("Upgrade heating controls to Smart Thermostats, room sensors and smart radiator valves (time & "
|
||||
"temperature zone control)")
|
||||
description = (
|
||||
"Upgrade heating controls to Smart Thermostats, room sensors and smart radiator valves (time & "
|
||||
"temperature zone control)"
|
||||
)
|
||||
|
||||
already_installed = "heating_control" in self.property.already_installed
|
||||
if already_installed:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from recommendations.Costs import Costs, BOILER_UPGRADE_SCHEME_ASHP_VALUE
|
||||
from recommendations.recommendation_utils import check_simulation_difference, override_costs
|
||||
from recommendations.recommendation_utils import (
|
||||
check_simulation_difference, override_costs, combine_recommendation_configs
|
||||
)
|
||||
from backend.Property import Property
|
||||
from backend.app.plan.schemas import MEASURE_MAP
|
||||
from etl.epc_clean.epc_attributes.MainheatAttributes import MainHeatAttributes
|
||||
|
|
@ -11,6 +13,37 @@ from recommendations.HeatingControlRecommender import HeatingControlRecommender
|
|||
class HeatingRecommender:
|
||||
high_heat_retention_contols_desc = "Controls for high heat retention storage heaters"
|
||||
|
||||
DUAL_HEATING_DESCRIPTIONS = {
|
||||
"Boiler and radiators, mains gas, electric storage heaters": {
|
||||
"hhr": {
|
||||
"mainheating_description": "Boiler and radiators, mains gas, Electric storage heaters",
|
||||
"recommendation_description": "Install high heat retention electric storage heaters alongside the "
|
||||
"boiler. The current electric heaters may be retrofit with high heat "
|
||||
"retention storage controls"
|
||||
" however this is dependent on the existing system and may not be "
|
||||
"possible.",
|
||||
"controls_prefix": "current_controls"
|
||||
},
|
||||
"boiler": {
|
||||
"mainheating_description": "Boiler and radiators, mains gas, electric storage heaters",
|
||||
"recommendation_description": "Upgrade the existing boiler to a new, more efficient condensing "
|
||||
"boiler. ",
|
||||
"controls_suffix": "Manual charge controls"
|
||||
},
|
||||
# These are the heating types we need to produce a dual heating recommendation
|
||||
"dual": {
|
||||
"recommendation_description": "Upgrade both the existing boiler to a new condensing boiler and"
|
||||
"upgrade storage heaters to high heat retention storage heaters.",
|
||||
"types": [
|
||||
# type 1
|
||||
"boiler_upgrade",
|
||||
# type 2
|
||||
"high_heat_retention_storage_heater",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, property_instance: Property):
|
||||
self.property = property_instance
|
||||
self.costs = Costs(self.property)
|
||||
|
|
@ -20,10 +53,34 @@ class HeatingRecommender:
|
|||
|
||||
self.has_electric_heating_description = self.property.main_heating["has_electric"]
|
||||
self.has_ashp = self.property.main_heating["has_air_source_heat_pump"]
|
||||
self.has_room_heaters = (
|
||||
self.property.main_heating["has_room_heaters"] or
|
||||
self.property.main_heating["has_portable_electric_heaters"]
|
||||
)
|
||||
self.has_boiler = self.property.main_heating["has_boiler"]
|
||||
|
||||
self.dual_heating = self.identify_dual_heating()
|
||||
|
||||
def identify_dual_heating(self):
|
||||
# All heat systems are in here so we identify whether two of these are true
|
||||
# MainHeatAttributes.HEAT_SYSTEMS
|
||||
|
||||
n_trues = 0
|
||||
for heat_system in MainHeatAttributes.HEAT_SYSTEMS:
|
||||
if self.property.main_heating[f"has_{heat_system.replace(' ', '_')}"]:
|
||||
n_trues += 1
|
||||
|
||||
if n_trues > 2 or n_trues == 0:
|
||||
raise Exception("Implement me")
|
||||
if n_trues == 1:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def is_high_heat_retention_valid(self, ashp_only_heating_recommendation, measures):
|
||||
"""
|
||||
Check conditions if high heat retention storage is valid
|
||||
If there's already an ASHP in place, we don't recommend HHR
|
||||
:return:
|
||||
"""
|
||||
|
||||
|
|
@ -32,8 +89,6 @@ class HeatingRecommender:
|
|||
|
||||
hhr_suitable = no_mains or self.has_electric_heating_description
|
||||
|
||||
# If there's already an ASHP in place, we don't recommend HHR
|
||||
|
||||
return (
|
||||
hhr_suitable and (not ashp_only_heating_recommendation) and not self.has_ashp and
|
||||
("high_heat_retention_storage_heater" in measures)
|
||||
|
|
@ -47,7 +102,7 @@ class HeatingRecommender:
|
|||
|
||||
# 1) if the property has mains heating with boiler and radiators, we recommend optimal heating controls
|
||||
# If it's NOT a gas boiler, we'll potentially recommend a boiler
|
||||
has_gas_boiler = self.property.main_heating["has_boiler"] and self.property.main_heating["has_mains_gas"]
|
||||
has_gas_boiler = self.has_boiler and self.property.main_heating["has_mains_gas"]
|
||||
|
||||
# 2) If the property doesn't have a heating system, but it has access to the mains gas
|
||||
no_heating_has_mains = self.property.main_heating["clean_description"] in [
|
||||
|
|
@ -55,21 +110,13 @@ class HeatingRecommender:
|
|||
] and self.property.data["mains-gas-flag"]
|
||||
|
||||
# The property is using portable heaters and has access to gas mains
|
||||
has_room_heaters = (
|
||||
(
|
||||
self.property.main_heating["has_room_heaters"] or
|
||||
self.property.main_heating["has_portable_electric_heaters"]
|
||||
) and
|
||||
self.property.data["mains-gas-flag"]
|
||||
)
|
||||
has_room_heaters = self.has_room_heaters and self.property.data["mains-gas-flag"]
|
||||
|
||||
# We also check if the property has electric heating, but it has access to the mains gas
|
||||
electic_heating_has_mains = self.has_electric_heating_description and self.property.data["mains-gas-flag"]
|
||||
|
||||
portable_heaters_has_mains = (
|
||||
self.property.main_heating["clean_description"] in ["Portable electric heaters assumed for most rooms"]
|
||||
and
|
||||
self.property.data["mains-gas-flag"]
|
||||
self.property.main_heating["has_portable_electric_heaters"] and self.property.data["mains-gas-flag"]
|
||||
)
|
||||
|
||||
# The next condition is if the home has a non-gas boiler, such as an oil boiler, with a mains gas connection
|
||||
|
|
@ -95,6 +142,55 @@ class HeatingRecommender:
|
|||
|
||||
return is_valid, has_gas_boiler
|
||||
|
||||
def recommend_dual_heating(self):
|
||||
|
||||
if self.property.main_heating["clean_description"] not in self.DUAL_HEATING_DESCRIPTIONS:
|
||||
return
|
||||
|
||||
dual_heating_description = self.DUAL_HEATING_DESCRIPTIONS[
|
||||
self.property.main_heating["clean_description"]
|
||||
]["dual"]["types"]
|
||||
|
||||
recommendation_system_types = list(set([x["system_type"] for x in self.heating_recommendations]))
|
||||
|
||||
# We check if we have the required type
|
||||
if not any([x in recommendation_system_types for x in dual_heating_description]):
|
||||
return
|
||||
|
||||
type_1_recommendations = [
|
||||
x for x in self.heating_recommendations if x["system_type"] == dual_heating_description[0]
|
||||
]
|
||||
type_2_recommendations = [
|
||||
x for x in self.heating_recommendations if x["system_type"] == dual_heating_description[1]
|
||||
]
|
||||
# we combine the two recommendations
|
||||
combined_recommendations = []
|
||||
for rec in type_1_recommendations:
|
||||
for rec2 in type_2_recommendations:
|
||||
combined_rec = rec.copy()
|
||||
# Update the description
|
||||
combined_rec["description"] = self.DUAL_HEATING_DESCRIPTIONS[
|
||||
self.property.main_heating["clean_description"]
|
||||
]["dual"]["recommendation_description"]
|
||||
|
||||
# Combine simulation_config
|
||||
# Make sure we end up with the best efficiecy values
|
||||
combined_rec["simulation_config"] = combine_recommendation_configs(
|
||||
rec["simulation_config"], rec2["simulation_config"]
|
||||
)
|
||||
# Combine description_simulation
|
||||
combined_rec["description_simulation"] = combine_recommendation_configs(
|
||||
rec["description_simulation"], rec2["description_simulation"]
|
||||
)
|
||||
|
||||
# Combine costs
|
||||
for k in ["total", "subtotal", "vat", "labour_hours", "labour_days"]:
|
||||
combined_rec[k] = rec[k] + rec2[k]
|
||||
|
||||
combined_recommendations.append(combined_rec)
|
||||
|
||||
self.heating_recommendations.extend(combined_recommendations)
|
||||
|
||||
def recommend(self, has_cavity_or_loft_recommendations, phase=0, measures=None):
|
||||
"""
|
||||
Produces heating recommendations
|
||||
|
|
@ -144,14 +240,16 @@ class HeatingRecommender:
|
|||
# This indicates that the home previously did not have a boiler in place and so would require
|
||||
# an overhaul to the system - right now, this is all reasons, apart from if there is an existing boiler
|
||||
system_change = not has_gas_boiler
|
||||
exising_room_heaters = self.property.main_heating["clean_description"] in [
|
||||
"Room heaters, electric", "Room heaters, mains gas"
|
||||
]
|
||||
exising_room_heaters = self.property.main_heating["has_room_heaters"]
|
||||
|
||||
self.recommend_boiler_upgrades(
|
||||
phase=phase, system_change=system_change, exising_room_heaters=exising_room_heaters
|
||||
)
|
||||
|
||||
# If we have dual heating and we allow for a combined recommendation, to upgrade both systems
|
||||
if self.dual_heating:
|
||||
self.recommend_dual_heating(phase=phase, measures=measures)
|
||||
|
||||
# We recommend air source heat pumps
|
||||
# Heat pumps are suitable for all property types:
|
||||
# https://energysavingtrust.org.uk/from-flats-to-terraced-houses-heat-pumps-are-suitable-for-all-property-types/
|
||||
|
|
@ -424,7 +522,8 @@ class HeatingRecommender:
|
|||
description,
|
||||
phase,
|
||||
heating_controls_only,
|
||||
system_change
|
||||
system_change,
|
||||
system_type
|
||||
):
|
||||
"""
|
||||
Given a recommendation for heating controls, and a recommendation for the heating system, we combine the two
|
||||
|
|
@ -439,6 +538,7 @@ class HeatingRecommender:
|
|||
:param system_change: Indicates if we are recommending a different type of heating system, compared to the
|
||||
current system. If we have a system change and we have a heat control recommendation, we only recommend
|
||||
both heating and controls together
|
||||
:param system_type: The type of heating system we are recommending
|
||||
:return:
|
||||
"""
|
||||
|
||||
|
|
@ -494,7 +594,9 @@ class HeatingRecommender:
|
|||
"already_installed": already_installed,
|
||||
**total_costs,
|
||||
"simulation_config": recommendation_simulation_config,
|
||||
"description_simulation": recommendation_description_simulation
|
||||
"description_simulation": recommendation_description_simulation,
|
||||
# We insert the heating system type here
|
||||
"system_type": system_type
|
||||
}
|
||||
|
||||
output.append(recommendation)
|
||||
|
|
@ -572,7 +674,19 @@ class HeatingRecommender:
|
|||
|
||||
# We only recommend Celect-type controls if the current heating system is not Celect-type controls
|
||||
if self.property.main_heating_controls["clean_description"] != self.high_heat_retention_contols_desc:
|
||||
controls_recommender.recommend(heating_description="Electric storage heaters, radiators")
|
||||
if self.dual_heating:
|
||||
if self.DUAL_HEATING_DESCRIPTIONS[self.property.main_heating["clean_description"]]["hhr"][
|
||||
"controls_prefix"
|
||||
] == "current_controls":
|
||||
description_prefix = self.property.main_heating_controls["clean_description"]
|
||||
else:
|
||||
raise NotImplementedError("Implement me")
|
||||
else:
|
||||
description_prefix = ""
|
||||
|
||||
controls_recommender.recommend(
|
||||
heating_description="Electric storage heaters, radiators", description_prefix=description_prefix
|
||||
)
|
||||
|
||||
has_hhr = self.is_hhr_already_installed()
|
||||
# Conditions for not recommending electric storage heaters
|
||||
|
|
@ -580,7 +694,13 @@ class HeatingRecommender:
|
|||
# No recommendation needed
|
||||
return
|
||||
|
||||
new_heating_description = "Electric storage heaters, radiators"
|
||||
# We check if the property has dual heating in place with a boiler and storage heaters
|
||||
if self.dual_heating:
|
||||
new_heating_description = self.DUAL_HEATING_DESCRIPTIONS[
|
||||
self.property.main_heating["clean_description"]
|
||||
]["hhr"]["mainheating_description"]
|
||||
else:
|
||||
new_heating_description = "Electric storage heaters, radiators"
|
||||
|
||||
# Set up artefacts, suitable for the simulation and regardless of controls
|
||||
heating_ending_config = MainHeatAttributes(new_heating_description).process()
|
||||
|
|
@ -588,7 +708,10 @@ class HeatingRecommender:
|
|||
new_config=heating_ending_config, old_config=self.property.main_heating
|
||||
)
|
||||
# This upgrade will only take the heating system to average energy efficiency
|
||||
heating_simulation_config["mainheat_energy_eff_ending"] = "Average"
|
||||
if self.property.data["mainheat-energy-eff"] in ["Very Poor", "Poor"]:
|
||||
heating_simulation_config["mainheat_energy_eff_ending"] = "Average"
|
||||
else:
|
||||
heating_simulation_config["mainheat_energy_eff_ending"] = self.property.data["mainheat-energy-eff"]
|
||||
|
||||
# If the property is off-gas and has no heating system in place, the number of heated rooms will actually
|
||||
# be 0, so we use the number of rooms as the figure
|
||||
|
|
@ -603,7 +726,13 @@ class HeatingRecommender:
|
|||
costs = self.costs.high_heat_electric_storage_heaters(
|
||||
number_heated_rooms=number_heated_rooms
|
||||
)
|
||||
description = "Install high heat retention electric storage heaters."
|
||||
if self.dual_heating:
|
||||
description = self.DUAL_HEATING_DESCRIPTIONS[
|
||||
self.property.main_heating["clean_description"]
|
||||
]["hhr"]["recommendation_description"]
|
||||
|
||||
else:
|
||||
description = "Install high heat retention electric storage heaters."
|
||||
|
||||
# We check the existing heating system and controls
|
||||
if (
|
||||
|
|
@ -627,7 +756,8 @@ class HeatingRecommender:
|
|||
description=description,
|
||||
phase=phase,
|
||||
heating_controls_only=heating_controls_only,
|
||||
system_change=system_change
|
||||
system_change=system_change,
|
||||
system_type="high_heat_retention_storage_heater"
|
||||
)
|
||||
if _return:
|
||||
return recommendations
|
||||
|
|
@ -721,11 +851,26 @@ class HeatingRecommender:
|
|||
num_heated_rooms=self.property.data["number-heated-rooms"],
|
||||
)
|
||||
|
||||
description = "Upgrade to a new condensing boiler"
|
||||
if self.dual_heating:
|
||||
description = self.DUAL_HEATING_DESCRIPTIONS[
|
||||
self.property.main_heating["clean_description"]
|
||||
]["boiler"]["recommendation_description"]
|
||||
else:
|
||||
description = "Upgrade to a new condensing boiler"
|
||||
|
||||
new_heating_eff = (
|
||||
"Good" if self.property.data["mainheat-energy-eff"] in ["Very Poor", "Poor", "Average"]
|
||||
else self.property.data["mainheat-energy-eff"]
|
||||
)
|
||||
|
||||
new_hotwater_eff = (
|
||||
"Good" if self.property.data["hot-water-energy-eff"] in ["Very Poor", "Poor", "Average"]
|
||||
else self.property.data["hot-water-energy-eff"]
|
||||
)
|
||||
|
||||
simulation_config = {
|
||||
"mainheat_energy_eff_ending": "Good",
|
||||
"hot_water_energy_eff_ending": "Good"
|
||||
"mainheat_energy_eff_ending": new_heating_eff,
|
||||
"hot_water_energy_eff_ending": new_hotwater_eff
|
||||
}
|
||||
|
||||
description_simulation = {
|
||||
|
|
@ -736,7 +881,13 @@ class HeatingRecommender:
|
|||
if system_change:
|
||||
# Installation of a boiler improves the hot water system so we need to reflect this in
|
||||
# the outcome of the recommendation
|
||||
new_heating_description = "Boiler and radiators, mains gas"
|
||||
if self.dual_heating:
|
||||
new_heating_description = self.DUAL_HEATING_DESCRIPTIONS[
|
||||
self.property.main_heating["clean_description"]
|
||||
]["boiler"]["mainheating_description"]
|
||||
else:
|
||||
new_heating_description = "Boiler and radiators, mains gas"
|
||||
|
||||
new_hotwater_description = "From main system"
|
||||
new_fuel_description = "mains gas (not community)"
|
||||
|
||||
|
|
@ -794,13 +945,23 @@ class HeatingRecommender:
|
|||
"already_installed": already_installed,
|
||||
"simulation_config": simulation_config,
|
||||
"description_simulation": description_simulation,
|
||||
**boiler_costs
|
||||
**boiler_costs,
|
||||
"system_type": "boiler_upgrade",
|
||||
}
|
||||
|
||||
# We recommend the heating controls
|
||||
# If the property did not previously have a boiler, we combine
|
||||
controls_recommender = HeatingControlRecommender(self.property)
|
||||
controls_recommender.recommend(heating_description="Boiler and radiators, mains gas")
|
||||
if self.dual_heating:
|
||||
description_suffix = self.DUAL_HEATING_DESCRIPTIONS[
|
||||
self.property.main_heating["clean_description"]
|
||||
]["boiler"]["controls_suffix"]
|
||||
else:
|
||||
description_suffix = ""
|
||||
controls_recommender.recommend(
|
||||
heating_description="Boiler and radiators, mains gas",
|
||||
description_suffix=description_suffix
|
||||
)
|
||||
# We may have 2 recommendations from the heating controls
|
||||
|
||||
if not controls_recommender.recommendation and not boiler_recommendation:
|
||||
|
|
@ -822,7 +983,8 @@ class HeatingRecommender:
|
|||
description=boiler_recommendation["description"],
|
||||
phase=recommendation_phase,
|
||||
heating_controls_only=False,
|
||||
system_change=True
|
||||
system_change=True,
|
||||
system_type="boiler_upgrade"
|
||||
)
|
||||
combined_recommendations.extend(combined_recommendation)
|
||||
|
||||
|
|
|
|||
|
|
@ -800,3 +800,44 @@ def override_costs(costs):
|
|||
costs[k] = 0
|
||||
|
||||
return costs
|
||||
|
||||
|
||||
def combine_recommendation_configs(recommendation_config1, recommendation_config2):
|
||||
"""
|
||||
Given two simulation configs, this function will combine them into one
|
||||
:param recommendation_config1:
|
||||
:param recommendation_config2:
|
||||
:return:
|
||||
"""
|
||||
# Efficiency values - keys which contain _energy_eff_ending
|
||||
eff_1 = {
|
||||
k: v for k, v in recommendation_config1.items() if ("_energy_eff_ending" in k) or ("-energy-eff" in k)
|
||||
}
|
||||
eff_2 = {
|
||||
k: v for k, v in recommendation_config2.items() if ("_energy_eff_ending" in k) or ("-energy-eff" in k)
|
||||
}
|
||||
|
||||
# We combine the simulation configs
|
||||
combined = {
|
||||
**recommendation_config1,
|
||||
**recommendation_config2
|
||||
}
|
||||
|
||||
# Find overlapping keys
|
||||
overlapping_keys = set(eff_1.keys()).intersection(set(eff_2.keys()))
|
||||
if overlapping_keys:
|
||||
# We make sure we take the best value - map efficiency values to numbers
|
||||
numerical_embedding = {
|
||||
"Very poor": 1,
|
||||
"Poor": 2,
|
||||
"Average": 3,
|
||||
"Good": 4,
|
||||
"Very good": 5,
|
||||
}
|
||||
for key in overlapping_keys:
|
||||
if numerical_embedding[eff_1[key]] >= numerical_embedding[eff_2[key]]:
|
||||
combined[key] = eff_1[key]
|
||||
else:
|
||||
combined[key] = eff_2[key]
|
||||
|
||||
return combined
|
||||
|
|
|
|||
|
|
@ -1066,6 +1066,99 @@ testing_examples = [
|
|||
"heating_recommendation_descriptions": [],
|
||||
"heating_controls_recommendation_descriptions": [],
|
||||
"notes": "This property already has an ashp. We don't recommend any heating upgrades"
|
||||
},
|
||||
{
|
||||
"epc": {
|
||||
'lmk-key': '1dd9aa80d6e5bae3e0e4892d9ed1a83b53f3af848568f4a928c9f7a63d8825ea',
|
||||
'address1': '49 Ridgeway Road', 'address2': 'Wordsley', 'address3': None, 'postcode': 'DY8 5UD',
|
||||
'building-reference-number': 10003464876, 'current-energy-rating': 'F', 'potential-energy-rating': 'D',
|
||||
'current-energy-efficiency': 35, 'potential-energy-efficiency': 64, 'property-type': 'House',
|
||||
'built-form': 'Semi-Detached', 'inspection-date': '2021-11-17', 'local-authority': 'E08000027',
|
||||
'constituency': 'E14000672', 'county': None, 'lodgement-date': '2022-10-10', 'transaction-type': 'rental',
|
||||
'environment-impact-current': 41, 'environment-impact-potential': 67, 'energy-consumption-current': 401,
|
||||
'energy-consumption-potential': 207, 'co2-emissions-current': 6.1, 'co2-emiss-curr-per-floor-area': 69,
|
||||
'co2-emissions-potential': 3.2, 'lighting-cost-current': 61, 'lighting-cost-potential': 61,
|
||||
'heating-cost-current': 1488, 'heating-cost-potential': 1015, 'hot-water-cost-current': 114,
|
||||
'hot-water-cost-potential': 77, 'total-floor-area': 89.0, 'energy-tariff': 'Single', 'mains-gas-flag': 'Y',
|
||||
'floor-level': None, 'flat-top-storey': None, 'flat-storey-count': None, 'main-heating-controls': None,
|
||||
'multi-glaze-proportion': 100.0, 'glazed-type': 'double glazing installed during or after 2002',
|
||||
'glazed-area': 'Normal', 'extension-count': 2, 'number-habitable-rooms': 5, 'number-heated-rooms': 5,
|
||||
'low-energy-lighting': 91, 'number-open-fireplaces': 0, 'hotwater-description': 'From main system',
|
||||
'hot-water-energy-eff': 'Good', 'hot-water-env-eff': 'Good',
|
||||
'floor-description': 'Solid, no insulation (assumed)', 'floor-energy-eff': None,
|
||||
'windows-description': 'Fully double glazed', 'windows-energy-eff': 'Good', 'windows-env-eff': 'Good',
|
||||
'walls-description': 'Cavity wall, as built, no insulation (assumed)', 'walls-energy-eff': 'Poor',
|
||||
'walls-env-eff': 'Poor', 'secondheat-description': 'Room heaters, electric',
|
||||
'roof-description': 'Pitched, 200 mm loft insulation', 'roof-energy-eff': 'Good', 'roof-env-eff': 'Good',
|
||||
'mainheat-description': 'Boiler and radiators, mains gas, Electric storage heaters',
|
||||
'mainheat-energy-eff': 'Good', 'mainheat-env-eff': 'Good',
|
||||
'mainheatcont-description': 'Programmer, room thermostat and TRVs', 'mainheatc-energy-eff': 'Good',
|
||||
'mainheatc-env-eff': 'Good', 'lighting-description': 'Low energy lighting in 91% of fixed outlets',
|
||||
'lighting-energy-eff': 'Very Good', 'lighting-env-eff': 'Very Good',
|
||||
'main-fuel': 'mains gas (not community)', 'wind-turbine-count': 0, 'heat-loss-corridor': None,
|
||||
'unheated-corridor-length': None, 'floor-height': 2.53, 'photo-supply': 0.0,
|
||||
'solar-water-heating-flag': 'N',
|
||||
'mechanical-ventilation': 'natural', 'address': '49 Ridgeway Road, Wordsley',
|
||||
'local-authority-label': 'Dudley', 'constituency-label': 'Dudley South', 'posttown': 'Stourbridge',
|
||||
'construction-age-band': 'England and Wales: 1950-1966', 'lodgement-datetime': '2022-10-10 16:41:36',
|
||||
'tenure': 'Rented (social)', 'fixed-lighting-outlets-count': 11.0, 'low-energy-fixed-light-count': None,
|
||||
'uprn': 90041166, 'uprn-source': 'Energy Assessor', 'sheating-energy-eff': None, 'sheating-env-eff': None
|
||||
},
|
||||
"heating_recommendation_descriptions": [
|
||||
'Install an air source heat pump, and upgrade heating controls to Smart Thermostats, room sensors and '
|
||||
'smart radiator valves (time & temperature zone control). The cost includes the £7500 boiler upgrade '
|
||||
'scheme grant',
|
||||
'Install high heat retention electric storage heaters alongside the boiler. The current electric heaters '
|
||||
'may be retrofit with high heat retention storage controls however this is dependent on the existing '
|
||||
'system and may not be possible. Upgrade heating controls to High Heat Retention Storage Heater Controls'
|
||||
],
|
||||
"heating_controls_recommendation_descriptions": [],
|
||||
"notes": "This property has dual heating. A boiler and electric storage heaters. The heating is efficient so"
|
||||
"we recommend ASHP and HHR"
|
||||
},
|
||||
{
|
||||
"epc": {
|
||||
'lmk-key': '1dd9aa80d6e5bae3e0e4892d9ed1a83b53f3af848568f4a928c9f7a63d8825ea',
|
||||
'address1': '49 Ridgeway Road', 'address2': 'Wordsley', 'address3': None, 'postcode': 'DY8 5UD',
|
||||
'building-reference-number': 10003464876, 'current-energy-rating': 'F', 'potential-energy-rating': 'D',
|
||||
'current-energy-efficiency': 35, 'potential-energy-efficiency': 64, 'property-type': 'House',
|
||||
'built-form': 'Semi-Detached', 'inspection-date': '2021-11-17', 'local-authority': 'E08000027',
|
||||
'constituency': 'E14000672', 'county': None, 'lodgement-date': '2022-10-10', 'transaction-type': 'rental',
|
||||
'environment-impact-current': 41, 'environment-impact-potential': 67, 'energy-consumption-current': 401,
|
||||
'energy-consumption-potential': 207, 'co2-emissions-current': 6.1, 'co2-emiss-curr-per-floor-area': 69,
|
||||
'co2-emissions-potential': 3.2, 'lighting-cost-current': 61, 'lighting-cost-potential': 61,
|
||||
'heating-cost-current': 1488, 'heating-cost-potential': 1015, 'hot-water-cost-current': 114,
|
||||
'hot-water-cost-potential': 77, 'total-floor-area': 89.0, 'energy-tariff': 'Single', 'mains-gas-flag': 'Y',
|
||||
'floor-level': None, 'flat-top-storey': None, 'flat-storey-count': None, 'main-heating-controls': None,
|
||||
'multi-glaze-proportion': 100.0, 'glazed-type': 'double glazing installed during or after 2002',
|
||||
'glazed-area': 'Normal', 'extension-count': 2, 'number-habitable-rooms': 5, 'number-heated-rooms': 5,
|
||||
'low-energy-lighting': 91, 'number-open-fireplaces': 0, 'hotwater-description': 'From main system',
|
||||
'hot-water-energy-eff': 'Good', 'hot-water-env-eff': 'Good',
|
||||
'floor-description': 'Solid, no insulation (assumed)', 'floor-energy-eff': None,
|
||||
'windows-description': 'Fully double glazed', 'windows-energy-eff': 'Good', 'windows-env-eff': 'Good',
|
||||
'walls-description': 'Cavity wall, as built, no insulation (assumed)', 'walls-energy-eff': 'Poor',
|
||||
'walls-env-eff': 'Poor', 'secondheat-description': 'Room heaters, electric',
|
||||
'roof-description': 'Pitched, 200 mm loft insulation', 'roof-energy-eff': 'Good', 'roof-env-eff': 'Good',
|
||||
'mainheat-description': 'Boiler and radiators, mains gas, Electric storage heaters',
|
||||
'mainheat-energy-eff': 'Average', 'mainheat-env-eff': 'Good',
|
||||
'mainheatcont-description': 'Programmer, room thermostat and TRVs', 'mainheatc-energy-eff': 'Good',
|
||||
'mainheatc-env-eff': 'Good', 'lighting-description': 'Low energy lighting in 91% of fixed outlets',
|
||||
'lighting-energy-eff': 'Very Good', 'lighting-env-eff': 'Very Good',
|
||||
'main-fuel': 'mains gas (not community)', 'wind-turbine-count': 0, 'heat-loss-corridor': None,
|
||||
'unheated-corridor-length': None, 'floor-height': 2.53, 'photo-supply': 0.0,
|
||||
'solar-water-heating-flag': 'N',
|
||||
'mechanical-ventilation': 'natural', 'address': '49 Ridgeway Road, Wordsley',
|
||||
'local-authority-label': 'Dudley', 'constituency-label': 'Dudley South', 'posttown': 'Stourbridge',
|
||||
'construction-age-band': 'England and Wales: 1950-1966', 'lodgement-datetime': '2022-10-10 16:41:36',
|
||||
'tenure': 'Rented (social)', 'fixed-lighting-outlets-count': 11.0, 'low-energy-fixed-light-count': None,
|
||||
'uprn': 90041166, 'uprn-source': 'Energy Assessor', 'sheating-energy-eff': None, 'sheating-env-eff': None
|
||||
},
|
||||
"heating_recommendation_descriptions": [
|
||||
],
|
||||
"heating_controls_recommendation_descriptions": [],
|
||||
"notes": "This property is a modified version of the previous dual heating property, where we lower the"
|
||||
"starting heating efficiency so that we a combined heating upgrade to both the boiler and the electric"
|
||||
"storage heaters"
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -1125,6 +1218,8 @@ completed_descriptions = [
|
|||
"Boiler and radiators, smokeless fuel",
|
||||
"Boiler and radiators, wood pellets",
|
||||
"Room heaters, dual fuel (mineral and wood)",
|
||||
"Air source heat pump, radiators, electric",
|
||||
"Portable electric heaters assumed for most rooms, Room heaters, electric",
|
||||
]
|
||||
|
||||
portfolio = pd.read_excel(
|
||||
|
|
@ -1139,7 +1234,7 @@ portfolio["lodgement-datetime"] = portfolio["lodgement-datetime"].astype(str)
|
|||
print(portfolio["mainheat-description"].value_counts())
|
||||
|
||||
eg = portfolio[
|
||||
(portfolio["mainheat-description"] == "Air source heat pump, radiators, electric")
|
||||
(portfolio["mainheat-description"] == "Boiler and radiators, mains gas, Electric storage heaters")
|
||||
].sample(1)
|
||||
eg = eg.squeeze().to_dict()
|
||||
print(eg)
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ class TestHeatingRecommendations:
|
|||
we retrieve alongside them
|
||||
:return:
|
||||
"""
|
||||
if test_case["epc"]["uprn"] == 90041166:
|
||||
raise Exception("Finish the second test case with this uprn")
|
||||
|
||||
epc_records = {"original_epc": test_case["epc"].copy(), "full_sap_epc": {}, "old_data": []}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue