mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
reviewing model performance for vectis - complete
This commit is contained in:
parent
32d702e930
commit
fb3fef5a4a
6 changed files with 142 additions and 82 deletions
|
|
@ -730,77 +730,80 @@ async def trigger_plan(body: PlanTriggerRequest):
|
||||||
recommendations[property_id] = recommendations_with_impact
|
recommendations[property_id] = recommendations_with_impact
|
||||||
|
|
||||||
# For Debugging
|
# For Debugging
|
||||||
recommendation_impact_df = []
|
# recommendation_impact_df = []
|
||||||
for property_id in recommendations.keys():
|
# for property_id in recommendations.keys():
|
||||||
for recs_by_type in recommendations[property_id]:
|
# for recs_by_type in recommendations[property_id]:
|
||||||
for rec in recs_by_type:
|
# for rec in recs_by_type:
|
||||||
recommendation_impact_df.append(
|
# recommendation_impact_df.append(
|
||||||
{
|
# {
|
||||||
"property_id": property_id,
|
# "property_id": property_id,
|
||||||
"uprn": [p.uprn for p in input_properties if p.id == property_id][0],
|
# "uprn": [p.uprn for p in input_properties if p.id == property_id][0],
|
||||||
"address": [p.address for p in input_properties if p.id == property_id][0],
|
# "address": [p.address for p in input_properties if p.id == property_id][0],
|
||||||
"recommendation_id": rec["recommendation_id"],
|
# "recommendation_id": rec["recommendation_id"],
|
||||||
"type": rec["type"],
|
# "type": rec["type"],
|
||||||
"description": rec["description"],
|
# "description": rec["description"],
|
||||||
"sap_points": rec["sap_points"],
|
# "sap_points": rec["sap_points"],
|
||||||
"co2_equivalent_savings": rec["co2_equivalent_savings"],
|
# "co2_equivalent_savings": rec["co2_equivalent_savings"],
|
||||||
"heat_demand": rec["heat_demand"]
|
# "heat_demand": rec["heat_demand"]
|
||||||
}
|
# }
|
||||||
)
|
# )
|
||||||
recommendation_impact_df = pd.DataFrame(recommendation_impact_df)
|
# recommendation_impact_df = pd.DataFrame(recommendation_impact_df)
|
||||||
|
#
|
||||||
surveyed_uprns = [
|
# surveyed_uprns = [
|
||||||
10024087855, 121016117, 121016124,
|
# 10024087855, 121016117, 121016124,
|
||||||
10024087902, 121016121, 121016128
|
# 10024087902, 121016121, 121016128
|
||||||
]
|
|
||||||
recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["uprn"].isin(surveyed_uprns)]
|
|
||||||
# recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["type"].isin(
|
|
||||||
# ["windows_glazing", "internal_wall_insulation"])
|
|
||||||
# ]
|
# ]
|
||||||
|
# recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["uprn"].isin(surveyed_uprns)]
|
||||||
actual_impacts_df = pd.DataFrame(
|
# # recommendation_impact_df = recommendation_impact_df[recommendation_impact_df["type"].isin(
|
||||||
[
|
# # ["windows_glazing", "internal_wall_insulation"])
|
||||||
# 10024087855
|
# # ]
|
||||||
{"uprn": 10024087855, "type": "internal_wall_insulation", "actual_sap_points": 5},
|
#
|
||||||
{"uprn": 10024087855, "type": "draught_proofing", "actual_sap_points": 2},
|
# actual_impacts_df = pd.DataFrame(
|
||||||
{"uprn": 10024087855, "type": "low_energy_lighting", "actual_sap_points": 0},
|
# [
|
||||||
{"uprn": 10024087855, "type": "windows_glazing", "actual_sap_points": 4},
|
# # 10024087855
|
||||||
# 121016117
|
# {"uprn": 10024087855, "type": "internal_wall_insulation", "actual_sap_points": 5},
|
||||||
{"uprn": 121016117, "type": "internal_wall_insulation", "actual_sap_points": 6},
|
# {"uprn": 10024087855, "type": "draught_proofing", "actual_sap_points": 2},
|
||||||
{"uprn": 121016117, "type": "draught_proofing", "actual_sap_points": 1},
|
# {"uprn": 10024087855, "type": "low_energy_lighting", "actual_sap_points": 0},
|
||||||
{"uprn": 121016117, "type": "low_energy_lighting", "actual_sap_points": 1},
|
# {"uprn": 10024087855, "type": "windows_glazing", "actual_sap_points": 4},
|
||||||
{"uprn": 121016117, "type": "windows_glazing", "actual_sap_points": 4},
|
# # 121016117
|
||||||
# 121016124
|
# {"uprn": 121016117, "type": "internal_wall_insulation", "actual_sap_points": 6},
|
||||||
{"uprn": 121016124, "type": "internal_wall_insulation", "actual_sap_points": 8},
|
# {"uprn": 121016117, "type": "draught_proofing", "actual_sap_points": 1},
|
||||||
{"uprn": 121016124, "type": "low_energy_lighting", "actual_sap_points": 2},
|
# {"uprn": 121016117, "type": "low_energy_lighting", "actual_sap_points": 1},
|
||||||
{"uprn": 121016124, "type": "windows_glazing", "actual_sap_points": 5},
|
# {"uprn": 121016117, "type": "windows_glazing", "actual_sap_points": 4},
|
||||||
# 10024087902
|
# # 121016124
|
||||||
{"uprn": 10024087902, "type": "room_roof_insulation", "actual_sap_points": 16},
|
# {"uprn": 121016124, "type": "internal_wall_insulation", "actual_sap_points": 8},
|
||||||
{"uprn": 10024087902, "type": "internal_wall_insulation", "actual_sap_points": 2},
|
# {"uprn": 121016124, "type": "low_energy_lighting", "actual_sap_points": 2},
|
||||||
{"uprn": 10024087902, "type": "low_energy_lighting", "actual_sap_points": 0},
|
# {"uprn": 121016124, "type": "windows_glazing", "actual_sap_points": 5},
|
||||||
# 121016121
|
# # 10024087902
|
||||||
{"uprn": 121016121, "type": "internal_wall_insulation", "actual_sap_points": 5},
|
# {"uprn": 10024087902, "type": "room_roof_insulation", "actual_sap_points": 16},
|
||||||
{"uprn": 121016121, "type": "suspended_floor_insulation", "actual_sap_points": 2},
|
# {"uprn": 10024087902, "type": "internal_wall_insulation", "actual_sap_points": 2},
|
||||||
{"uprn": 121016121, "type": "draught_proofing", "actual_sap_points": 1},
|
# {"uprn": 10024087902, "type": "low_energy_lighting", "actual_sap_points": 0},
|
||||||
{"uprn": 121016121, "type": "windows_glazing", "actual_sap_points": 3},
|
# # 121016121
|
||||||
# 121016128
|
# {"uprn": 121016121, "type": "internal_wall_insulation", "actual_sap_points": 5},
|
||||||
{"uprn": 121016128, "type": "internal_wall_insulation", "actual_sap_points": 6},
|
# {"uprn": 121016121, "type": "suspended_floor_insulation", "actual_sap_points": 2},
|
||||||
{"uprn": 121016128, "type": "suspended_floor_insulation", "actual_sap_points": 1},
|
# {"uprn": 121016121, "type": "draught_proofing", "actual_sap_points": 1},
|
||||||
{"uprn": 121016128, "type": "draught_proofing", "actual_sap_points": 1},
|
# {"uprn": 121016121, "type": "windows_glazing", "actual_sap_points": 3},
|
||||||
{"uprn": 121016128, "type": "low_energy_lighting", "actual_sap_points": 1},
|
# # 121016128
|
||||||
{"uprn": 121016128, "type": "windows_glazing", "actual_sap_points": 3},
|
# {"uprn": 121016128, "type": "internal_wall_insulation", "actual_sap_points": 6},
|
||||||
]
|
# {"uprn": 121016128, "type": "suspended_floor_insulation", "actual_sap_points": 1},
|
||||||
)
|
# {"uprn": 121016128, "type": "draught_proofing", "actual_sap_points": 1},
|
||||||
|
# {"uprn": 121016128, "type": "low_energy_lighting", "actual_sap_points": 1},
|
||||||
comparison = recommendation_impact_df.merge(
|
# {"uprn": 121016128, "type": "windows_glazing", "actual_sap_points": 3},
|
||||||
actual_impacts_df, how="inner", on=["uprn", "type"]
|
# ]
|
||||||
)
|
# )
|
||||||
|
#
|
||||||
property_recs = recommendation_impact_df[recommendation_impact_df["uprn"] == 121016128]
|
# comparison = recommendation_impact_df.merge(
|
||||||
property = [p for p in input_properties if p.uprn == 121016128][0]
|
# actual_impacts_df, how="inner", on=["uprn", "type"]
|
||||||
print(property.data["current-energy-efficiency"])
|
# )
|
||||||
print(property_recs["sap_points"].sum())
|
#
|
||||||
property_recs["address"]
|
# print(recommendation_impact_df.groupby(["uprn"])["sap_points"].sum())
|
||||||
|
# property_recs = recommendation_impact_df[recommendation_impact_df["uprn"] == 121016128]
|
||||||
|
# property = [p for p in input_properties if p.uprn == 121016128][0]
|
||||||
|
# print(property.data["current-energy-efficiency"])
|
||||||
|
# print(property_recs["sap_points"].sum())
|
||||||
|
# print(property_recs["type"])
|
||||||
|
# print(float(property.data["current-energy-efficiency"]) + property_recs["sap_points"].sum())
|
||||||
|
# recommendations[property.id][2][0]["simulation_config"]
|
||||||
|
|
||||||
# from utils.s3 import read_dataframe_from_s3_parquet
|
# from utils.s3 import read_dataframe_from_s3_parquet
|
||||||
# training_data = read_dataframe_from_s3_parquet(
|
# training_data = read_dataframe_from_s3_parquet(
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ SPECIFIC_MEASURES = [
|
||||||
# Solar
|
# Solar
|
||||||
"solar_pv",
|
"solar_pv",
|
||||||
# Windows Glazing
|
# Windows Glazing
|
||||||
"windows",
|
"double_glazing",
|
||||||
|
"secondary_glazing",
|
||||||
# Mechanical ventilation
|
# Mechanical ventilation
|
||||||
"ventilation",
|
"ventilation",
|
||||||
# Other
|
# Other
|
||||||
|
|
@ -62,6 +63,7 @@ MEASURE_MAP = {
|
||||||
"roof_insulation": ["loft_insulation", "flat_roof_insulation", "room_roof_insulation"],
|
"roof_insulation": ["loft_insulation", "flat_roof_insulation", "room_roof_insulation"],
|
||||||
"floor_insulation": ["suspended_floor_insulation", "solid_floor_insulation"],
|
"floor_insulation": ["suspended_floor_insulation", "solid_floor_insulation"],
|
||||||
"heating": ["boiler_upgrade", "high_heat_retention_storage_heater", "air_source_heat_pump"],
|
"heating": ["boiler_upgrade", "high_heat_retention_storage_heater", "air_source_heat_pump"],
|
||||||
|
"windows": ["double_glazing", "secondary_glazing"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,9 @@ class FloorRecommendations(Definitions):
|
||||||
|
|
||||||
simulation_config = {
|
simulation_config = {
|
||||||
**floor_simulation_config,
|
**floor_simulation_config,
|
||||||
"floor_thermal_transmittance_ending": new_u_value,
|
# We don't simulate the impact using this U-value, but rather the average because this
|
||||||
|
# variable is way too volatile. Will likely be removed from the model
|
||||||
|
"floor_thermal_transmittance_ending": 0.685593,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.recommendations.append(
|
self.recommendations.append(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
from backend.Property import Property
|
from backend.Property import Property
|
||||||
from typing import List
|
from typing import List
|
||||||
from recommendations.Costs import Costs
|
from recommendations.Costs import Costs
|
||||||
|
|
@ -30,6 +32,37 @@ class LightingRecommendations:
|
||||||
self.material = material[0]
|
self.material = material[0]
|
||||||
self.recommendation = []
|
self.recommendation = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_sap_limit(cls, lighting_energy_efficiency: str, lighting_proportion: float):
|
||||||
|
"""
|
||||||
|
Lighting seems to be a more straight forward measure to estimate SAP points for, based on the starting
|
||||||
|
energy efficiency rating.
|
||||||
|
|
||||||
|
We seem to have the following brackes based on % of LEDs in outlets
|
||||||
|
Very poor: 0 - 9%
|
||||||
|
Poor: 10 - 24%
|
||||||
|
Average: 25 - 44%
|
||||||
|
Good: 45 - 69%
|
||||||
|
Very good: 70 - 100%
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if lighting_energy_efficiency == "Very Good":
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if lighting_energy_efficiency in ["Good", "Average"]:
|
||||||
|
return cls.SAP_LOWER_LIMIT
|
||||||
|
|
||||||
|
# If lighting_energy_efficiency is missing, we'll use the proportion of low energy lighting
|
||||||
|
if not lighting_energy_efficiency or pd.isnull(lighting_energy_efficiency):
|
||||||
|
if lighting_proportion >= 0.7:
|
||||||
|
return 0
|
||||||
|
if lighting_proportion >= 0.25:
|
||||||
|
return cls.SAP_LOWER_LIMIT
|
||||||
|
return cls.SAP_LIMIT
|
||||||
|
|
||||||
|
return cls.SAP_LIMIT
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def estimate_lighting_impact(number_of_bulbs: int):
|
def estimate_lighting_impact(number_of_bulbs: int):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,13 @@ class Recommendations:
|
||||||
|
|
||||||
inclusions_full = [MEASURE_MAP[x] if x in MEASURE_MAP else x for x in self.inclusions]
|
inclusions_full = [MEASURE_MAP[x] if x in MEASURE_MAP else x for x in self.inclusions]
|
||||||
exclusions_full = [MEASURE_MAP[x] if x in MEASURE_MAP else x for x in self.exclusions]
|
exclusions_full = [MEASURE_MAP[x] if x in MEASURE_MAP else x for x in self.exclusions]
|
||||||
|
# We need to unlist any lists, but we should check if they're lists first
|
||||||
|
inclusions_full = [
|
||||||
|
item for sublist in inclusions_full for item in (sublist if isinstance(sublist, list) else [sublist])
|
||||||
|
]
|
||||||
|
exclusions_full = [
|
||||||
|
item for sublist in exclusions_full for item in (sublist if isinstance(sublist, list) else [sublist])
|
||||||
|
]
|
||||||
|
|
||||||
# If inclusions and exclusions are empty, it means that nothing was specified, so we allow
|
# If inclusions and exclusions are empty, it means that nothing was specified, so we allow
|
||||||
# all recommendation types
|
# all recommendation types
|
||||||
|
|
@ -163,9 +170,9 @@ class Recommendations:
|
||||||
property_recommendations.append(self.lighting_recommender.recommendation)
|
property_recommendations.append(self.lighting_recommender.recommendation)
|
||||||
phase += 1
|
phase += 1
|
||||||
|
|
||||||
if "windows" in measures and "mixed_glazing" not in non_invasive_recommendation_types:
|
if "mixed_glazing" not in non_invasive_recommendation_types:
|
||||||
# If we have a mixed glazing recommendation, we prioritise this over the windows recommendation
|
# If we have a mixed glazing recommendation, we prioritise this over the windows recommendation
|
||||||
self.windows_recommender.recommend(phase=phase)
|
self.windows_recommender.recommend(phase=phase, measures=measures)
|
||||||
if self.windows_recommender.recommendation:
|
if self.windows_recommender.recommendation:
|
||||||
property_recommendations.append(self.windows_recommender.recommendation)
|
property_recommendations.append(self.windows_recommender.recommendation)
|
||||||
phase += 1
|
phase += 1
|
||||||
|
|
@ -538,11 +545,11 @@ class Recommendations:
|
||||||
|
|
||||||
# For the moment, we cap the number of SAP points that can be achieved by LEDs at 2
|
# For the moment, we cap the number of SAP points that can be achieved by LEDs at 2
|
||||||
if rec["type"] == "low_energy_lighting":
|
if rec["type"] == "low_energy_lighting":
|
||||||
|
lighting_sap_limit = LightingRecommendations.get_sap_limit(
|
||||||
|
property_instance.data["lighting-energy-eff"],
|
||||||
|
property_instance.lighting["low_energy_proportion"]
|
||||||
|
)
|
||||||
|
|
||||||
if property_instance.data["low-energy-lighting"] < 50:
|
|
||||||
lighting_sap_limit = LightingRecommendations.SAP_LIMIT
|
|
||||||
else:
|
|
||||||
lighting_sap_limit = LightingRecommendations.SAP_LOWER_LIMIT
|
|
||||||
property_phase_impact["sap"] = min(property_phase_impact["sap"], lighting_sap_limit)
|
property_phase_impact["sap"] = min(property_phase_impact["sap"], lighting_sap_limit)
|
||||||
property_phase_impact["carbon"] = min(
|
property_phase_impact["carbon"] = min(
|
||||||
property_phase_impact["carbon"], rec["co2_equivalent_savings"]
|
property_phase_impact["carbon"], rec["co2_equivalent_savings"]
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ from typing import List
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from backend.Property import Property
|
from backend.Property import Property
|
||||||
|
from backend.app.plan.schemas import MEASURE_MAP
|
||||||
from etl.epc_clean.epc_attributes.WindowAttributes import WindowAttributes
|
from etl.epc_clean.epc_attributes.WindowAttributes import WindowAttributes
|
||||||
from recommendations.Costs import Costs
|
from recommendations.Costs import Costs
|
||||||
from recommendations.recommendation_utils import override_costs, check_simulation_difference
|
from recommendations.recommendation_utils import override_costs, check_simulation_difference
|
||||||
|
|
@ -32,7 +33,7 @@ class WindowsRecommendations:
|
||||||
raise ValueError("There should only be one window glazing material")
|
raise ValueError("There should only be one window glazing material")
|
||||||
self.glazing_material = self.glazing_material[0]
|
self.glazing_material = self.glazing_material[0]
|
||||||
|
|
||||||
def recommend(self, phase=0):
|
def recommend(self, measures=None, phase=0):
|
||||||
"""
|
"""
|
||||||
This method will recommend the best possible glazing options for a property.
|
This method will recommend the best possible glazing options for a property.
|
||||||
|
|
||||||
|
|
@ -41,14 +42,26 @@ class WindowsRecommendations:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
measures = MEASURE_MAP["windows"] if measures is None else measures
|
||||||
|
|
||||||
|
# If we have no windows recs, leave
|
||||||
|
if not any(x in measures for x in MEASURE_MAP["windows"]):
|
||||||
|
return
|
||||||
|
|
||||||
# If the property is in a conservation area or is a listed building, it becomes more difficult to install
|
# If the property is in a conservation area or is a listed building, it becomes more difficult to install
|
||||||
# double glazing. Therefore, we don't recommend it. It is still possible but is not practical as it
|
# double glazing. Therefore, we don't recommend it. It is still possible but is not practical as it
|
||||||
# requires planning permission and might require a more expensive window type, such as timber.
|
# requires planning permission and might require a more expensive window type, such as timber.
|
||||||
|
|
||||||
number_of_windows = self.property.number_of_windows
|
number_of_windows = self.property.number_of_windows
|
||||||
is_secondary_glazing = self.property.restricted_measures or (
|
|
||||||
self.property.windows["glazing_type"] == "secondary"
|
if "double_glazing" in measures and "secondary_glazing" not in measures:
|
||||||
)
|
is_secondary_glazing = False
|
||||||
|
elif "secondary_glazing" in measures and "double_glazing" not in measures:
|
||||||
|
is_secondary_glazing = True
|
||||||
|
else:
|
||||||
|
is_secondary_glazing = self.property.restricted_measures or (
|
||||||
|
self.property.windows["glazing_type"] == "secondary"
|
||||||
|
)
|
||||||
windows_area = self.property.windows_area
|
windows_area = self.property.windows_area
|
||||||
|
|
||||||
if not number_of_windows:
|
if not number_of_windows:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue