Added simulation config

This commit is contained in:
Khalim Conn-Kowlessar 2024-02-19 16:04:46 +00:00
parent a5eb42794e
commit fe25228362
5 changed files with 63 additions and 24 deletions

View file

@ -238,12 +238,15 @@ class Property:
# Note: often when the wall is insulatied, the internal/external insulation is not noted so we should
# test the impact of using these booleans
if recommendation["type"] == "external_wall_insulation":
output["external_insulation"] = True
output["internal_insulation"] = False
output["external_insulation_ending"] = True
output["internal_insulation_ending"] = False
if recommendation["type"] == "internal_wall_insulation":
output["external_insulation"] = False
output["internal_insulation"] = True
output["external_insulation_ending"] = False
output["internal_insulation_ending"] = True
if recommendation["type"] == "cavity_wall_insulation":
output["is_filled_cavity_ending"] = True
# TODO: perhaps detrimental
# When making a recommendation for the wall, we will also update the ventilation
@ -314,7 +317,7 @@ class Property:
if recommendation["type"] == "low_energy_lighting":
output["low_energy_lighting_ending"] = 100
output["lighting_energy_eff_starting"] = "Very Good"
output["lighting_energy_eff_ending"] = "Very Good"
if recommendation["type"] == "windows_glazing":
output["multi_glaze_proportion_ending"] = 100
@ -340,6 +343,10 @@ class Property:
else:
output["glazed_type_ending"] = "double glazing installed during or after 2002 "
if recommendation["type"] == "heating_control":
# We update the data, as defined in the recommendaton
output.update(recommendation["simulation_config"])
if recommendation["type"] == "solar_pv":
output["photo_supply_ending"] = recommendation["photo_supply"]
@ -348,7 +355,7 @@ class Property:
"internal_wall_insulation", "external_wall_insulation", "cavity_wall_insulation",
"loft_insulation", "room_roof_insulation", "flat_roof_insulation",
"solid_floor_insulation", "suspended_floor_insulation", "exposed_floor_insulation",
"windows_glazing", "solar_pv"
"windows_glazing", "solar_pv", "heating_control",
]:
raise NotImplementedError("Implement me")

View file

@ -144,6 +144,9 @@ async def trigger_plan(body: PlanTriggerRequest):
recommender = Recommendations(property_instance=p, materials=materials)
property_recommendations, property_representative_recommendations = recommender.recommend()
# TODO: Re-run property_dimensions
# Do the simulation in adjust_difference_record_with_recommendations, to update the values
if not property_recommendations:
continue

View file

@ -467,8 +467,7 @@ class EPCRecord:
]
if (
self.construction_age_band is not None
and self.construction_age_band not in DATA_ANOMALY_MATCHES
self.construction_age_band not in DATA_ANOMALY_MATCHES
):
result = result[
(result["CONSTRUCTION_AGE_BAND"] == self.construction_age_band)
@ -481,7 +480,7 @@ class EPCRecord:
result = result[(result["BUILT_FORM"] == self.prepared_epc["built-form"])]
return result[
["NUMBER_HABITABLE_ROOMS", "TOTAL_FLOOR_AREA", "FLOOR_HEIGHT"]
["NUMBER_HABITABLE_ROOMS", "NUMBER_HEATED_ROOMS", "TOTAL_FLOOR_AREA", "FLOOR_HEIGHT"]
].mean()
def _clean_property_dimensions(self):
@ -490,12 +489,11 @@ class EPCRecord:
"""
if not self.prepared_epc:
raise ValueError("EPC Recrod doesn not contain epc data")
raise ValueError("EPC Record doesn not contain epc data")
if not self.prepared_epc["number-habitable-rooms"] or (
self.prepared_epc["floor-height"] == ""
or self.prepared_epc["floor-height"] in DATA_ANOMALY_MATCHES
):
if (self.prepared_epc["number-habitable-rooms"] in DATA_ANOMALY_MATCHES) or (
self.prepared_epc["floor-height"] in DATA_ANOMALY_MATCHES
) or (self.prepared_epc["number-heated-rooms"] in DATA_ANOMALY_MATCHES):
property_dimensions = read_dataframe_from_s3_parquet(
bucket_name=DATA_BUCKET,
file_key=f"property_dimensions/{self.prepared_epc['local-authority']}.parquet",
@ -504,14 +502,17 @@ class EPCRecord:
property_dimensions
)
if not self.prepared_epc["number-habitable-rooms"]:
if self.prepared_epc["number-habitable-rooms"] in DATA_ANOMALY_MATCHES:
self.prepared_epc["number-habitable-rooms"] = float(
self.property_dimensions["NUMBER_HABITABLE_ROOMS"].round()
)
else:
self.prepared_epc["number-habitable-rooms"] = float(
self.prepared_epc["number-habitable-rooms"]
)
self.prepared_epc["number-habitable-rooms"] = float(self.prepared_epc["number-habitable-rooms"])
if self.prepared_epc["number-heated-rooms"] in DATA_ANOMALY_MATCHES:
self.prepared_epc["number-heated-rooms"] = float(self.property_dimensions["NUMBER_HEATED_ROOMS"].round())
else:
self.prepared_epc["number-heated-rooms"] = float(self.prepared_epc["number-heated-rooms"])
self.number_of_floors = estimate_number_of_floors(
self.prepared_epc["property-type"]
@ -729,7 +730,7 @@ class EPCRecord:
old_record["lodgement-datetime"]
for old_record in self.old_data
if old_record["construction-age-band"]
not in DATA_ANOMALY_MATCHES
not in DATA_ANOMALY_MATCHES
]
)

View file

@ -7,7 +7,7 @@ from pathlib import Path
import pandas as pd
from tqdm import tqdm
from etl.epc.settings import EARLIEST_EPC_DATE
from etl.epc.DataProcessor import DataProcessor
from etl.epc.DataProcessor import EPCDataProcessor
from BaseUtility import Definitions
from utils.s3 import save_dataframe_to_s3_parquet
@ -28,17 +28,21 @@ def app():
data["TOTAL_FLOOR_AREA"] = data["TOTAL_FLOOR_AREA"].astype(float)
data["CONSTRUCTION_AGE_BAND"] = data["CONSTRUCTION_AGE_BAND"].apply(
lambda x: DataProcessor.clean_construction_age_band(x)
lambda x: EPCDataProcessor.clean_construction_age_band(x)
)
data = data[~pd.isnull(data["CONSTRUCTION_AGE_BAND"])]
data = data[~data["CONSTRUCTION_AGE_BAND"].isin(Definitions.DATA_ANOMALY_MATCHES)]
data = data[~pd.isnull(data["TOTAL_FLOOR_AREA"])]
data = data[~pd.isnull(data["NUMBER_HABITABLE_ROOMS"])]
data = data[~pd.isnull(data["FLOOR_HEIGHT"])]
data = data[~pd.isnull(data["NUMBER_HEATED_ROOMS"])]
df = (
data.groupby(GROUPBY)
.agg({"NUMBER_HABITABLE_ROOMS": "median", "TOTAL_FLOOR_AREA": "mean", "FLOOR_HEIGHT": "mean"})
.agg(
{"NUMBER_HEATED_ROOMS": "median", "NUMBER_HABITABLE_ROOMS": "median", "TOTAL_FLOOR_AREA": "mean",
"FLOOR_HEIGHT": "mean"}
)
.reset_index()
)

View file

@ -1,5 +1,6 @@
from recommendations.Costs import Costs
from backend.Property import Property
from etl.epc_clean.epc_attributes.MainheatControlAttributes import MainheatControlAttributes
class HeatingRecommender:
@ -12,10 +13,23 @@ class HeatingRecommender:
def recommend(self, phase=0):
# This first iteration of the recommender will provide very basic recommendation
if self.property.main_heating == "Room heaters, electric":
if self.property.main_heating["clean_description"] == "Room heaters, electric":
self.recommend_room_heaters_electric(phase=phase)
return
@staticmethod
def check_simulation_difference(old_config, new_config):
"""
Given two dictionaries, that describe the heating control configurations, this method will compare the two
and pick out the differences. These differences will be things that have been added and things that have been
removed. This will be used to determine how we should be updating the configuration in the simulation
:return:
"""
differences = {key + "_ending": new_config[key] for key in new_config if old_config[key] != new_config[key]}
return differences
def recommend_room_heaters_electric(self, phase):
"""
If the home has Room heaters, electric, we start by identifying potential heating controls that could
@ -40,6 +54,15 @@ class HeatingRecommender:
# In order to cost, we check if the property already has a programmer, and therefor we will just need to
# add the cost of the appliance thermostats
has_programmer = self.property.main_heating_controls["switch_system"] == "programmer"
ending_config = MainheatControlAttributes("Programmer and appliance thermostats").process()
# We look at what has changed in the ending config, and compare it to the current config
# We use this to determine how we should be updating the config
simulation_config = self.check_simulation_difference(
new_config=ending_config, old_config=self.property.main_heating_controls
)
self.recommendations.append(
{
"phase": phase,
@ -52,7 +75,8 @@ class HeatingRecommender:
"starting_u_value": None,
"new_u_value": None,
"sap_points": None,
**self.costs.programmer_and_appliance_thermostat(has_programmer=has_programmer)
**self.costs.programmer_and_appliance_thermostat(has_programmer=has_programmer),
"simulation_config": simulation_config
}
)