implemented recommendation level kwh and cost savings

This commit is contained in:
Khalim Conn-Kowlessar 2024-08-09 18:21:23 +01:00
parent a0eabd5f09
commit 2efb2a4f3e
2 changed files with 160 additions and 22 deletions

View file

@ -183,6 +183,7 @@ class Property:
self.recommendations_scoring_data = []
self.simulation_epcs = {}
self.updated_simulation_epcs = []
# This additional condition data should change how we pass kwargs to this. We should no longer need to pass
# kwargs to this class, but instead, we should pass the energy assessment condition data
@ -454,6 +455,9 @@ class Property:
)
updated_simulation_epcs.append(sim_epc)
# Now we havet this data inthe
self.updated_simulation_epcs = updated_simulation_epcs
return updated_simulation_epcs
@staticmethod

View file

@ -671,29 +671,14 @@ async def trigger_plan(body: PlanTriggerRequest):
# We use the impact_summary to update the simulation_epcs with the new SAP, heat demand, carbon, cost etc
# at each phase
property_scoring_epcs = property_instance.update_simulation_epcs(impact_summary)
scoring_epcs.extend(property_scoring_epcs)
property_instance.update_simulation_epcs(impact_summary)
scoring_epcs.extend(property_instance.updated_simulation_epcs)
recommendations[property_id] = recommendations_with_impact
# We call the API with the scoring epcs
scoring_epcs = pd.DataFrame(scoring_epcs)
scoring_epcs = kwh_client.transform(data=scoring_epcs, cleaned=cleaned)
# There should be no difference between index 9 and index 8, apart from photo-supply (other that sap, etc)
a = scoring_epcs[scoring_epcs.index == 6]
b = scoring_epcs[scoring_epcs.index == 11]
difference = []
for col in a.columns:
if a[col].values[0] != b[col].values[0]:
difference.append(
{
"col": col,
"without_solar": a[col].values[0],
"with_solar": b[col].values[0]
}
)
difference = pd.DataFrame(difference)
kwh_simulation_predictions = model_api.paginated_predictions(
data=scoring_epcs,
bucket=get_settings().DATA_BUCKET,
@ -715,16 +700,42 @@ async def trigger_plan(body: PlanTriggerRequest):
how="inner",
on="id",
suffixes=("_heating", "_hotwater")
).reset_index(drop=True)
# We adjust this table with the kwh estimates for low energy lighting kwh values, and solar kwh estimates
led_recommendation = pd.DataFrame([
{
"phase": r["phase"],
"recommendation_id": r["recommendation_id"],
"lighting_kwh_savings": r["kwh_savings"] * GoogleSolarApi.SOLAR_CONSUMPTION_PROPORTION,
} for recs in property_recommendations for r in recs if r["type"] == "low_energy_lighting"
], columns=["phase", "recommendation_id", "lighting_kwh_savings"])
solar_recommendations = pd.DataFrame([
{
"phase": r["phase"],
"recommendation_id": r["recommendation_id"],
"solar_kwh_savings": r["initial_ac_kwh_per_year"] * GoogleSolarApi.SOLAR_CONSUMPTION_PROPORTION,
} for recs in property_recommendations for r in recs if r["type"] == "solar_pv"
], columns=["phase", "recommendation_id", "solar_kwh_savings"])
# merge them on
kwh_impact_table = kwh_impact_table.merge(
led_recommendation, how="left", on=["phase", "recommendation_id"]
).merge(
solar_recommendations, how="left", on=["phase", "recommendation_id"]
)
property_kwh = property_instance.energy_consumption_estimates["unadjusted"]
starting_dummy_id_value = -9999
kwh_impact_table = pd.concat(
[
pd.DataFrame(
[
{
"id": None,
"id": starting_dummy_id_value,
"phase": starting_dummy_id_value,
"recommendation_id": starting_dummy_id_value,
"predictions_heating": property_kwh["heating"],
"predictions_hotwater": property_kwh["hot_water"],
}
@ -732,7 +743,19 @@ async def trigger_plan(body: PlanTriggerRequest):
),
kwh_impact_table
]
)
).sort_values(["phase", "recommendation_id"], ascending=True).reset_index(drop=True)
for i in range(0, len(kwh_impact_table)):
current_phase = kwh_impact_table.loc[i, 'phase']
previous_phase_id = (current_phase - 1) if (current_phase > 0) else -9999
previous_phase = kwh_impact_table[kwh_impact_table['phase'] == previous_phase_id]
if not previous_phase.empty:
for col in ["predictions_heating", "predictions_hotwater"]:
if kwh_impact_table.loc[i, col] > previous_phase[col].max():
kwh_impact_table.loc[i, col] = previous_phase[col].max()
from backend.ml_models.AnnualBillSavings import AnnualBillSavings
# We adjust the predictions with the UCL model
for k in ["heating", "hotwater"]:
kwh_impact_table[f"adjusted_{k}"] = kwh_impact_table[f"predictions_{k}"].apply(
@ -741,13 +764,124 @@ async def trigger_plan(body: PlanTriggerRequest):
)
)
kwh_impact_table["heating_fuel"] = property_instance.heating_energy_source
kwh_impact_table["hotwater_fuel"] = property_instance.hot_water_energy_source
ASHP_COP = 3
descriptions_to_fuel_types = {
"Air source heat pump, radiators, electric": {"fuel": "Electricity", "cop": ASHP_COP},
"Boiler and radiators, mains gas": {"fuel": 'Natural Gas', "cop": 0.9}
}
def map_descriptions_to_fuel(heating_description, hotwater_description):
mapped = descriptions_to_fuel_types[heating_description]
heating_fuel = mapped["fuel"]
if hotwater_description == "From main system":
return {
"heating_fuel_type": heating_fuel, "hotwater_fuel_type": heating_fuel,
"heating_cop": mapped["cop"], "hotwater_cop": mapped["cop"]
}
raise NotImplementedError("Implement me")
# For heating system recommendations, this could result in a fuel type change so we reflect that
fuel_mapping = pd.DataFrame([
{
"id": epc["id"],
**map_descriptions_to_fuel(epc["mainheat-description"], epc["hotwater-description"])
} for epc in property_instance.updated_simulation_epcs
])
fuel_mapping = pd.concat(
[
pd.DataFrame(
[
{
"id": starting_dummy_id_value,
**map_descriptions_to_fuel(
property_instance.data["mainheat-description"],
property_instance.data["hotwater-description"]
)
}
]
),
fuel_mapping
]
)
kwh_impact_table = kwh_impact_table.merge(
fuel_mapping, how="left", on="id"
).sort_values(["phase", "recommendation_id"], ascending=True).reset_index(drop=True)
kwh_impact_table["heating_fuel_type"] = np.where(
kwh_impact_table["id"] == starting_dummy_id_value,
property_instance.heating_energy_source,
kwh_impact_table["heating_fuel_type"]
)
kwh_impact_table["hotwater_fuel_type"] = np.where(
kwh_impact_table["id"] == starting_dummy_id_value,
property_instance.hot_water_energy_source,
kwh_impact_table["hotwater_fuel_type"]
)
def calculate_recommendation_fuel_cost(kwh, fuel, cop):
if fuel == "Electricity":
return (kwh / cop) * AnnualBillSavings.ELECTRICITY_PRICE_CAP
if fuel == "Natural Gas":
return (kwh / cop) * AnnualBillSavings.GAS_PRICE_CAP
# We now calculate the fuel cost
for k in ["heating", "hotwater"]:
kwh_impact_table[f"{k}_cost"] = kwh_impact_table.apply(
lambda x: calculate_recommendation_fuel_cost(
x[f"adjusted_{k}"], x[f"{k}_fuel_type"], x[f"{k}_cop"]
), axis=1
)
# TODO: The impact of remapping EPC is huge!
# We now deduce if any of the recommendations result in a change of fuel type
for recs in property_recommendations:
for rec in recs:
print(rec["description_simulation"])
if rec["type"] == "mechanical_ventilation":
continue
rec_impact = kwh_impact_table[kwh_impact_table["recommendation_id"] == rec["recommendation_id"]]
prevous_phase_id = (rec["phase"] - 1) if (rec["phase"] > 0) else starting_dummy_id_value
previous_phase_impact = kwh_impact_table[kwh_impact_table["phase"] == prevous_phase_id]
if rec["type"] == "solar_pv":
rec["kwh_savings"] = rec_impact["solar_kwh_savings"].values[0]
rec["energy_cost_savings"] = (
rec_impact["solar_kwh_savings"].values[0] * AnnualBillSavings.ELECTRICITY_PRICE_CAP
)
continue
heating_kwh_savings = (
previous_phase_impact["adjusted_heating"].mean() - rec_impact["adjusted_heating"].values[0]
)
heating_cost_savings = (
previous_phase_impact["heating_cost"].mean() - rec_impact["heating_cost"].values[0]
)
hotwater_kwh_savings = (
previous_phase_impact["adjusted_hotwater"].mean() - rec_impact["adjusted_hotwater"].values[0]
)
hotwater_host = (
previous_phase_impact["hotwater_cost"].mean() - rec_impact["hotwater_cost"].values[0]
)
total_kwh_savings = heating_kwh_savings + hotwater_kwh_savings
energy_cost_savings = heating_cost_savings + hotwater_host
if rec["type"] == "lighting":
# In this case, we should probably just SKIP but check when we have one!
raise Exception("Implement me 3")
rec["kwh_savings"] = total_kwh_savings
rec["energy_cost_savings"] = energy_cost_savings
# TODO: Given the default recommendations, calcualte a total kwh and cost saving for the property!!!
# Insert the predictions into the recommendations and run the optimiser
# TODO: If a recommendation has a negative impact on SAP, we should remove it - this seems to have become a