Sommerzeit Probleme (bei Zeitumstellung gibt es eine Stunde weniger)

Für die PVForecast kann jetzt ein aktuelle Messwert der PV Leistung
eingegeben werden!
This commit is contained in:
Bla Bla 2024-03-31 13:00:01 +02:00
parent 97f407073a
commit c265885286
6 changed files with 161 additions and 50 deletions

View File

@ -1,6 +1,5 @@
from flask import Flask, jsonify, request
import numpy as np
from datetime import datetime
from modules.class_load import *
from modules.class_ems import *
from modules.class_pv_forecast import *
@ -8,23 +7,37 @@ from modules.class_akku import *
from modules.class_strompreis import *
from modules.class_heatpump import *
from modules.class_load_container import *
from modules.class_eauto import *
from modules.class_sommerzeit import *
from modules.visualize import *
import os
from flask import Flask, send_from_directory
from pprint import pprint
import matplotlib
matplotlib.use('Agg') # Setzt das Backend auf Agg
import matplotlib.pyplot as plt
from modules.visualize import *
import string
from datetime import datetime
from deap import base, creator, tools, algorithms
import numpy as np
import random
import os
start_hour = datetime.now().hour
# if ist_dst_wechsel(datetime.now()):
# prediction_hours = 23 # Anpassung auf 23 Stunden für DST-Wechseltage
# else:
# prediction_hours = 24 # Standardwert für Tage ohne DST-Wechsel
prediction_hours = 24
start_hour = datetime.now().hour
hohe_strafe = 10.0
# print(prediction_hours)
# sys.exit()
def isfloat(num):
try:
float(num)
return True
except ValueError:
return False
def evaluate_inner(individual, ems):
@ -91,7 +104,7 @@ def optimize(start_solution=None):
population.insert(0, creator.Individual(start_solution))
#algorithms.eaMuPlusLambda(population, toolbox, 100, 200, cxpb=0.3, mutpb=0.3, ngen=500, stats=stats, halloffame=hof, verbose=True)
algorithms.eaSimple(population, toolbox, cxpb=0.5, mutpb=0.8, ngen=200, stats=stats, halloffame=hof, verbose=True)
algorithms.eaSimple(population, toolbox, cxpb=0.8, mutpb=0.8, ngen=400, stats=stats, halloffame=hof, verbose=True)
return hof[0]
@ -104,6 +117,8 @@ app = Flask(__name__)
# Ersetzen Sie diese Logik durch Ihren eigentlichen Optimierungscode
def durchfuehre_simulation(parameter):
start_hour = datetime.now().hour
############
# Parameter
############
@ -113,7 +128,7 @@ def durchfuehre_simulation(parameter):
akku_size = parameter['pv_akku_cap'] # Wh
year_energy = parameter['year_energy'] #2000*1000 #Wh
einspeiseverguetung_cent_pro_wh = np.full(prediction_hours, parameter["einspeiseverguetung_euro_pro_wh"]) #= # € / Wh 7/(1000.0*100.0)
einspeiseverguetung_euro_pro_wh = np.full(prediction_hours, parameter["einspeiseverguetung_euro_pro_wh"]) #= # € / Wh 7/(1000.0*100.0)
max_heizleistung = parameter['max_heizleistung'] #1000 # 5 kW Heizleistung
wp = Waermepumpe(max_heizleistung,prediction_hours)
@ -146,7 +161,13 @@ def durchfuehre_simulation(parameter):
###############
#PVforecast = PVForecast(filepath=os.path.join(r'test_data', r'pvprognose.json'))
PVforecast = PVForecast(prediction_hours = prediction_hours, url=pv_forecast_url)
#print("PVPOWER",parameter['pvpowernow'])
if isfloat(parameter['pvpowernow']):
PVforecast.update_ac_power_measurement(date_time=datetime.now(), ac_power_measurement=float(parameter['pvpowernow']))
#PVforecast.print_ac_power_and_measurement()
pv_forecast = PVforecast.get_pv_forecast_for_date_range(date_now,date) #get_forecast_for_date(date)
temperature_forecast = PVforecast.get_temperature_for_date_range(date_now,date)
@ -165,7 +186,7 @@ def durchfuehre_simulation(parameter):
leistung_wp = wp.simulate_24h(temperature_forecast)
gesamtlast.hinzufuegen("Heatpump", leistung_wp)
ems = EnergieManagementSystem(akku=akku, gesamtlast = gesamtlast, pv_prognose_wh=pv_forecast, strompreis_cent_pro_wh=specific_date_prices, einspeiseverguetung_cent_pro_wh=einspeiseverguetung_cent_pro_wh, eauto=eauto)
ems = EnergieManagementSystem(akku=akku, gesamtlast = gesamtlast, pv_prognose_wh=pv_forecast, strompreis_euro_pro_wh=specific_date_prices, einspeiseverguetung_euro_pro_wh=einspeiseverguetung_euro_pro_wh, eauto=eauto)
o = ems.simuliere(start_hour)
@ -189,7 +210,9 @@ def durchfuehre_simulation(parameter):
#print(o)
visualisiere_ergebnisse(gesamtlast, pv_forecast, specific_date_prices, o,best_solution[0::2],best_solution[1::2] , temperature_forecast, start_hour, prediction_hours)
visualisiere_ergebnisse(gesamtlast, pv_forecast, specific_date_prices, o,best_solution[0::2],best_solution[1::2] , temperature_forecast, start_hour, prediction_hours,einspeiseverguetung_euro_pro_wh)
os.system("scp visualisierungsergebnisse.pdf andreas@192.168.1.135:")
#print(eauto)
return {"discharge_hours_bin":discharge_hours_bin, "eautocharge_hours_float":eautocharge_hours_float ,"result":o ,"eauto_obj":eauto,"start_solution":best_solution}
@ -203,7 +226,7 @@ def simulation():
parameter = request.json
# Erforderliche Parameter prüfen
erforderliche_parameter = [ 'pv_akku_cap', 'year_energy',"einspeiseverguetung_euro_pro_wh", 'max_heizleistung', 'pv_forecast_url', 'eauto_min_soc', "eauto_cap","eauto_charge_efficiency","eauto_charge_power","eauto_soc","pv_soc","start_solution"]
erforderliche_parameter = [ 'pv_akku_cap', 'year_energy',"einspeiseverguetung_euro_pro_wh", 'max_heizleistung', 'pv_forecast_url', 'eauto_min_soc', "eauto_cap","eauto_charge_efficiency","eauto_charge_power","eauto_soc","pv_soc","start_solution","pvpowernow"]
for p in erforderliche_parameter:
if p not in parameter:
return jsonify({"error": f"Fehlender Parameter: {p}"}), 400
@ -217,6 +240,10 @@ def simulation():
return jsonify(ergebnis)
@app.route('/visualisierungsergebnisse.pdf')
def get_pdf():
return send_from_directory('', 'visualisierungsergebnisse.pdf')

View File

@ -1,6 +1,6 @@
import numpy as np
class PVAkku:
def __init__(self, kapazitaet_wh=None, hours=None, lade_effizienz=0.9, entlade_effizienz=0.9,max_ladeleistung_w=None,start_soc_prozent=0):
def __init__(self, kapazitaet_wh=None, hours=None, lade_effizienz=0.9, entlade_effizienz=0.9,max_ladeleistung_w=None,start_soc_prozent=0,min_soc_prozent=0,max_soc_prozent=100):
# Kapazität des Akkus in Wh
self.kapazitaet_wh = kapazitaet_wh
# Initialer Ladezustand des Akkus in Wh
@ -13,6 +13,9 @@ class PVAkku:
self.lade_effizienz = lade_effizienz
self.entlade_effizienz = entlade_effizienz
self.max_ladeleistung_w = max_ladeleistung_w if max_ladeleistung_w else self.kapazitaet_wh
self.min_soc_prozent = min_soc_prozent
self.max_soc_prozent = max_soc_prozent
def to_dict(self):
return {
@ -94,13 +97,16 @@ class PVAkku:
wh = wh if wh is not None else self.max_ladeleistung_w
# Berechnung der tatsächlichen Lademenge unter Berücksichtigung der Ladeeffizienz
effektive_lademenge = min(wh, self.max_ladeleistung_w) * self.lade_effizienz
effektive_lademenge = min(wh, self.max_ladeleistung_w)
# Aktualisierung des Ladezustands ohne die Kapazität zu überschreiten
geladene_menge = min(self.kapazitaet_wh - self.soc_wh, effektive_lademenge)
geladene_menge_ohne_verlust = min(self.kapazitaet_wh - self.soc_wh, effektive_lademenge)
geladene_menge = geladene_menge_ohne_verlust * self.lade_effizienz
self.soc_wh += geladene_menge
verluste_wh = geladene_menge* (1.0-self.lade_effizienz)
verluste_wh = geladene_menge_ohne_verlust* (1.0-self.lade_effizienz)
return geladene_menge, verluste_wh
# effektive_lademenge = wh * self.lade_effizienz

View File

@ -3,13 +3,13 @@ from pprint import pprint
class EnergieManagementSystem:
def __init__(self, akku=None, pv_prognose_wh=None, strompreis_cent_pro_wh=None, einspeiseverguetung_cent_pro_wh=None, eauto=None, gesamtlast=None):
def __init__(self, akku=None, pv_prognose_wh=None, strompreis_euro_pro_wh=None, einspeiseverguetung_euro_pro_wh=None, eauto=None, gesamtlast=None):
self.akku = akku
#self.lastkurve_wh = lastkurve_wh
self.gesamtlast = gesamtlast
self.pv_prognose_wh = pv_prognose_wh
self.strompreis_cent_pro_wh = strompreis_cent_pro_wh # Strompreis in Cent pro Wh
self.einspeiseverguetung_cent_pro_wh = einspeiseverguetung_cent_pro_wh # Einspeisevergütung in Cent pro Wh
self.strompreis_euro_pro_wh = strompreis_euro_pro_wh # Strompreis in Cent pro Wh
self.einspeiseverguetung_euro_pro_wh = einspeiseverguetung_euro_pro_wh # Einspeisevergütung in Cent pro Wh
self.eauto = eauto
def set_akku_discharge_hours(self, ds):
@ -46,7 +46,7 @@ class EnergieManagementSystem:
lastkurve_wh = self.gesamtlast.gesamtlast_berechnen()
ende = min( len(lastkurve_wh),len(self.pv_prognose_wh), len(self.strompreis_cent_pro_wh))
ende = min( len(lastkurve_wh),len(self.pv_prognose_wh), len(self.strompreis_euro_pro_wh))
#print(ende)
# Berechnet das Ende basierend auf der Länge der Lastkurve
for stunde in range(start_stunde, ende):
@ -54,7 +54,7 @@ class EnergieManagementSystem:
# Anpassung, um sicherzustellen, dass Indizes korrekt sind
verbrauch = lastkurve_wh[stunde]
erzeugung = self.pv_prognose_wh[stunde]
strompreis = self.strompreis_cent_pro_wh[stunde] if stunde < len(self.strompreis_cent_pro_wh) else self.strompreis_cent_pro_wh[-1]
strompreis = self.strompreis_euro_pro_wh[stunde] if stunde < len(self.strompreis_euro_pro_wh) else self.strompreis_euro_pro_wh[-1]
verluste_wh_pro_stunde.append(0.0)
#eauto_soc = self.eauto.get_stuendlicher_soc()[stunde]
@ -83,7 +83,7 @@ class EnergieManagementSystem:
#print("verluste_laden_akku:",verluste_laden_akku)
netzeinspeisung_wh_pro_stunde.append(überschuss - geladene_energie-verluste_laden_akku)
eigenverbrauch_wh_pro_stunde.append(verbrauch)
stündliche_einnahmen_euro = (überschuss - geladene_energie-verluste_laden_akku) * self.einspeiseverguetung_cent_pro_wh[stunde]
stündliche_einnahmen_euro = (überschuss - geladene_energie-verluste_laden_akku) * self.einspeiseverguetung_euro_pro_wh[stunde]
#print(überschuss," ", geladene_energie," ",verluste_laden_akku)
netzbezug_wh_pro_stunde.append(0.0)
else:

View File

@ -4,16 +4,19 @@ from datetime import datetime
from pprint import pprint
import json, sys, os
import requests, hashlib
from dateutil import parser, tz
class ForecastData:
def __init__(self, date_time, dc_power, ac_power, windspeed_10m, temperature):
def __init__(self, date_time, dc_power, ac_power, windspeed_10m=None, temperature=None,ac_power_measurement=None):
self.date_time = date_time
self.dc_power = dc_power
self.ac_power = ac_power
self.windspeed_10m = windspeed_10m
self.temperature = temperature
self.ac_power_measurement = None
# Getter für die ForecastData-Attribute
def get_date_time(self):
return self.date_time
@ -21,8 +24,14 @@ class ForecastData:
def get_dc_power(self):
return self.dc_power
def ac_power_measurement(self):
return self.ac_power_measurement
def get_ac_power(self):
return self.ac_power
if self.ac_power_measurement != None:
return self.ac_power_measurement
else:
return self.ac_power
def get_windspeed_10m(self):
return self.windspeed_10m
@ -36,6 +45,7 @@ class PVForecast:
self.forecast_data = []
self.cache_dir = cache_dir
self.prediction_hours = prediction_hours
self.current_measurement = None
if not os.path.exists(self.cache_dir):
os.makedirs(self.cache_dir)
@ -48,6 +58,32 @@ class PVForecast:
if len(self.forecast_data) < self.prediction_hours:
raise ValueError(f"Die Vorhersage muss mindestens {self.prediction_hours} Stunden umfassen, aber es wurden nur {len(self.forecast_data)} Stunden vorhergesagt.")
def update_ac_power_measurement(self, date_time=None, ac_power_measurement=None):
"""Aktualisiert einen DC-Leistungsmesswert oder fügt ihn hinzu."""
found = False
target_timezone = tz.gettz('Europe/Berlin')
input_date_hour = date_time.astimezone(target_timezone).replace(minute=0, second=0, microsecond=0)
for forecast in self.forecast_data:
forecast_date_hour = datetime.strptime(forecast.date_time, "%Y-%m-%dT%H:%M:%S.%f%z").astimezone(target_timezone).replace(minute=0, second=0, microsecond=0)
#print(forecast_date_hour," ",input_date_hour)
if forecast_date_hour == input_date_hour:
forecast.ac_power_measurement = ac_power_measurement
found = True
break
# if not found:
# # Erstelle ein neues ForecastData-Objekt, falls kein entsprechender Zeitstempel gefunden wurde
# # Hier kannst du entscheiden, wie die anderen Werte gesetzt werden sollen, falls keine Vorhersage existiert
# new_forecast = ForecastData(date_time, dc_power=None, ac_power=None, dc_power_measurement=dc_power_measurement)
# self.forecast_data.append(new_forecast)
# # Liste sortieren, um sie chronologisch zu ordnen
# self.forecast_data.sort(key=lambda x: datetime.strptime(x.date_time, "%Y-%m-%dT%H:%M:%S.%f%z").replace(minute=0, second=0, microsecond=0))
def process_data(self, data):
@ -71,16 +107,6 @@ class PVForecast:
self.forecast_data.append(forecast)
# values = data.get('values', [])[0]
# for value in values:
# forecast = ForecastData(
# date_time=value.get('datetime'),
# dc_power=value.get('dcPower'),
# ac_power=value.get('power'),
# windspeed_10m=value.get('windspeed_10m'),
# temperature=value.get('temperature')
# )
# self.forecast_data.append(forecast)
def load_data_from_file(self, filepath):
with open(filepath, 'r') as file:
@ -127,14 +153,14 @@ class PVForecast:
return self.forecast_data
def get_forecast_for_date(self, input_date_str):
input_date = datetime.strptime(input_date_str, "%Y-%m-%d")
daily_forecast_obj = [data for data in self.forecast_data if datetime.strptime(data.get_date_time(), "%Y-%m-%dT%H:%M:%S.%f%z").date() == input_date.date()]
daily_forecast = []
for d in daily_forecast_obj:
daily_forecast.append(d.get_ac_power())
# def get_forecast_for_date(self, input_date_str):
# input_date = datetime.strptime(input_date_str, "%Y-%m-%d")
# daily_forecast_obj = [data for data in self.forecast_data if datetime.strptime(data.get_date_time(), "%Y-%m-%dT%H:%M:%S.%f%z").date() == input_date.date()]
# daily_forecast = []
# for d in daily_forecast_obj:
# daily_forecast.append(d.get_ac_power())
return np.array(daily_forecast)
# return np.array(daily_forecast)
def get_temperature_forecast_for_date(self, input_date_str):
input_date = datetime.strptime(input_date_str, "%Y-%m-%d")
@ -175,12 +201,19 @@ class PVForecast:
return np.array(temperature_forecast)[:self.prediction_hours]
def print_ac_power_and_measurement(self):
"""Druckt die DC-Leistung und das Messwert für jede Stunde."""
for forecast in self.forecast_data:
date_time = forecast.date_time
print(f"Zeit: {date_time}, DC: {forecast.dc_power}, AC: {forecast.ac_power}, Messwert: {forecast.ac_power_measurement} AC GET: {forecast.get_ac_power()}")
# Beispiel für die Verwendung der Klasse
if __name__ == '__main__':
forecast = PVForecast(r'..\test_data\pvprognose.json')
for data in forecast.get_forecast_data():
print(data.get_date_time(), data.get_dc_power(), data.get_ac_power(), data.get_windspeed_10m(), data.get_temperature())
date_now = datetime.now()
forecast = PVForecast(prediction_hours = 24, url="https://api.akkudoktor.net/forecast?lat=50.8588&lon=7.3747&power=5000&azimuth=-10&tilt=7&powerInvertor=10000&horizont=20,27,22,20&power=4800&azimuth=-90&tilt=7&powerInvertor=10000&horizont=30,30,30,50&power=1400&azimuth=-40&tilt=60&powerInvertor=2000&horizont=60,30,0,30&power=1600&azimuth=5&tilt=45&powerInvertor=1400&horizont=45,25,30,60&past_days=5&cellCoEff=-0.36&inverterEfficiency=0.8&albedo=0.25&timezone=Europe%2FBerlin&hourly=relativehumidity_2m%2Cwindspeed_10m")
forecast.update_ac_power_measurement(date_time=datetime.now(), ac_power_measurement=1000)
forecast.print_ac_power_and_measurement()

View File

@ -0,0 +1,26 @@
import datetime
import pytz
def ist_dst_wechsel(tag, timezone="Europe/Berlin"):
"""Prüft, ob an einem gegebenen Tag die Sommerzeit beginnt oder endet."""
tz = pytz.timezone(timezone)
# Hole den aktuellen Tag und den nächsten Tag
aktueller_tag = datetime.datetime(tag.year, tag.month, tag.day)
naechster_tag = aktueller_tag + datetime.timedelta(days=1)
# Lokalisiere die Tage in der gegebenen Zeitzone
aktueller_tag_localized = tz.localize(aktueller_tag, is_dst=None)
naechster_tag_localized = tz.localize(naechster_tag, is_dst=None)
# Prüfe, ob die UTC-Offsets unterschiedlich sind (DST-Wechsel)
dst_wechsel = aktueller_tag_localized.dst() != naechster_tag_localized.dst()
return dst_wechsel, aktueller_tag_localized.dst(), naechster_tag_localized.dst()
# Beispielverwendung
start_datum = datetime.datetime(2024, 3, 31) # Datum der DST-Umstellung
if ist_dst_wechsel(start_datum):
prediction_hours = 23 # Anpassung auf 23 Stunden für DST-Wechseltage
else:
prediction_hours = 24 # Standardwert für Tage ohne DST-Wechsel

View File

@ -1,14 +1,15 @@
import numpy as np
from modules.class_sommerzeit import *
from modules.class_load_container import Gesamtlast # Stellen Sie sicher, dass dies dem tatsächlichen Importpfad entspricht
import matplotlib
matplotlib.use('Agg') # Setzt das Backend auf Agg
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
from datetime import datetime
def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, discharge_hours, laden_moeglich, temperature, start_hour, prediction_hours):
def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, discharge_hours, laden_moeglich, temperature, start_hour, prediction_hours,einspeiseverguetung_euro_pro_wh):
#####################
# 24h
@ -18,7 +19,7 @@ def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, d
# Last und PV-Erzeugung
plt.figure(figsize=(14, 14))
plt.subplot(3, 2, 1)
plt.subplot(3, 3, 1)
stunden = np.arange(0, prediction_hours)
@ -56,8 +57,19 @@ def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, d
plt.legend()
plt.grid(True)
# Temperatur Forecast
# Vergütung
stundenp = np.arange(0, len(strompreise))
plt.subplot(3, 2, 4)
plt.plot(stunden, einspeiseverguetung_euro_pro_wh, label='Vergütung €/Wh', marker='x')
plt.title('Vergütung')
plt.xlabel('Stunde des Tages')
plt.ylabel('€/Wh')
plt.legend()
plt.grid(True)
# Temperatur Forecast
plt.subplot(3, 2, 5)
plt.title('Temperatur Forecast °C')
plt.plot(stunden, temperature, label='Temperatur °C', marker='x')
plt.xlabel('Stunde des Tages')
@ -74,7 +86,13 @@ def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, d
#####################
plt.figure(figsize=(14, 10))
stunden = np.arange(start_hour, prediction_hours)
if ist_dst_wechsel(datetime.now()):
stunden = np.arange(start_hour, prediction_hours-1)
else:
stunden = np.arange(start_hour, prediction_hours-1)
# Eigenverbrauch, Netzeinspeisung und Netzbezug
plt.subplot(3, 2, 1)
plt.plot(stunden, ergebnisse['Eigenverbrauch_Wh_pro_Stunde'], label='Eigenverbrauch (Wh)', marker='o')
@ -112,6 +130,7 @@ def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, d
plt.grid(True)
fig, axs = plt.subplots(1, 2, figsize=(14, 10)) # Erstellt 1x2 Raster von Subplots
gesamtkosten = ergebnisse['Gesamtkosten_Euro']