Model/backend/ml_models/AnnualBillSavings.py

93 lines
3.2 KiB
Python

class AnnualBillSavings:
"""
This is a simple class which will estimate the annual bill savings, based on the kwh savings.
This class uses data from Ofgem, including their price caps, to provide us with an estimate for
1KWH of energy.
"""
# These gas an electricity consumption figures are based off of figures presented by Ofgem
# https://www.ofgem.gov.uk/information-consumers/energy-advice-households/average-gas-and-electricity-use-explained
AVERAGE_ELECTRICITY_CONSUMPTION = 2700
AVERAGE_GAS_CONSUMPTION = 11500
# Latest price cap figures from Ofgem are for January 2024
# https://www.ofgem.gov.uk/publications/changes-energy-price-cap-1-january-2024
ELECTRICITY_PRICE_CAP = 0.29
GAS_PRICE_CAP = 0.07
# This is a weighted mean of the price caps, using the consumption figures above as weights
PRICE_FACTOR = 0.11183098591549295
EPC_BANDS = ["G", "F", "E", "D", "C", "B", "A"]
@classmethod
def estimate(cls, kwh: float):
"""
Estimate the annual bill savings based on the kwh savings
:param kwh: The kwh savings
:return: An estimate for annual bill savings
"""
return cls.PRICE_FACTOR * kwh
@classmethod
def adjust_energy_to_metered(cls, epc_energy_consumption, current_epc_rating):
"""
The over-prediction of energy use by EPCs in Great Britain: A comparison
of EPC-modelled and metered primary energy use intensity
Which can be found here: https://www.sciencedirect.com/science/article/pii/S0378778823002542
We implement the results on page 10
:return:
"""
gradients = {
"A": -0.1,
"B": -0.1,
"C": -0.43,
"D": -0.52,
"E": -0.7,
"F": -0.76,
"G": -0.76
}
intercepts = {
"A": 28,
"B": 28,
"C": 97,
"D": 119,
"E": 160,
"F": 157,
"G": 157
}
gradient = gradients[current_epc_rating]
intercept = intercepts[current_epc_rating]
# This should be negative
consumption_difference = gradient * epc_energy_consumption + intercept
if consumption_difference > 0:
raise ValueError("consumption_difference should be negative")
adjusted_consumption = (epc_energy_consumption + consumption_difference)
return adjusted_consumption
@classmethod
def adjust_expected_band(cls, expected_epc_rating, current_epc_rating):
"""
Because of the differing intercepts and intercepts when adjusting, it's possible for
expected_adjusted_energy to be bigger than current_adjusted_energy. In this case, we'll
adjust, against at most 1 EPC band above the curent. This function performs the EPC adjustment
:param expected_epc_rating: The expected EPC rating
:param current_epc_rating: The current EPC rating
"""
# Find index of expected EPC rating
expected_index = cls.EPC_BANDS.index(expected_epc_rating)
current_index = cls.EPC_BANDS.index(current_epc_rating)
if expected_index - 1 < current_index:
return current_epc_rating
return cls.EPC_BANDS[expected_index - 1]