Implemented baseline policy

This commit is contained in:
Victor Mylle
2023-12-13 10:32:09 +00:00
parent f74d588e62
commit c437c23566
4 changed files with 229 additions and 6 deletions

View File

@@ -4,7 +4,7 @@ Eerst literatuur bekijken ofdat probleem al voorkomt
- [x] compare reconstructed prices with real imbalance prices (on figure and metrics on whole test set)
- [ ] Baseline policy (Eche imbalanceprijzen):
- [x] Baseline policy (Eche imbalanceprijzen):
- Batterij:
- 2 MWh
- 1 MW power (charging / discharging)
@@ -13,9 +13,7 @@ Eerst literatuur bekijken ofdat probleem al voorkomt
- 1000 generaties voor 1 dag -> 1000 prijzen reconstrueren -> tresholds bepalen (logische tresholds) -> 2 tresholds voor 1 dag en toepassen op volledige test set
-> resultaten: # charge cycle
- [ ] Learning rate non autoregressive
- [x] Learning rate non autoregressive
- [x] !!! Historgram quantile plot volledige dag
- [x] CRPS en MAE, over 96 wanneer wordt het slecht
@@ -199,4 +197,4 @@ Estimated Total Size (MB): 219.17
| Model | Experiment | test_L1Loss | test_CRPSLoss |
|---|---|---| ---|
| Linear Model | [Link](https://clearml.victormylle.be/projects/2e46d4af6f1e4c399cf9f5aa30bc8795/experiments/8f00fa7713f94d2ca376ce0eabc2742f/info-output/metrics/scalar?columns=selected&columns=type&columns=name&columns=tags&columns=status&columns=project.name&columns=users&columns=started&columns=last_update&columns=last_iteration&columns=parent.name&columns=m.1a899a19b54957e02a21c3a1d82577ad.0970ca62a85af2722008c5220e9d8a9e.value.Summary%2Ftest_CRPSLoss.lastreported&columns=m.293da6b015ca6a65992dcf7a53fa0237.098f6bcd4621d373cade4e832627b4f6.min_value.PinballLoss.test&order=-last_update&filter=) | 104.89022124436754 | 69.04472427024562 |
| Non Linear Model | [Link](https://clearml.victormylle.be/projects/2e46d4af6f1e4c399cf9f5aa30bc8795/experiments/f2ed5c3b54cd4d859d72bc55e53ff646/info-output/metrics/scalar?columns=selected&columns=type&columns=name&columns=tags&columns=status&columns=project.name&columns=users&columns=started&columns=last_update&columns=last_iteration&columns=parent.name&columns=m.1a899a19b54957e02a21c3a1d82577ad.0970ca62a85af2722008c5220e9d8a9e.value.Summary%2Ftest_CRPSLoss.lastreported&columns=m.293da6b015ca6a65992dcf7a53fa0237.098f6bcd4621d373cade4e832627b4f6.min_value.PinballLoss.test&order=-last_update&filter=) | 103.250159162178 | 74.21675055952228 |
| Non Linear Model | [Link](https://clearml.victormylle.be/projects/2e46d4af6f1e4c399cf9f5aa30bc8795/experiments/d19c767120a24f97b3231f0e8ac9f2b5/info-output/metrics/scalar?columns=selected&columns=type&columns=name&columns=tags&columns=status&columns=project.name&columns=users&columns=started&columns=last_update&columns=last_iteration&columns=parent.name&columns=m.1a899a19b54957e02a21c3a1d82577ad.0970ca62a85af2722008c5220e9d8a9e.value.Summary%2Ftest_CRPSLoss.lastreported&columns=m.293da6b015ca6a65992dcf7a53fa0237.098f6bcd4621d373cade4e832627b4f6.min_value.PinballLoss.test&order=-last_update&filter=) | 103.3332725941881 | 69.50204645931149 |

View File

@@ -0,0 +1,85 @@
# Policies for battery charging
## Battery
Capacity: 2MWh \
Charging Power: 1MW \
Discharging Power: 1MW
## Baseline policy
Determining 2 thresholds for battery charging and discharging. Charging when the imbalance price is below the lower threshold and discharging when the imbalance price is above the upper threshold.
Training data: 01-01-2020 - 31-12-2022
Test data: 01-01-2023 - 12-12-2023
| Charging threshold (€/MWh) | Discharge threshold (€/MWh) | Profit (€) | Charge Cycles |
------------------:|---------------------:|---------------:|----------------:|
| 150 | 100 | 366406 | 2658 |
| 150 | 50 | 366406 | 2658 |
| 150 | 0 | 366406 | 2658 |
| 150 | 150 | 365999 | 2639 |
| 100 | 150 | 364905 | 2306 |
| 100 | 100 | 364483 | 2886 |
| 100 | 0 | 364376 | 2896 |
| 100 | 50 | 364376 | 2896 |
| 200 | 150 | 352953 | 2416 |
| 200 | 100 | 352953 | 2416 |
| 200 | 50 | 352953 | 2416 |
| 200 | 0 | 352953 | 2416 |
| 200 | 200 | 352811 | 2413 |
| 150 | 200 | 350975 | 2243 |
| 100 | 200 | 345855 | 1929 |
| 50 | 50 | 341569 | 3864 |
| 50 | 100 | 341421 | 2357 |
| 50 | 0 | 341202 | 3865 |
| 200 | 250 | 337567 | 1996 |
| 50 | 150 | 330189 | 1815 |
| 150 | 250 | 329684 | 1835 |
| 200 | 300 | 329343 | 1610 |
| 250 | 250 | 323456 | 2089 |
| 250 | 0 | 322692 | 2103 |
| 250 | 150 | 322692 | 2103 |
| 250 | 50 | 322692 | 2103 |
| 250 | 100 | 322692 | 2103 |
| 250 | 200 | 322692 | 2103 |
| 250 | 300 | 321378 | 1685 |
| 100 | 250 | 319651 | 1554 |
| 150 | 300 | 319089 | 1462 |
| 300 | 250 | 314776 | 1752 |
| 300 | 50 | 314776 | 1752 |
| 300 | 100 | 314776 | 1752 |
| 300 | 150 | 314776 | 1752 |
| 300 | 200 | 314776 | 1752 |
| 300 | 0 | 314776 | 1752 |
| 50 | 200 | 313685 | 1476 |
| 300 | 300 | 313469 | 1749 |
| 100 | 300 | 309773 | 1236 |
| 200 | 350 | 306832 | 1341 |
| 250 | 350 | 302349 | 1411 |
| 150 | 350 | 295917 | 1202 |
| 300 | 350 | 290570 | 1466 |
| 50 | 250 | 290097 | 1195 |
| 350 | 300 | 284856 | 1507 |
| 350 | 250 | 284856 | 1507 |
| 350 | 150 | 284856 | 1507 |
| 350 | 100 | 284856 | 1507 |
| 350 | 50 | 284856 | 1507 |
| 350 | 0 | 284856 | 1507 |
| 350 | 200 | 284856 | 1507 |
| 100 | 350 | 284323 | 996 |
| 50 | 300 | 280922 | 954 |
| 350 | 350 | 279834 | 1496 |
| 0 | 150 | 277863 | 1243 |
| 0 | 100 | 276435 | 1477 |
| 0 | 200 | 270056 | 1080 |
| 0 | 50 | 269265 | 1914 |
| 50 | 350 | 259569 | 787 |
| 0 | 250 | 258282 | 908 |
| 0 | 300 | 250679 | 741 |
| 0 | 0 | 248357 | 2065 |
| 0 | 350 | 233775 | 624 |
## Optimal policy
Charge threshold: 150 €/MWh \
Discharge threshold: 100 €/MWh
Profit on test data: € 169846.84 \
Charge cycles: 1403

View File

@@ -11,3 +11,4 @@ clearml
properscoring
nbconvert
torchinfo
tabulate

View File

@@ -0,0 +1,139 @@
import pandas as pd
import numpy as np
import itertools
from tqdm import tqdm
imbalance_prices = "data/imbalance_prices.csv"
class Battery:
def __init__(self, capacity: float, power: float):
"""
:param capacity: Battery capacity in MWh
:param current_charge: Current charge in MWh
:param power: Power in MW
"""
self.capacity = capacity
self.current_charge = 0
self.power = power
self.charge_cycles = 0
self.charging = False
self.discharging = False
def simulate(self):
"""
Simulate the battery for one time step (one quarter of an hour)
"""
if self.charging:
return self.charge()
elif self.discharging:
return self.discharge()
return 0
def discharge(self):
"""
Discharge the battery by one time step (one quarter of an hour)
"""
if self.current_charge == 0:
return 0
self.discharging = True
self.current_charge -= self.power / 4
if self.current_charge <= 0:
self.current_charge = 0
self.discharging = False
self.charge_cycles += 1
return self.power / 4
def charge(self):
"""
Charge the battery by one time step (one quarter of an hour)
"""
if self.current_charge == self.capacity:
return 0
self.charging = True
self.current_charge += self.power / 4
if self.current_charge >= self.capacity:
self.current_charge = self.capacity
self.charging = False
return self.power / 4
def reset(self):
"""
Reset the battery to its initial state
"""
self.current_charge = 0
self.charging = False
self.discharging = False
self.charge_cycles = 0
class BaselinePolicy():
def __init__(self, battery: Battery):
self.battery = battery
self.train_data = self.load_imbalance_prices(train=True)
self.test_data = self.load_imbalance_prices(train=False)
# print first datetime of train and test data
print(f"Training range: {self.train_data.iloc[0]['DateTime'].strftime('%d-%m-%Y')} - {self.train_data.iloc[-1]['DateTime'].strftime('%d-%m-%Y')}")
print(f"Test range: {self.test_data.iloc[0]['DateTime'].strftime('%d-%m-%Y')} - {self.test_data.iloc[-1]['DateTime'].strftime('%d-%m-%Y')}")
def load_imbalance_prices(self, train: bool = True):
imbalance_prices = pd.read_csv('data/imbalance_prices.csv', parse_dates=True, sep=";")
imbalance_prices = imbalance_prices[['DateTime', 'Positive imbalance price']]
imbalance_prices['DateTime'] = pd.to_datetime(imbalance_prices['DateTime'], utc=True)
if train:
imbalance_prices = imbalance_prices.loc[imbalance_prices['DateTime'].dt.year < 2023]
imbalance_prices = imbalance_prices.loc[imbalance_prices['DateTime'].dt.year >= 2020]
else:
imbalance_prices = imbalance_prices.loc[imbalance_prices['DateTime'].dt.year == 2023]
imbalance_prices = imbalance_prices.sort_values(by=['DateTime'])
return imbalance_prices
def get_train_score(self, charge_treshold, discharge_treshold):
return self.get_score(self.train_data, charge_treshold, discharge_treshold)
def get_test_score(self, charge_treshold, discharge_treshold):
return self.get_score(self.test_data, charge_treshold, discharge_treshold)
# if price is below treshold -> charge battery: total_profit -= charge * price
# if price is above treshold -> discharge battery: total_profit += discharge * price
def get_score(self, df, charge_treshold, discharge_treshold):
self.battery.reset()
total_profit = 0
for index, row in df.iterrows():
if self.battery.charging:
total_profit -= self.battery.simulate() * row['Positive imbalance price']
elif self.battery.discharging:
total_profit += self.battery.simulate() * row['Positive imbalance price']
else:
if row['Positive imbalance price'] < charge_treshold:
total_profit -= self.battery.charge() * row['Positive imbalance price']
elif row['Positive imbalance price'] > discharge_treshold:
total_profit += self.battery.discharge() * row['Positive imbalance price']
return total_profit, self.battery.charge_cycles
def treshold_scores(self, charge_tresholds, discharge_tresholds):
df = pd.DataFrame(columns=["Charge treshold", "Discharge treshold", "Total Profit", "Charge cycles"])
for charge_treshold, discharge_treshold in tqdm(itertools.product(charge_tresholds, discharge_tresholds)):
total_profit, charge_cycles = self.get_train_score(charge_treshold, discharge_treshold)
df = pd.concat([df, pd.DataFrame([[charge_treshold, discharge_treshold, total_profit, charge_cycles]], columns=["Charge treshold", "Discharge treshold", "Total Profit", "Charge cycles"])])
df = df.sort_values(by=['Total Profit'], ascending=False)
return df
battery = Battery(2, 1)
policy = BaselinePolicy(battery)
# charge_tresholds = [0, 50, 100, 150, 200, 250, 300, 350]
# discharge_tresholds = [0, 50, 100, 150, 200, 250, 300, 350]
# df = policy.treshold_scores(charge_tresholds, discharge_tresholds)
# print(df.to_markdown())
print(policy.get_test_score(150, 100))