mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Added simulation config
This commit is contained in:
parent
a5eb42794e
commit
fe25228362
5 changed files with 63 additions and 24 deletions
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue