Closed antoinetavant closed 1 week ago
Issue démarrée sur la branche 'tempo_pred'.
@mattcln voici ce que j'ai fait à partir de la Doc RTE. Ça peut être une "baseline"
import pandas as pd
from energy_forecast import ROOT_DIR
data_file = ROOT_DIR / "data/rte_agg_daily_2014_2024.csv"
data_agg = pd.read_csv(data_file, index_col=0, parse_dates=True)
used_cols = ["Prévision_J-1", "Type_de_jour_TEMPO", "Eolien", "Solaire"]
period_start = "2015-09-01" # included
period_end = "2016-08-31" # included
data = data_agg.loc[period_start:period_end, used_cols]
data["production_nette"] = data["Prévision_J-1"] - (data["Eolien"] + data["Solaire"]) # here need to use Predicted production
q80 = data["production_nette"].quantile(0.8)
q40 = data["production_nette"].quantile(0.4)
qtemp30 = 8.5 # Rought estimation
gamma = -0.1176
kappa = 8.3042
data["production_norm"] = (data["production_nette"] - q40) / ( (q80 - q40) * np.exp(gamma * (kappa - qtemp30)) ) # there is a typo in the docs here : it is a - not a +
start_BLANC = 43
start_ROUGE = 22
start_blanc_rouge = start_BLANC + start_ROUGE
categories = pd.get_dummies(data["Type_de_jour_TEMPO"]).astype(int)
data["stock_rouge"] = start_ROUGE - categories["ROUGE"].cumsum()
data["stock_blanc"] = start_BLANC - categories["BLANC"].cumsum()
data["stock_blanc_rouge"] = data["stock_rouge"] + data["stock_blanc"]
data["jour_tempo"] = range(1, len(data) + 1) # Not sur if start from 0 or 1 in docs
A_rouge = 3.15
B_rouge = -0.010
C_rouge = -0.031
A_blanc_rouge = 4
B_blanc_rouge = -0.015
C_blanc_rouge = -0.026
data["seuil_rouge"] = (
A_rouge + B_rouge * data["jour_tempo"] + C_rouge * data["stock_rouge"]
)
data["seuil_blanc_rouge"] = (
A_blanc_rouge
+ B_blanc_rouge * data["jour_tempo"]
+ C_blanc_rouge * data["stock_blanc_rouge"]
)
prediction_rouge = data["production_norm"] > data["seuil_rouge"]
start_ROUGE_allowed = "2015-10-31"
end_ROUGE_allowed = "2016-04-01"
prediction_rouge[:start_ROUGE_allowed] = False
prediction_rouge[end_ROUGE_allowed:] = False
prediction_rouge[data["stock_rouge"] < 0] = False
sundays = data.index.weekday == 6
saturdays = data.index.weekday == 5
prediction_rouge[sundays] = False
prediction_rouge[saturdays] = False
prediction_blanc_rouge = data["production_norm"] > data["seuil_blanc_rouge"]
prediction_blanc = prediction_blanc_rouge & ~prediction_rouge
prediction_blanc[sundays] = False
prediction_blanc[data["stock_blanc"] < 0] = False
J'ai eu cette réflexion pendant que j'implémentais cette logique : On doit considérer la classification uniquement des N (entre 2 et 4) prochain jours. Donc les valeurs par exemple de stock restant peuvent utiliser les valeurs réelles fournies par RTE.
De plus, en situation réelles on pourra utiliser les données de cette API pour utiliser les productions ENR prévues par RTE afin de mettre a jours les differents parameters de classifications tempo https://data.rte-france.com/catalog/-/api/generation/Generation-Forecast/v2.1
En fin de période, une vérification de l’écoulement du stock est effectuée pour assurer que l’intégralité du stock soit bien placée. En cas de besoin, l’algorithme peut placer des jours même si les seuils requis ne sont pas atteints
En 2019-2020 ils avaient utilisés 18/22 jours rouges (sous covid) et en 2021-2022 un jour blanc n'a pas été placé. Pour les autres années, tout a bien été écoulé.
Si on veut tester le modèle, j'ai une partie de code qui recalcule le stock au jour J pour tout l'historique.
stock_jours_annuels = {
"rouge": 22,
"blanc": 43,
"bleu": 300
}
stock_jours_annuels_b = {
"rouge": 22,
"blanc": 43,
"bleu": 301
}
def get_base_stock(year:int) -> dict:
"""
Vérifie si l'année est bissextiles ou non
Renvoie le stock initial de jour tempo.
:param year: _description_
:return: _description_
"""
if isleap(year):
return stock_jours_annuels_b.copy()
else:
return stock_jours_annuels.copy()
def update_stock(previous_tempo:str, stock_actuel:dict) -> dict:
if previous_tempo:
if previous_tempo == "ROUGE":
stock_actuel["rouge"] -= 1
elif previous_tempo == "BLANC":
stock_actuel["blanc"] -= 1
elif previous_tempo == "BLEU":
stock_actuel["bleu"] -= 1
return stock_actuel
else:
return None
def check_stock_empty(stock_actuel:dict, year:int):
"""
Cette fonction permet de vérifier si les stocks sont bien calculés :
Tout les stocks doivent à 0, si ce n'est pas le cas c'est que RTE
n'a pas utilisé tout les jours en stock, l'écart devrait alors se
répercuter négativement sur les jours bleus (la somme du dictionnaire doit être de 0 forcément)
:param stock_actuel: _description_
:param year: _description_
"""
if stock_actuel:
if stock_actuel["rouge"] == stock_actuel["bleu"] == stock_actuel["blanc"] == 0:
pass
else:
print(f"Les stocks de jours n'était pas à 0 le 1er septembre {year} ! :(")
print(f"Voici l'état du stock : {stock_actuel}")
if stock_actuel["rouge"] + stock_actuel["bleu"] + stock_actuel["blanc"] != 0:
raise ValueError("La somme des stocks ne fait pas 0 à la fin de l'année.")
def get_cumulative_stock(data):
"""
On calcule les stocks restants pour chaque jour tempo à une date X.
Les valeurs représentent la situation à J-1 car la prédiction
pour une date D se fait avec le stocks restants à J-1.
Ex :
Une row avec
- stock_j_rouge = 0
- stock_j_blanc = 1
- stock_j_bleu = 14
- type_tempo = "BLANC"
=> Au moment de prédire le jour "BLANC", je n'avais plus de jour rouge
mais il me restait un dernier jour blanc en stock
:param data: _description_
:return: _description_
"""
stock_rouge, stock_bleu, stock_blanc = [], [], []
previous_tempo = stock_actuel = None
for i, row in data.iterrows():
# Remise à zéro les 1er Septembre
# sinon on update le stock en fonction du type tempo de la veille
if row["Date"].month == 9 and row["Date"].day == 1:
update_stock(previous_tempo, stock_actuel)
check_stock_empty(stock_actuel, row["Date"].year)
stock_actuel = get_base_stock(row["Date"].year + 1)
else:
update_stock(previous_tempo, stock_actuel)
stock_rouge.append(stock_actuel["rouge"])
stock_blanc.append(stock_actuel["blanc"])
stock_bleu.append(stock_actuel["bleu"])
# On enregistre le type tempo
previous_tempo = row["type_tempo"]
data["stock_j_rouge"] = stock_rouge
data["stock_j_blanc"] = stock_blanc
data["stock_j_bleu"] = stock_bleu
return data
data = get_cumulative_stock(data)
Super ! J'avais pas vu ça effectivement !
Cette issue est résolue par #22 On pourra améliorer la performance, mais au moins, on a une baseline !
L'objectif de cette issue est d'améliorer la classification des jours tempos (bleu, blancs et rouges).
Pour rappel, les classes tempo modifient la tarification de l'électricité : d'après la grille 2024, le tarif heure pleine bleu est à 16 cent/kWh, alors que le tarif rouge est à 75.6 cents/kWh !
Un premier essaie utilisant un classificateur sur la seule valeur de la production carbonée (consommation - production ENR) ne marche pas bien pour plusieurs raisons :
Pour ces deux raisons, il est impératif de modifier l'approche de classification des jours tempo.
La définition la plus explicite des règles est fournie par RTE ici. Cependant, il est possible que les seuils et règles effectivement en place évoluent.
Pour moi, il faut :
Remarque : On a vu que les classes tempo était mieux corrélée avec la prévision de consommation à J+1 plutôt que la consommation réelle. C'est normal, car les jours sont définis la veille pour le lendemain, mais il faut bien le prendre en compte dans la classification.