Added in second part of recommendation impact calculations

This commit is contained in:
Khalim Conn-Kowlessar 2024-07-09 19:27:21 +01:00
parent af66744979
commit 3bdc4db92a
4 changed files with 226 additions and 35 deletions

View file

@ -380,6 +380,7 @@ async def trigger_plan(body: PlanTriggerRequest):
target_rating=body.goal_value,
current_consumption=p.current_adjusted_energy
),
"property_id": p.id
} for p in input_properties if p.building_id is not None
]
if building_ids:
@ -419,7 +420,13 @@ async def trigger_plan(body: PlanTriggerRequest):
# Insert this into the properties that have this building id
for p in input_properties:
if p.building_id == building_id:
p.set_solar_panel_configuration(solar_panel_configuration[building_id])
unit_solar_panel_configuration = solar_panel_configuration[building_id].copy()
unit_solar_panel_configuration["unit_share_of_energy"] = (
[x for x in building_ids if x["property_id"] == p.id][0]["energy_consumption"] /
energy_consumption
)
p.set_solar_panel_configuration(unit_solar_panel_configuration)
else:
# Model the solar potential at the property level

View file

@ -28,8 +28,10 @@ class AnnualBillSavings:
# https://www.ofgem.gov.uk/energy-price-cap
ELECTRICITY_PRICE_CAP = 0.2236
GAS_PRICE_CAP = 0.0548
# This is the most recent export payment figure, at 12p per kwh
ELECTRICITY_EXPORT_PAYMENT = 0.12
# This is the most recent export payment figure, at 9.28p/kWh
# Smart export guarantee rates can be found here:
# https://www.sunsave.energy/solar-panels-advice/exporting-to-the-grid/best-seg-rates
ELECTRICITY_EXPORT_PAYMENT = 0.0928
# This is a weighted mean of the price caps, using the consumption figures above as weights
PRICE_FACTOR = 0.09549999999999999

View file

@ -14,6 +14,7 @@ from recommendations.HeatingRecommender import HeatingRecommender
from recommendations.HotwaterRecommendations import HotwaterRecommendations
from recommendations.SecondaryHeating import SecondaryHeating
from backend.ml_models.AnnualBillSavings import AnnualBillSavings
from backend.apis.GoogleSolarApi import GoogleSolarApi
class Recommendations:
@ -374,6 +375,8 @@ class Recommendations:
# # TODO: We should determine if the home is gas & electricity or just electricity
# expected_energy_bill = AnnualBillSavings.calculate_annual_bill(expected_adjusted_energy)
phase_lighting_costs = {}
phase_kwh_figures = {}
for recommendations_by_type in property_recommendations:
for rec in recommendations_by_type:
@ -422,23 +425,52 @@ class Recommendations:
float(property_instance.data["energy-consumption-current"]) - new_heat_demand
)
if rec["type"] == "lighting":
new_heating_cost = property_instance.energy_cost_estimates["adjusted"]["heating"]
new_hot_water_cost = property_instance.energy_cost_estimates["adjusted"]["hot_water"]
new_lighting_cost = min(
new_lighting_cost, property_instance.energy_cost_estimates["adjusted"]["lighting"]
)
scoring_heating_cost = property_instance.energy_cost_estimates["unadjusted"]["heating"]
scoring_hot_water_cost = property_instance.energy_cost_estimates["unadjusted"]["hot_water"]
scoring_lighting_cost = min(
property_instance.energy_cost_estimates["unadjusted"]["lighting"],
new_lighting_cost_unadjusted
)
else:
new_heating_cost = min(
new_heating_cost, property_instance.energy_cost_estimates["adjusted"]["heating"]
)
new_hot_water_cost = min(
new_hot_water_cost, property_instance.energy_cost_estimates["adjusted"]["hot_water"]
)
new_lighting_cost = property_instance.energy_cost_estimates["adjusted"]["lighting"]
scoring_heating_cost = min(
property_instance.energy_cost_estimates["unadjusted"]["heating"],
new_heating_cost_unadjusted
)
scoring_hot_water_cost = min(
property_instance.energy_cost_estimates["unadjusted"]["hot_water"],
new_hot_water_cost_unadjusted
)
scoring_lighting_cost = property_instance.energy_cost_estimates["unadjusted"]["lighting"]
predicted_heating_cost_reduction = (
float(property_instance.energy_cost_estimates["adjusted"]["heating"]) - new_heating_cost
property_instance.energy_cost_estimates["adjusted"]["heating"] - new_heating_cost
)
predicted_hot_water_cost_reduction = (
float(property_instance.energy_cost_estimates["adjusted"]["hot_water"]) - new_hot_water_cost
)
predicted_heating_cost_reduction = (
0 if predicted_heating_cost_reduction < 0 else predicted_heating_cost_reduction
)
predicted_hot_water_cost_reduction = (
0 if predicted_hot_water_cost_reduction < 0 else predicted_hot_water_cost_reduction
property_instance.energy_cost_estimates["adjusted"]["hot_water"] - new_hot_water_cost
)
# Only lighting recommendations can have an impact here
predicted_lighting_cost_reduction = 0 if rec["type"] != "lighting" else (
float(property_instance.energy_cost_estimates["adjusted"]["lighting"]) - new_lighting_cost
property_instance.energy_cost_estimates["adjusted"]["lighting"] - new_lighting_cost
)
# We store this value for later
phase_lighting_costs[rec["phase"]] = {
"adjusted": new_lighting_cost,
"unadjusted": scoring_lighting_cost
}
# This is the total bill savings for the recommendation
if rec["type"] == "solar_pv":
@ -456,17 +488,6 @@ class Recommendations:
)
# We now predict the kwh savings using the xgb model
scoring_heating_cost = min(
property_instance.energy_cost_estimates["unadjusted"]["heating"], new_heating_cost_unadjusted
)
scoring_hot_water_cost = min(
property_instance.energy_cost_estimates["unadjusted"]["hot_water"],
new_hot_water_cost_unadjusted
)
scoring_lighting_cost = min(
property_instance.energy_cost_estimates["unadjusted"]["lighting"], new_lighting_cost_unadjusted
) if rec["type"] == "lighting" \
else property_instance.energy_cost_estimates["unadjusted"]["lighting"]
simulation_epc = property_instance.simulation_epcs[rec["phase"]].copy()
# The current heating, hot water and energy kwh should be based on the new, unadjusted
@ -513,6 +534,17 @@ class Recommendations:
kwh_reduction = heating_kwh_reduction + hot_water_kwh_reduction + lighting_kwh_reduction
phase_kwh_figures[rec["phase"]] = {
"adjusted": {
"heating": new_heating_kwh_adjusted,
"hot_water": new_hot_water_kwh_adjusted
},
"unadjusted": {
"heating": new_heating_kwh,
"hot_water": new_hot_water_kwh
}
}
else:
previous_phase = rec["phase"] - 1
predicted_sap_points = (
@ -527,30 +559,177 @@ class Recommendations:
new_heat_demand
)
if rec["type"] == "lighting":
# If we have a lighting recommendation, the heating, hot water and lighting costs will
# be from the previous phase - nothing will change
new_heating_cost = heating_cost_phase_impact[
heating_cost_phase_impact["phase"] == previous_phase
]["adjusted_cost"].values[0]
new_hot_water_cost = hot_water_cost_phase_impact[
hot_water_cost_phase_impact["phase"] == previous_phase
]["adjusted_cost"].values[0]
new_lighting_cost = min(
new_lighting_cost, phase_lighting_costs[previous_phase]["adjusted"]
)
# We also use the unadjusted costs for the scoring from the previous phase
scoring_heating_cost = heating_cost_phase_impact[
heating_cost_phase_impact["phase"] == previous_phase
]["predictions"].values[0]
scoring_hot_water_cost = hot_water_cost_phase_impact[
hot_water_cost_phase_impact["phase"] == previous_phase
]["predictions"].values[0]
scoring_lighting_cost = min(
new_lighting_cost_unadjusted,
phase_lighting_costs[previous_phase]["unadjusted"]
)
else:
# Whereas for other recommendations, we use the new costs
new_heating_cost = min(
new_heating_cost,
heating_cost_phase_impact[
heating_cost_phase_impact["phase"] == previous_phase
]["adjusted_cost"].values[0]
)
new_hot_water_cost = min(
new_hot_water_cost,
hot_water_cost_phase_impact[
hot_water_cost_phase_impact["phase"] == previous_phase
]["adjusted_cost"].values[0]
)
new_lighting_cost = phase_lighting_costs[previous_phase]["adjusted"]
scoring_heating_cost = min(
new_heating_cost_unadjusted,
heating_cost_phase_impact[
heating_cost_phase_impact["phase"] == previous_phase
]["predictions"].values[0]
)
scoring_hot_water_cost = min(
new_hot_water_cost_unadjusted,
hot_water_cost_phase_impact[
hot_water_cost_phase_impact["phase"] == previous_phase
]["predictions"].values[0]
)
scoring_lighting_cost = phase_lighting_costs[previous_phase]["unadjusted"]
# We now estimate the adjusted cost savings for the recommendation
predicted_heating_cost_reduction = (
heating_cost_phase_impact[heating_cost_phase_impact["phase"] == previous_phase][
"adjusted_cost"
].values[0] - new_heating_cost
)
predicted_heating_cost_reduction = (
0 if predicted_heating_cost_reduction < 0 else predicted_heating_cost_reduction
)
predicted_hot_water_cost_reduction = (
hot_water_cost_phase_impact[hot_water_cost_phase_impact["phase"] == previous_phase][
"adjusted_cost"
].values[0] - new_hot_water_cost
)
predicted_hot_water_cost_reduction = (
0 if predicted_hot_water_cost_reduction < 0 else predicted_hot_water_cost_reduction
)
# Only lighting recommendations can have an impact here
predicted_lighting_cost_reduction = 0 if rec["type"] != "lighting" else (
lighting_cost_phase_impact[lighting_cost_phase_impact["phase"] == previous_phase][
"adjusted_cost"
].values[0] - new_lighting_cost
predicted_lighting_cost_reduction = (
phase_lighting_costs[previous_phase]["adjusted"] - new_lighting_cost
)
# We now predict the kwh savings using the xgb model - this is based on
# the new costs at this phase
simulation_epc = property_instance.simulation_epcs[rec["phase"]].copy()
# The current heating, hot water and energy kwh should be based on the new, unadjusted
# costs for lighting, heating, hot water
simulation_epc["heating-cost-current"] = int(scoring_heating_cost)
simulation_epc["hot-water-cost-current"] = int(scoring_hot_water_cost)
simulation_epc["lighting-cost-current"] = int(scoring_lighting_cost)
# We predict with the energy consumption model
scoring_df = pd.DataFrame([simulation_epc])
# Change columns from underscores to hyphens
scoring_df.columns = [
x.lower().replace("_", "-") for x in scoring_df.columns
]
for col in ["heating_kwh", "hot_water_kwh"]:
scoring_df[col] = None
energy_consumption_client.data = None
new_heating_kwh = energy_consumption_client.score_new_data(
new_data=scoring_df, target="heating_kwh"
)[0]
new_hot_water_kwh = energy_consumption_client.score_new_data(
new_data=scoring_df, target="hot_water_kwh"
)[0]
# Adjust these figures
new_heating_kwh_adjusted = AnnualBillSavings.adjust_energy_to_metered(
new_heating_kwh, current_epc_rating=property_instance.data["current-energy-rating"]
)
new_hot_water_kwh_adjusted = AnnualBillSavings.adjust_energy_to_metered(
new_hot_water_kwh, current_epc_rating=property_instance.data["current-energy-rating"]
)
heating_kwh_reduction = 0 if predicted_heating_cost_reduction == 0 else (
phase_kwh_figures[previous_phase]["adjusted"]["heating"] - new_heating_kwh_adjusted
)
hot_water_kwh_reduction = 0 if predicted_hot_water_cost_reduction == 0 else (
phase_kwh_figures[previous_phase]["adjusted"]["hot_water"] - new_hot_water_kwh_adjusted
)
lighting_kwh_reduction = predicted_lighting_cost_reduction / AnnualBillSavings.ELECTRICITY_PRICE_CAP
# This is the total bill savings for the recommendation
predicted_appliances_cost_reduction = 0
predicted_appliances_kwh_reduction = 0
if rec["type"] == "solar_pv":
# Calulate the amount of energy the solar panel array will generate for this unit
unit_energy_consumption = (
rec["initial_ac_kwh_per_year"] *
property_instance.solar_panel_configuration["unit_share_of_energy"]
)
unit_energy_utilised = unit_energy_consumption * GoogleSolarApi.SOLAR_CONSUMPTION_PROPORTION
unit_energy_exported = unit_energy_consumption - unit_energy_utilised
unit_energy_exported_value = unit_energy_exported * AnnualBillSavings.ELECTRICITY_EXPORT_PAYMENT
# We assume that 50% of the energy generated will be used by the property without a battery
# to be conservative
# of the energy utilised, some of it is used by heating, hot water and lighting so we
# remove that from the total
unit_energy_utilised -= (
heating_kwh_reduction + hot_water_kwh_reduction + lighting_kwh_reduction
)
unit_energy_utilised = 0 if unit_energy_utilised < 0 else unit_energy_utilised
# This is how much energy the appliances will use after install
post_install_appliance_kwh = (
property_instance.energy_consumption_estimates["adjusted"]["appliances"] -
unit_energy_utilised
)
post_install_appliance_kwh = (
0 if post_install_appliance_kwh < 0 else post_install_appliance_kwh
)
predicted_appliances_kwh_reduction = (
property_instance.energy_consumption_estimates["adjusted"]["appliances"] -
post_install_appliance_kwh
)
predicted_appliances_cost_reduction = unit_energy_exported_value + (
predicted_appliances_kwh_reduction * AnnualBillSavings.ELECTRICITY_PRICE_CAP
)
# We now calculate the predicted_bill_savings
predicted_bill_savings = (
predicted_heating_cost_reduction + predicted_hot_water_cost_reduction +
predicted_lighting_cost_reduction + predicted_appliances_cost_reduction
)
kwh_reduction = (
heating_kwh_reduction +
hot_water_kwh_reduction +
lighting_kwh_reduction +
predicted_appliances_kwh_reduction
)
if rec["type"] == "low_energy_lighting":

View file

@ -113,13 +113,15 @@ class SolarPvRecommendations:
for rank, recommendation_config in best_configurations.iterrows():
roof_coverage_percent = round(recommendation_config["panneled_roof_area"] / total_roof_area * 100)
# Spread the cost to the individual units
# Spread the cost to the individual units - adding a 20% contingency
total_cost = recommendation_config["total_cost"] / n_units
kw = np.floor(recommendation_config["array_warrage"] / 100) / 10
description = (f"Install a {kw} kilowatt-peak (kWp) solar photovoltaic (PV) panel system on the roof "
"of the building")
initial_ac_kwh_per_year = recommendation_config["initial_ac_kwh_per_year"]
self.recommendation.append(
{
"phase": phase,
@ -135,6 +137,7 @@ class SolarPvRecommendations:
# back up here
"photo_supply": roof_coverage_percent,
"has_battery": False,
"initial_ac_kwh_per_year": initial_ac_kwh_per_year,
"description_simulation": {"photo-supply": roof_coverage_percent},
"rank": rank # Rank is used to get the representative recommendation - rank 0 will be chosen
}