mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 08:55:15 +00:00
Apply isort and ruff code style
This commit is contained in:
parent
05a3c1a5bb
commit
a4d178d250
@ -1,26 +1,36 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
|
||||||
prediction_hours = 48
|
prediction_hours = 48
|
||||||
optimization_hours = 24
|
optimization_hours = 24
|
||||||
strafe = 10
|
strafe = 10
|
||||||
moegliche_ladestroeme_in_prozent = [0.0 ,6.0/16.0, 7.0/16.0, 8.0/16.0, 9.0/16.0, 10.0/16.0, 11.0/16.0, 12.0/16.0, 13.0/16.0, 14.0/16.0, 15.0/16.0, 1.0 ]
|
moegliche_ladestroeme_in_prozent = [
|
||||||
|
0.0,
|
||||||
|
6.0 / 16.0,
|
||||||
|
7.0 / 16.0,
|
||||||
|
8.0 / 16.0,
|
||||||
|
9.0 / 16.0,
|
||||||
|
10.0 / 16.0,
|
||||||
|
11.0 / 16.0,
|
||||||
|
12.0 / 16.0,
|
||||||
|
13.0 / 16.0,
|
||||||
|
14.0 / 16.0,
|
||||||
|
15.0 / 16.0,
|
||||||
|
1.0,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# Optional
|
# Optional
|
||||||
db_config = {
|
db_config = {"user": "eos", "password": "eos", "host": "127.0.0.1", "database": "eos"}
|
||||||
'user': 'eos',
|
|
||||||
'password': 'eos',
|
|
||||||
'host': '127.0.0.1',
|
|
||||||
'database': 'eos'
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_start_enddate(prediction_hours=48, startdate=None):
|
def get_start_enddate(prediction_hours=48, startdate=None):
|
||||||
############
|
############
|
||||||
# Parameter
|
# Parameter
|
||||||
############
|
############
|
||||||
if startdate == None:
|
if startdate == None:
|
||||||
date = (datetime.now().date() + timedelta(hours = prediction_hours)).strftime("%Y-%m-%d")
|
date = (datetime.now().date() + timedelta(hours=prediction_hours)).strftime(
|
||||||
|
"%Y-%m-%d"
|
||||||
|
)
|
||||||
date_now = datetime.now().strftime("%Y-%m-%d")
|
date_now = datetime.now().strftime("%Y-%m-%d")
|
||||||
else:
|
else:
|
||||||
date = (startdate + timedelta(hours=prediction_hours)).strftime("%Y-%m-%d")
|
date = (startdate + timedelta(hours=prediction_hours)).strftime("%Y-%m-%d")
|
||||||
|
209
flask_server.py
209
flask_server.py
@ -1,19 +1,18 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import random
|
|
||||||
|
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
import matplotlib
|
import matplotlib
|
||||||
matplotlib.use('Agg') # Sets the Matplotlib backend to 'Agg' for rendering plots in environments without a display
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import numpy as np
|
|
||||||
import pandas as pd
|
|
||||||
from deap import base, creator, tools, algorithms
|
|
||||||
from flask import Flask, jsonify, request, redirect, send_from_directory, url_for
|
|
||||||
|
|
||||||
|
matplotlib.use(
|
||||||
|
"Agg"
|
||||||
|
) # Sets the Matplotlib backend to 'Agg' for rendering plots in environments without a display
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
from config import *
|
from config import *
|
||||||
|
from flask import Flask, jsonify, redirect, request, send_from_directory, url_for
|
||||||
|
|
||||||
from modules.class_akku import *
|
from modules.class_akku import *
|
||||||
from modules.class_ems import *
|
from modules.class_ems import *
|
||||||
from modules.class_heatpump import *
|
from modules.class_heatpump import *
|
||||||
@ -26,10 +25,12 @@ from modules.class_soc_calc import *
|
|||||||
from modules.class_sommerzeit import *
|
from modules.class_sommerzeit import *
|
||||||
from modules.class_strompreis import *
|
from modules.class_strompreis import *
|
||||||
from modules.visualize import *
|
from modules.visualize import *
|
||||||
from datetime import datetime, timedelta
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
opt_class = optimization_problem(prediction_hours=prediction_hours, strafe=10, optimization_hours=optimization_hours)
|
opt_class = optimization_problem(
|
||||||
|
prediction_hours=prediction_hours, strafe=10, optimization_hours=optimization_hours
|
||||||
|
)
|
||||||
|
|
||||||
# @app.route('/last_correction', methods=['GET'])
|
# @app.route('/last_correction', methods=['GET'])
|
||||||
# def flask_last_correction():
|
# def flask_last_correction():
|
||||||
@ -53,7 +54,8 @@ opt_class = optimization_problem(prediction_hours=prediction_hours, strafe=10, o
|
|||||||
# print(last)
|
# print(last)
|
||||||
# return jsonify(last.tolist())
|
# return jsonify(last.tolist())
|
||||||
|
|
||||||
@app.route('/soc', methods=['GET'])
|
|
||||||
|
@app.route("/soc", methods=["GET"])
|
||||||
def flask_soc():
|
def flask_soc():
|
||||||
# MariaDB connection details
|
# MariaDB connection details
|
||||||
config = db_config
|
config = db_config
|
||||||
@ -66,15 +68,24 @@ def flask_soc():
|
|||||||
bat_capacity = 33 * 1000 / 48 # Battery capacity in watt-hours
|
bat_capacity = 33 * 1000 / 48 # Battery capacity in watt-hours
|
||||||
|
|
||||||
# Define the reference time point (3 weeks ago)
|
# Define the reference time point (3 weeks ago)
|
||||||
zeitpunkt_x = (datetime.now() - timedelta(weeks=3)).strftime('%Y-%m-%d %H:%M:%S')
|
zeitpunkt_x = (datetime.now() - timedelta(weeks=3)).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
# Instantiate BatteryDataProcessor and perform calculations
|
# Instantiate BatteryDataProcessor and perform calculations
|
||||||
processor = BatteryDataProcessor(config, voltage_high_threshold, voltage_low_threshold, current_low_threshold, gap, bat_capacity)
|
processor = BatteryDataProcessor(
|
||||||
|
config,
|
||||||
|
voltage_high_threshold,
|
||||||
|
voltage_low_threshold,
|
||||||
|
current_low_threshold,
|
||||||
|
gap,
|
||||||
|
bat_capacity,
|
||||||
|
)
|
||||||
processor.connect_db()
|
processor.connect_db()
|
||||||
processor.fetch_data(zeitpunkt_x)
|
processor.fetch_data(zeitpunkt_x)
|
||||||
processor.process_data()
|
processor.process_data()
|
||||||
last_points_100_df, last_points_0_df = processor.find_soc_points()
|
last_points_100_df, last_points_0_df = processor.find_soc_points()
|
||||||
soc_df, integration_results = processor.calculate_resetting_soc(last_points_100_df, last_points_0_df)
|
soc_df, integration_results = processor.calculate_resetting_soc(
|
||||||
|
last_points_100_df, last_points_0_df
|
||||||
|
)
|
||||||
# soh_df = processor.calculate_soh(integration_results) # Optional State of Health calculation
|
# soh_df = processor.calculate_soh(integration_results) # Optional State of Health calculation
|
||||||
processor.update_database_with_soc(soc_df) # Update database with SOC data
|
processor.update_database_with_soc(soc_df) # Update database with SOC data
|
||||||
# processor.plot_data(last_points_100_df, last_points_0_df, soc_df) # Optional data visualization
|
# processor.plot_data(last_points_100_df, last_points_0_df, soc_df) # Optional data visualization
|
||||||
@ -82,50 +93,65 @@ def flask_soc():
|
|||||||
|
|
||||||
return jsonify("Done")
|
return jsonify("Done")
|
||||||
|
|
||||||
@app.route('/strompreis', methods=['GET'])
|
|
||||||
|
@app.route("/strompreis", methods=["GET"])
|
||||||
def flask_strompreis():
|
def flask_strompreis():
|
||||||
# Get the current date and the end date based on prediction hours
|
# Get the current date and the end date based on prediction hours
|
||||||
date_now, date = get_start_enddate(prediction_hours, startdate=datetime.now().date())
|
date_now, date = get_start_enddate(
|
||||||
filepath = os.path.join(r'test_data', r'strompreise_akkudokAPI.json') # Adjust the path to the JSON file
|
prediction_hours, startdate=datetime.now().date()
|
||||||
price_forecast = HourlyElectricityPriceForecast(source=f"https://api.akkudoktor.net/prices?start={date_now}&end={date}", prediction_hours=prediction_hours)
|
)
|
||||||
specific_date_prices = price_forecast.get_price_for_daterange(date_now, date) # Fetch prices for the specified date range
|
filepath = os.path.join(
|
||||||
|
r"test_data", r"strompreise_akkudokAPI.json"
|
||||||
|
) # Adjust the path to the JSON file
|
||||||
|
price_forecast = HourlyElectricityPriceForecast(
|
||||||
|
source=f"https://api.akkudoktor.net/prices?start={date_now}&end={date}",
|
||||||
|
prediction_hours=prediction_hours,
|
||||||
|
)
|
||||||
|
specific_date_prices = price_forecast.get_price_for_daterange(
|
||||||
|
date_now, date
|
||||||
|
) # Fetch prices for the specified date range
|
||||||
return jsonify(specific_date_prices.tolist())
|
return jsonify(specific_date_prices.tolist())
|
||||||
|
|
||||||
|
|
||||||
# Endpoint to handle total load calculation based on the latest measured data
|
# Endpoint to handle total load calculation based on the latest measured data
|
||||||
@app.route('/gesamtlast', methods=['POST'])
|
@app.route("/gesamtlast", methods=["POST"])
|
||||||
def flask_gesamtlast():
|
def flask_gesamtlast():
|
||||||
# Retrieve data from the JSON body
|
# Retrieve data from the JSON body
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
|
|
||||||
# Extract year_energy and prediction_hours from the request JSON
|
# Extract year_energy and prediction_hours from the request JSON
|
||||||
year_energy = float(data.get("year_energy"))
|
year_energy = float(data.get("year_energy"))
|
||||||
prediction_hours = int(data.get("hours", 48)) # Default to 48 hours if not specified
|
prediction_hours = int(
|
||||||
|
data.get("hours", 48)
|
||||||
|
) # Default to 48 hours if not specified
|
||||||
|
|
||||||
# Measured data in JSON format
|
# Measured data in JSON format
|
||||||
measured_data_json = data.get("measured_data")
|
measured_data_json = data.get("measured_data")
|
||||||
measured_data = pd.DataFrame(measured_data_json)
|
measured_data = pd.DataFrame(measured_data_json)
|
||||||
measured_data['time'] = pd.to_datetime(measured_data['time'])
|
measured_data["time"] = pd.to_datetime(measured_data["time"])
|
||||||
|
|
||||||
# Ensure datetime has timezone info for accurate calculations
|
# Ensure datetime has timezone info for accurate calculations
|
||||||
if measured_data['time'].dt.tz is None:
|
if measured_data["time"].dt.tz is None:
|
||||||
measured_data['time'] = measured_data['time'].dt.tz_localize('Europe/Berlin')
|
measured_data["time"] = measured_data["time"].dt.tz_localize("Europe/Berlin")
|
||||||
else:
|
else:
|
||||||
measured_data['time'] = measured_data['time'].dt.tz_convert('Europe/Berlin')
|
measured_data["time"] = measured_data["time"].dt.tz_convert("Europe/Berlin")
|
||||||
|
|
||||||
# Remove timezone info after conversion to simplify further processing
|
# Remove timezone info after conversion to simplify further processing
|
||||||
measured_data['time'] = measured_data['time'].dt.tz_localize(None)
|
measured_data["time"] = measured_data["time"].dt.tz_localize(None)
|
||||||
|
|
||||||
# Instantiate LoadForecast and generate forecast data
|
# Instantiate LoadForecast and generate forecast data
|
||||||
lf = LoadForecast(filepath=r'load_profiles.npz', year_energy=year_energy)
|
lf = LoadForecast(filepath=r"load_profiles.npz", year_energy=year_energy)
|
||||||
forecast_list = []
|
forecast_list = []
|
||||||
|
|
||||||
# Generate daily forecasts for the date range based on measured data
|
# Generate daily forecasts for the date range based on measured data
|
||||||
for single_date in pd.date_range(measured_data['time'].min().date(), measured_data['time'].max().date()):
|
for single_date in pd.date_range(
|
||||||
date_str = single_date.strftime('%Y-%m-%d')
|
measured_data["time"].min().date(), measured_data["time"].max().date()
|
||||||
|
):
|
||||||
|
date_str = single_date.strftime("%Y-%m-%d")
|
||||||
daily_forecast = lf.get_daily_stats(date_str)
|
daily_forecast = lf.get_daily_stats(date_str)
|
||||||
mean_values = daily_forecast[0]
|
mean_values = daily_forecast[0]
|
||||||
hours = [single_date + pd.Timedelta(hours=i) for i in range(24)]
|
hours = [single_date + pd.Timedelta(hours=i) for i in range(24)]
|
||||||
daily_forecast_df = pd.DataFrame({'time': hours, 'Last Pred': mean_values})
|
daily_forecast_df = pd.DataFrame({"time": hours, "Last Pred": mean_values})
|
||||||
forecast_list.append(daily_forecast_df)
|
forecast_list.append(daily_forecast_df)
|
||||||
|
|
||||||
# Concatenate all daily forecasts into a single DataFrame
|
# Concatenate all daily forecasts into a single DataFrame
|
||||||
@ -135,17 +161,22 @@ def flask_gesamtlast():
|
|||||||
adjuster = LoadPredictionAdjuster(measured_data, predicted_data, lf)
|
adjuster = LoadPredictionAdjuster(measured_data, predicted_data, lf)
|
||||||
adjuster.calculate_weighted_mean() # Calculate weighted mean for adjustment
|
adjuster.calculate_weighted_mean() # Calculate weighted mean for adjustment
|
||||||
adjuster.adjust_predictions() # Adjust predictions based on measured data
|
adjuster.adjust_predictions() # Adjust predictions based on measured data
|
||||||
future_predictions = adjuster.predict_next_hours(prediction_hours) # Predict future load
|
future_predictions = adjuster.predict_next_hours(
|
||||||
|
prediction_hours
|
||||||
|
) # Predict future load
|
||||||
|
|
||||||
# Extract household power predictions
|
# Extract household power predictions
|
||||||
leistung_haushalt = future_predictions['Adjusted Pred'].values
|
leistung_haushalt = future_predictions["Adjusted Pred"].values
|
||||||
gesamtlast = Gesamtlast(prediction_hours=prediction_hours)
|
gesamtlast = Gesamtlast(prediction_hours=prediction_hours)
|
||||||
gesamtlast.hinzufuegen("Haushalt", leistung_haushalt) # Add household load to total load calculation
|
gesamtlast.hinzufuegen(
|
||||||
|
"Haushalt", leistung_haushalt
|
||||||
|
) # Add household load to total load calculation
|
||||||
|
|
||||||
# Calculate the total load
|
# Calculate the total load
|
||||||
last = gesamtlast.gesamtlast_berechnen() # Compute total load
|
last = gesamtlast.gesamtlast_berechnen() # Compute total load
|
||||||
return jsonify(last.tolist())
|
return jsonify(last.tolist())
|
||||||
|
|
||||||
|
|
||||||
# @app.route('/gesamtlast', methods=['GET'])
|
# @app.route('/gesamtlast', methods=['GET'])
|
||||||
# def flask_gesamtlast():
|
# def flask_gesamtlast():
|
||||||
# if request.method == 'GET':
|
# if request.method == 'GET':
|
||||||
@ -207,20 +238,33 @@ def flask_gesamtlast():
|
|||||||
# print(last) # Output total load
|
# print(last) # Output total load
|
||||||
# return jsonify(last.tolist()) # Return total load as JSON
|
# return jsonify(last.tolist()) # Return total load as JSON
|
||||||
|
|
||||||
@app.route('/gesamtlast_simple', methods=['GET'])
|
|
||||||
|
@app.route("/gesamtlast_simple", methods=["GET"])
|
||||||
def flask_gesamtlast_simple():
|
def flask_gesamtlast_simple():
|
||||||
if request.method == 'GET':
|
if request.method == "GET":
|
||||||
year_energy = float(request.args.get("year_energy")) # Get annual energy value from query parameters
|
year_energy = float(
|
||||||
date_now, date = get_start_enddate(prediction_hours, startdate=datetime.now().date()) # Get the current date and prediction end date
|
request.args.get("year_energy")
|
||||||
|
) # Get annual energy value from query parameters
|
||||||
|
date_now, date = get_start_enddate(
|
||||||
|
prediction_hours, startdate=datetime.now().date()
|
||||||
|
) # Get the current date and prediction end date
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Load Forecast
|
# Load Forecast
|
||||||
###############
|
###############
|
||||||
lf = LoadForecast(filepath=r'load_profiles.npz', year_energy=year_energy) # Instantiate LoadForecast with specified parameters
|
lf = LoadForecast(
|
||||||
leistung_haushalt = lf.get_stats_for_date_range(date_now, date)[0] # Get expected household load for the date range
|
filepath=r"load_profiles.npz", year_energy=year_energy
|
||||||
|
) # Instantiate LoadForecast with specified parameters
|
||||||
|
leistung_haushalt = lf.get_stats_for_date_range(date_now, date)[
|
||||||
|
0
|
||||||
|
] # Get expected household load for the date range
|
||||||
|
|
||||||
gesamtlast = Gesamtlast(prediction_hours=prediction_hours) # Create Gesamtlast instance
|
gesamtlast = Gesamtlast(
|
||||||
gesamtlast.hinzufuegen("Haushalt", leistung_haushalt) # Add household load to total load calculation
|
prediction_hours=prediction_hours
|
||||||
|
) # Create Gesamtlast instance
|
||||||
|
gesamtlast.hinzufuegen(
|
||||||
|
"Haushalt", leistung_haushalt
|
||||||
|
) # Add household load to total load calculation
|
||||||
|
|
||||||
# ###############
|
# ###############
|
||||||
# # WP (Heat Pump)
|
# # WP (Heat Pump)
|
||||||
@ -233,56 +277,91 @@ def flask_gesamtlast_simple():
|
|||||||
return jsonify(last.tolist()) # Return total load as JSON
|
return jsonify(last.tolist()) # Return total load as JSON
|
||||||
|
|
||||||
|
|
||||||
@app.route('/pvforecast', methods=['GET'])
|
@app.route("/pvforecast", methods=["GET"])
|
||||||
def flask_pvprognose():
|
def flask_pvprognose():
|
||||||
if request.method == 'GET':
|
if request.method == "GET":
|
||||||
# Retrieve URL and AC power measurement from query parameters
|
# Retrieve URL and AC power measurement from query parameters
|
||||||
url = request.args.get("url")
|
url = request.args.get("url")
|
||||||
ac_power_measurement = request.args.get("ac_power_measurement")
|
ac_power_measurement = request.args.get("ac_power_measurement")
|
||||||
date_now, date = get_start_enddate(prediction_hours, startdate=datetime.now().date())
|
date_now, date = get_start_enddate(
|
||||||
|
prediction_hours, startdate=datetime.now().date()
|
||||||
|
)
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# PV Forecast
|
# PV Forecast
|
||||||
###############
|
###############
|
||||||
PVforecast = PVForecast(prediction_hours=prediction_hours, url=url) # Instantiate PVForecast with given parameters
|
PVforecast = PVForecast(
|
||||||
if isfloat(ac_power_measurement): # Check if the AC power measurement is a valid float
|
prediction_hours=prediction_hours, url=url
|
||||||
PVforecast.update_ac_power_measurement(date_time=datetime.now(), ac_power_measurement=float(ac_power_measurement)) # Update measurement
|
) # Instantiate PVForecast with given parameters
|
||||||
|
if isfloat(
|
||||||
|
ac_power_measurement
|
||||||
|
): # Check if the AC power measurement is a valid float
|
||||||
|
PVforecast.update_ac_power_measurement(
|
||||||
|
date_time=datetime.now(),
|
||||||
|
ac_power_measurement=float(ac_power_measurement),
|
||||||
|
) # Update measurement
|
||||||
|
|
||||||
# Get PV forecast and temperature forecast for the specified date range
|
# Get PV forecast and temperature forecast for the specified date range
|
||||||
pv_forecast = PVforecast.get_pv_forecast_for_date_range(date_now, date)
|
pv_forecast = PVforecast.get_pv_forecast_for_date_range(date_now, date)
|
||||||
temperature_forecast = PVforecast.get_temperature_for_date_range(date_now, date)
|
temperature_forecast = PVforecast.get_temperature_for_date_range(date_now, date)
|
||||||
|
|
||||||
# Return both forecasts as a JSON response
|
# Return both forecasts as a JSON response
|
||||||
ret = {"temperature": temperature_forecast.tolist(), "pvpower": pv_forecast.tolist()}
|
ret = {
|
||||||
|
"temperature": temperature_forecast.tolist(),
|
||||||
|
"pvpower": pv_forecast.tolist(),
|
||||||
|
}
|
||||||
return jsonify(ret)
|
return jsonify(ret)
|
||||||
|
|
||||||
@app.route('/optimize', methods=['POST'])
|
|
||||||
|
@app.route("/optimize", methods=["POST"])
|
||||||
def flask_optimize():
|
def flask_optimize():
|
||||||
if request.method == 'POST':
|
if request.method == "POST":
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# Retrieve optimization parameters from the request JSON
|
# Retrieve optimization parameters from the request JSON
|
||||||
parameter = request.json
|
parameter = request.json
|
||||||
|
|
||||||
# Check for required parameters
|
# Check for required parameters
|
||||||
required_parameters = ['preis_euro_pro_wh_akku', 'strompreis_euro_pro_wh', "gesamtlast", 'pv_akku_cap',
|
required_parameters = [
|
||||||
"einspeiseverguetung_euro_pro_wh", 'pv_forecast', 'temperature_forecast',
|
"preis_euro_pro_wh_akku",
|
||||||
'eauto_min_soc', "eauto_cap", "eauto_charge_efficiency", "eauto_charge_power",
|
"strompreis_euro_pro_wh",
|
||||||
"eauto_soc", "pv_soc", "start_solution", "haushaltsgeraet_dauer",
|
"gesamtlast",
|
||||||
"haushaltsgeraet_wh"]
|
"pv_akku_cap",
|
||||||
|
"einspeiseverguetung_euro_pro_wh",
|
||||||
|
"pv_forecast",
|
||||||
|
"temperature_forecast",
|
||||||
|
"eauto_min_soc",
|
||||||
|
"eauto_cap",
|
||||||
|
"eauto_charge_efficiency",
|
||||||
|
"eauto_charge_power",
|
||||||
|
"eauto_soc",
|
||||||
|
"pv_soc",
|
||||||
|
"start_solution",
|
||||||
|
"haushaltsgeraet_dauer",
|
||||||
|
"haushaltsgeraet_wh",
|
||||||
|
]
|
||||||
# Identify any missing parameters
|
# Identify any missing parameters
|
||||||
missing_params = [p for p in required_parameters if p not in parameter]
|
missing_params = [p for p in required_parameters if p not in parameter]
|
||||||
if missing_params:
|
if missing_params:
|
||||||
return jsonify({"error": f"Missing parameter: {', '.join(missing_params)}"}), 400 # Return error for missing parameters
|
return jsonify(
|
||||||
|
{"error": f"Missing parameter: {', '.join(missing_params)}"}
|
||||||
|
), 400 # Return error for missing parameters
|
||||||
|
|
||||||
# Perform optimization simulation
|
# Perform optimization simulation
|
||||||
result = opt_class.optimierung_ems(parameter=parameter, start_hour=datetime.now().hour)
|
result = opt_class.optimierung_ems(
|
||||||
|
parameter=parameter, start_hour=datetime.now().hour
|
||||||
|
)
|
||||||
|
|
||||||
return jsonify(result) # Return optimization results as JSON
|
return jsonify(result) # Return optimization results as JSON
|
||||||
|
|
||||||
@app.route('/visualisierungsergebnisse.pdf')
|
|
||||||
|
@app.route("/visualisierungsergebnisse.pdf")
|
||||||
def get_pdf():
|
def get_pdf():
|
||||||
# Endpoint to serve the generated PDF with visualization results
|
# Endpoint to serve the generated PDF with visualization results
|
||||||
return send_from_directory('', 'visualisierungsergebnisse.pdf') # Adjust the directory if needed
|
return send_from_directory(
|
||||||
|
"", "visualisierungsergebnisse.pdf"
|
||||||
|
) # Adjust the directory if needed
|
||||||
|
|
||||||
|
|
||||||
@app.route("/site-map")
|
@app.route("/site-map")
|
||||||
def site_map():
|
def site_map():
|
||||||
@ -299,6 +378,7 @@ def site_map():
|
|||||||
defaults = rule.defaults if rule.defaults is not None else ()
|
defaults = rule.defaults if rule.defaults is not None else ()
|
||||||
arguments = rule.arguments if rule.arguments is not None else ()
|
arguments = rule.arguments if rule.arguments is not None else ()
|
||||||
return len(defaults) >= len(arguments)
|
return len(defaults) >= len(arguments)
|
||||||
|
|
||||||
# Collect all valid GET routes without empty parameters
|
# Collect all valid GET routes without empty parameters
|
||||||
links = []
|
links = []
|
||||||
for rule in app.url_map.iter_rules():
|
for rule in app.url_map.iter_rules():
|
||||||
@ -308,19 +388,22 @@ def site_map():
|
|||||||
return print_links(sorted(links)) # Return the sorted links as HTML
|
return print_links(sorted(links)) # Return the sorted links as HTML
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route("/")
|
||||||
def root():
|
def root():
|
||||||
# Redirect the root URL to the site map
|
# Redirect the root URL to the site map
|
||||||
return redirect("/site-map", code=302)
|
return redirect("/site-map", code=302)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
# Set host and port from environment variables or defaults
|
# Set host and port from environment variables or defaults
|
||||||
host = os.getenv("FLASK_RUN_HOST", "0.0.0.0")
|
host = os.getenv("FLASK_RUN_HOST", "0.0.0.0")
|
||||||
port = os.getenv("FLASK_RUN_PORT", 8503)
|
port = os.getenv("FLASK_RUN_PORT", 8503)
|
||||||
app.run(debug=True, host=host, port=port) # Run the Flask application
|
app.run(debug=True, host=host, port=port) # Run the Flask application
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Could not bind to host {host}:{port}. Error: {e}") # Error handling for binding issues
|
print(
|
||||||
|
f"Could not bind to host {host}:{port}. Error: {e}"
|
||||||
|
) # Error handling for binding issues
|
||||||
|
|
||||||
# PV Forecast:
|
# PV Forecast:
|
||||||
# object {
|
# object {
|
||||||
|
@ -1,27 +1,40 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
class PVAkku:
|
class PVAkku:
|
||||||
def __init__(self, kapazitaet_wh=None, hours=None, lade_effizienz=0.88, entlade_effizienz=0.88,
|
def __init__(
|
||||||
max_ladeleistung_w=None, start_soc_prozent=0, min_soc_prozent=0, max_soc_prozent=100):
|
self,
|
||||||
|
kapazitaet_wh=None,
|
||||||
|
hours=None,
|
||||||
|
lade_effizienz=0.88,
|
||||||
|
entlade_effizienz=0.88,
|
||||||
|
max_ladeleistung_w=None,
|
||||||
|
start_soc_prozent=0,
|
||||||
|
min_soc_prozent=0,
|
||||||
|
max_soc_prozent=100,
|
||||||
|
):
|
||||||
# Battery capacity in Wh
|
# Battery capacity in Wh
|
||||||
self.kapazitaet_wh = kapazitaet_wh
|
self.kapazitaet_wh = kapazitaet_wh
|
||||||
# Initial state of charge in Wh
|
# Initial state of charge in Wh
|
||||||
self.start_soc_prozent = start_soc_prozent
|
self.start_soc_prozent = start_soc_prozent
|
||||||
self.soc_wh = (start_soc_prozent / 100) * kapazitaet_wh
|
self.soc_wh = (start_soc_prozent / 100) * kapazitaet_wh
|
||||||
self.hours = hours if hours is not None else 24 # Default to 24 hours if not specified
|
self.hours = (
|
||||||
|
hours if hours is not None else 24
|
||||||
|
) # Default to 24 hours if not specified
|
||||||
self.discharge_array = np.full(self.hours, 1)
|
self.discharge_array = np.full(self.hours, 1)
|
||||||
self.charge_array = np.full(self.hours, 1)
|
self.charge_array = np.full(self.hours, 1)
|
||||||
# Charge and discharge efficiency
|
# Charge and discharge efficiency
|
||||||
self.lade_effizienz = lade_effizienz
|
self.lade_effizienz = lade_effizienz
|
||||||
self.entlade_effizienz = entlade_effizienz
|
self.entlade_effizienz = entlade_effizienz
|
||||||
self.max_ladeleistung_w = max_ladeleistung_w if max_ladeleistung_w else self.kapazitaet_wh
|
self.max_ladeleistung_w = (
|
||||||
|
max_ladeleistung_w if max_ladeleistung_w else self.kapazitaet_wh
|
||||||
|
)
|
||||||
self.min_soc_prozent = min_soc_prozent
|
self.min_soc_prozent = min_soc_prozent
|
||||||
self.max_soc_prozent = max_soc_prozent
|
self.max_soc_prozent = max_soc_prozent
|
||||||
# Calculate min and max SoC in Wh
|
# Calculate min and max SoC in Wh
|
||||||
self.min_soc_wh = (self.min_soc_prozent / 100) * self.kapazitaet_wh
|
self.min_soc_wh = (self.min_soc_prozent / 100) * self.kapazitaet_wh
|
||||||
self.max_soc_wh = (self.max_soc_prozent / 100) * self.kapazitaet_wh
|
self.max_soc_wh = (self.max_soc_prozent / 100) * self.kapazitaet_wh
|
||||||
|
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
return {
|
||||||
"kapazitaet_wh": self.kapazitaet_wh,
|
"kapazitaet_wh": self.kapazitaet_wh,
|
||||||
@ -32,7 +45,7 @@ class PVAkku:
|
|||||||
"charge_array": self.charge_array.tolist(),
|
"charge_array": self.charge_array.tolist(),
|
||||||
"lade_effizienz": self.lade_effizienz,
|
"lade_effizienz": self.lade_effizienz,
|
||||||
"entlade_effizienz": self.entlade_effizienz,
|
"entlade_effizienz": self.entlade_effizienz,
|
||||||
"max_ladeleistung_w": self.max_ladeleistung_w
|
"max_ladeleistung_w": self.max_ladeleistung_w,
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -44,12 +57,14 @@ class PVAkku:
|
|||||||
lade_effizienz=data["lade_effizienz"],
|
lade_effizienz=data["lade_effizienz"],
|
||||||
entlade_effizienz=data["entlade_effizienz"],
|
entlade_effizienz=data["entlade_effizienz"],
|
||||||
max_ladeleistung_w=data["max_ladeleistung_w"],
|
max_ladeleistung_w=data["max_ladeleistung_w"],
|
||||||
start_soc_prozent=data["start_soc_prozent"]
|
start_soc_prozent=data["start_soc_prozent"],
|
||||||
)
|
)
|
||||||
# Set arrays
|
# Set arrays
|
||||||
obj.discharge_array = np.array(data["discharge_array"])
|
obj.discharge_array = np.array(data["discharge_array"])
|
||||||
obj.charge_array = np.array(data["charge_array"])
|
obj.charge_array = np.array(data["charge_array"])
|
||||||
obj.soc_wh = data["soc_wh"] # Set current state of charge, which may differ from start_soc_prozent
|
obj.soc_wh = data[
|
||||||
|
"soc_wh"
|
||||||
|
] # Set current state of charge, which may differ from start_soc_prozent
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@ -77,8 +92,12 @@ class PVAkku:
|
|||||||
return 0.0, 0.0 # No energy discharge and no losses
|
return 0.0, 0.0 # No energy discharge and no losses
|
||||||
|
|
||||||
# Calculate the maximum energy that can be discharged considering min_soc and efficiency
|
# Calculate the maximum energy that can be discharged considering min_soc and efficiency
|
||||||
max_possible_discharge_wh = (self.soc_wh - self.min_soc_wh) * self.entlade_effizienz
|
max_possible_discharge_wh = (
|
||||||
max_possible_discharge_wh = max(max_possible_discharge_wh, 0.0) # Ensure non-negative
|
self.soc_wh - self.min_soc_wh
|
||||||
|
) * self.entlade_effizienz
|
||||||
|
max_possible_discharge_wh = max(
|
||||||
|
max_possible_discharge_wh, 0.0
|
||||||
|
) # Ensure non-negative
|
||||||
|
|
||||||
# Consider the maximum discharge power of the battery
|
# Consider the maximum discharge power of the battery
|
||||||
max_abgebbar_wh = min(max_possible_discharge_wh, self.max_ladeleistung_w)
|
max_abgebbar_wh = min(max_possible_discharge_wh, self.max_ladeleistung_w)
|
||||||
@ -88,7 +107,9 @@ class PVAkku:
|
|||||||
|
|
||||||
# Calculate the actual amount withdrawn from the battery (before efficiency loss)
|
# Calculate the actual amount withdrawn from the battery (before efficiency loss)
|
||||||
if self.entlade_effizienz > 0:
|
if self.entlade_effizienz > 0:
|
||||||
tatsaechliche_entnahme_wh = tatsaechlich_abgegeben_wh / self.entlade_effizienz
|
tatsaechliche_entnahme_wh = (
|
||||||
|
tatsaechlich_abgegeben_wh / self.entlade_effizienz
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
tatsaechliche_entnahme_wh = 0.0
|
tatsaechliche_entnahme_wh = 0.0
|
||||||
|
|
||||||
@ -103,7 +124,6 @@ class PVAkku:
|
|||||||
# Return the actually discharged energy and the losses
|
# Return the actually discharged energy and the losses
|
||||||
return tatsaechlich_abgegeben_wh, verluste_wh
|
return tatsaechlich_abgegeben_wh, verluste_wh
|
||||||
|
|
||||||
|
|
||||||
def energie_laden(self, wh, hour):
|
def energie_laden(self, wh, hour):
|
||||||
if hour is not None and self.charge_array[hour] == 0:
|
if hour is not None and self.charge_array[hour] == 0:
|
||||||
return 0, 0 # Charging not allowed in this hour
|
return 0, 0 # Charging not allowed in this hour
|
||||||
@ -117,7 +137,9 @@ class PVAkku:
|
|||||||
|
|
||||||
# Calculate the maximum energy that can be charged considering max_soc and efficiency
|
# Calculate the maximum energy that can be charged considering max_soc and efficiency
|
||||||
if self.lade_effizienz > 0:
|
if self.lade_effizienz > 0:
|
||||||
max_possible_charge_wh = (self.max_soc_wh - self.soc_wh) / self.lade_effizienz
|
max_possible_charge_wh = (
|
||||||
|
self.max_soc_wh - self.soc_wh
|
||||||
|
) / self.lade_effizienz
|
||||||
else:
|
else:
|
||||||
max_possible_charge_wh = 0.0
|
max_possible_charge_wh = 0.0
|
||||||
max_possible_charge_wh = max(max_possible_charge_wh, 0.0) # Ensure non-negative
|
max_possible_charge_wh = max(max_possible_charge_wh, 0.0) # Ensure non-negative
|
||||||
@ -138,7 +160,6 @@ class PVAkku:
|
|||||||
|
|
||||||
return geladene_menge, verluste_wh
|
return geladene_menge, verluste_wh
|
||||||
|
|
||||||
|
|
||||||
def aktueller_energieinhalt(self):
|
def aktueller_energieinhalt(self):
|
||||||
"""
|
"""
|
||||||
This method returns the current remaining energy considering efficiency.
|
This method returns the current remaining energy considering efficiency.
|
||||||
@ -149,11 +170,16 @@ class PVAkku:
|
|||||||
return max(nutzbare_energie, 0.0)
|
return max(nutzbare_energie, 0.0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
if __name__ == '__main__':
|
|
||||||
# Test battery discharge below min_soc
|
# Test battery discharge below min_soc
|
||||||
print("Test: Discharge below min_soc")
|
print("Test: Discharge below min_soc")
|
||||||
akku = PVAkku(kapazitaet_wh=10000, hours=1, start_soc_prozent=50, min_soc_prozent=20, max_soc_prozent=80)
|
akku = PVAkku(
|
||||||
|
kapazitaet_wh=10000,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=50,
|
||||||
|
min_soc_prozent=20,
|
||||||
|
max_soc_prozent=80,
|
||||||
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
||||||
|
|
||||||
@ -165,7 +191,13 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# Test battery charge above max_soc
|
# Test battery charge above max_soc
|
||||||
print("\nTest: Charge above max_soc")
|
print("\nTest: Charge above max_soc")
|
||||||
akku = PVAkku(kapazitaet_wh=10000, hours=1, start_soc_prozent=50, min_soc_prozent=20, max_soc_prozent=80)
|
akku = PVAkku(
|
||||||
|
kapazitaet_wh=10000,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=50,
|
||||||
|
min_soc_prozent=20,
|
||||||
|
max_soc_prozent=80,
|
||||||
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
||||||
|
|
||||||
@ -177,7 +209,13 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# Test charging when battery is at max_soc
|
# Test charging when battery is at max_soc
|
||||||
print("\nTest: Charging when at max_soc")
|
print("\nTest: Charging when at max_soc")
|
||||||
akku = PVAkku(kapazitaet_wh=10000, hours=1, start_soc_prozent=80, min_soc_prozent=20, max_soc_prozent=80)
|
akku = PVAkku(
|
||||||
|
kapazitaet_wh=10000,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=80,
|
||||||
|
min_soc_prozent=20,
|
||||||
|
max_soc_prozent=80,
|
||||||
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
||||||
|
|
||||||
@ -187,7 +225,13 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# Test discharging when battery is at min_soc
|
# Test discharging when battery is at min_soc
|
||||||
print("\nTest: Discharging when at min_soc")
|
print("\nTest: Discharging when at min_soc")
|
||||||
akku = PVAkku(kapazitaet_wh=10000, hours=1, start_soc_prozent=20, min_soc_prozent=20, max_soc_prozent=80)
|
akku = PVAkku(
|
||||||
|
kapazitaet_wh=10000,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=20,
|
||||||
|
min_soc_prozent=20,
|
||||||
|
max_soc_prozent=80,
|
||||||
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pprint import pprint
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import modules.class_akku as PVAkku
|
|
||||||
|
|
||||||
def replace_nan_with_none(data):
|
def replace_nan_with_none(data):
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
@ -18,22 +18,31 @@ def replace_nan_with_none(data):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class EnergieManagementSystem:
|
class EnergieManagementSystem:
|
||||||
def __init__(self, pv_prognose_wh=None, strompreis_euro_pro_wh=None, einspeiseverguetung_euro_pro_wh=None, eauto=None, gesamtlast=None, haushaltsgeraet=None, wechselrichter=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
pv_prognose_wh=None,
|
||||||
|
strompreis_euro_pro_wh=None,
|
||||||
|
einspeiseverguetung_euro_pro_wh=None,
|
||||||
|
eauto=None,
|
||||||
|
gesamtlast=None,
|
||||||
|
haushaltsgeraet=None,
|
||||||
|
wechselrichter=None,
|
||||||
|
):
|
||||||
self.akku = wechselrichter.akku
|
self.akku = wechselrichter.akku
|
||||||
# self.lastkurve_wh = lastkurve_wh
|
# self.lastkurve_wh = lastkurve_wh
|
||||||
self.gesamtlast = gesamtlast
|
self.gesamtlast = gesamtlast
|
||||||
self.pv_prognose_wh = pv_prognose_wh
|
self.pv_prognose_wh = pv_prognose_wh
|
||||||
self.strompreis_euro_pro_wh = strompreis_euro_pro_wh # Strompreis in Cent pro Wh
|
self.strompreis_euro_pro_wh = (
|
||||||
self.einspeiseverguetung_euro_pro_wh = einspeiseverguetung_euro_pro_wh # Einspeisevergütung in Cent 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
|
self.eauto = eauto
|
||||||
self.haushaltsgeraet = haushaltsgeraet
|
self.haushaltsgeraet = haushaltsgeraet
|
||||||
self.wechselrichter = wechselrichter
|
self.wechselrichter = wechselrichter
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def set_akku_discharge_hours(self, ds):
|
def set_akku_discharge_hours(self, ds):
|
||||||
self.akku.set_discharge_per_hour(ds)
|
self.akku.set_discharge_per_hour(ds)
|
||||||
|
|
||||||
@ -58,17 +67,20 @@ class EnergieManagementSystem:
|
|||||||
# Beginne die Simulation ab der aktuellen Stunde und führe sie für die berechnete Dauer aus
|
# Beginne die Simulation ab der aktuellen Stunde und führe sie für die berechnete Dauer aus
|
||||||
return self.simuliere(start_stunde)
|
return self.simuliere(start_stunde)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def simuliere(self, start_stunde):
|
def simuliere(self, start_stunde):
|
||||||
|
|
||||||
|
|
||||||
lastkurve_wh = self.gesamtlast
|
lastkurve_wh = self.gesamtlast
|
||||||
# Anzahl der Stunden berechnen
|
# Anzahl der Stunden berechnen
|
||||||
assert len(lastkurve_wh) == len(self.pv_prognose_wh) == len(self.strompreis_euro_pro_wh), f"Arraygrößen stimmen nicht überein: Lastkurve = {len(lastkurve_wh)}, PV-Prognose = {len(self.pv_prognose_wh)}, Strompreis = {len(self.strompreis_euro_pro_wh)}"
|
assert (
|
||||||
|
len(lastkurve_wh)
|
||||||
ende = min( len(lastkurve_wh),len(self.pv_prognose_wh), len(self.strompreis_euro_pro_wh))
|
== len(self.pv_prognose_wh)
|
||||||
|
== len(self.strompreis_euro_pro_wh)
|
||||||
|
), f"Arraygrößen stimmen nicht überein: Lastkurve = {len(lastkurve_wh)}, PV-Prognose = {len(self.pv_prognose_wh)}, Strompreis = {len(self.strompreis_euro_pro_wh)}"
|
||||||
|
|
||||||
|
ende = min(
|
||||||
|
len(lastkurve_wh),
|
||||||
|
len(self.pv_prognose_wh),
|
||||||
|
len(self.strompreis_euro_pro_wh),
|
||||||
|
)
|
||||||
|
|
||||||
total_hours = ende - start_stunde
|
total_hours = ende - start_stunde
|
||||||
|
|
||||||
@ -88,13 +100,14 @@ class EnergieManagementSystem:
|
|||||||
if self.eauto:
|
if self.eauto:
|
||||||
eauto_soc_pro_stunde[start_stunde] = self.eauto.ladezustand_in_prozent()
|
eauto_soc_pro_stunde[start_stunde] = self.eauto.ladezustand_in_prozent()
|
||||||
|
|
||||||
|
|
||||||
for stunde in range(start_stunde + 1, ende):
|
for stunde in range(start_stunde + 1, ende):
|
||||||
stunde_since_now = stunde - start_stunde
|
stunde_since_now = stunde - start_stunde
|
||||||
# print(stunde_since_now)
|
# print(stunde_since_now)
|
||||||
# Anfangszustände
|
# Anfangszustände
|
||||||
akku_soc_start = self.akku.ladezustand_in_prozent()
|
akku_soc_start = self.akku.ladezustand_in_prozent()
|
||||||
eauto_soc_start = self.eauto.ladezustand_in_prozent() if self.eauto else None
|
eauto_soc_start = (
|
||||||
|
self.eauto.ladezustand_in_prozent() if self.eauto else None
|
||||||
|
)
|
||||||
|
|
||||||
# Verbrauch und zusätzliche Lasten bestimmen
|
# Verbrauch und zusätzliche Lasten bestimmen
|
||||||
verbrauch = self.gesamtlast[stunde]
|
verbrauch = self.gesamtlast[stunde]
|
||||||
@ -115,13 +128,19 @@ class EnergieManagementSystem:
|
|||||||
|
|
||||||
# E-Auto-Verbrauch bestimmen
|
# E-Auto-Verbrauch bestimmen
|
||||||
if self.eauto:
|
if self.eauto:
|
||||||
geladene_menge_eauto, verluste_eauto = self.eauto.energie_laden(None, stunde)
|
geladene_menge_eauto, verluste_eauto = self.eauto.energie_laden(
|
||||||
|
None, stunde
|
||||||
|
)
|
||||||
verbrauch += geladene_menge_eauto
|
verbrauch += geladene_menge_eauto
|
||||||
verluste_wh_pro_stunde[stunde_since_now] += verluste_eauto
|
verluste_wh_pro_stunde[stunde_since_now] += verluste_eauto
|
||||||
eauto_soc_pro_stunde[stunde_since_now] = self.eauto.ladezustand_in_prozent()
|
eauto_soc_pro_stunde[stunde_since_now] = (
|
||||||
|
self.eauto.ladezustand_in_prozent()
|
||||||
|
)
|
||||||
|
|
||||||
# Wechselrichter-Logik
|
# Wechselrichter-Logik
|
||||||
netzeinspeisung, netzbezug, verluste, eigenverbrauch = self.wechselrichter.energie_verarbeiten(erzeugung, verbrauch, stunde)
|
netzeinspeisung, netzbezug, verluste, eigenverbrauch = (
|
||||||
|
self.wechselrichter.energie_verarbeiten(erzeugung, verbrauch, stunde)
|
||||||
|
)
|
||||||
|
|
||||||
# Ergebnisse speichern
|
# Ergebnisse speichern
|
||||||
netzeinspeisung_wh_pro_stunde[stunde_since_now] = netzeinspeisung
|
netzeinspeisung_wh_pro_stunde[stunde_since_now] = netzeinspeisung
|
||||||
@ -130,30 +149,33 @@ class EnergieManagementSystem:
|
|||||||
last_wh_pro_stunde[stunde_since_now] = verbrauch
|
last_wh_pro_stunde[stunde_since_now] = verbrauch
|
||||||
# Finanzen berechnen
|
# Finanzen berechnen
|
||||||
kosten_euro_pro_stunde[stunde_since_now] = netzbezug * strompreis
|
kosten_euro_pro_stunde[stunde_since_now] = netzbezug * strompreis
|
||||||
einnahmen_euro_pro_stunde[stunde_since_now] = netzeinspeisung * self.einspeiseverguetung_euro_pro_wh[stunde]
|
einnahmen_euro_pro_stunde[stunde_since_now] = (
|
||||||
|
netzeinspeisung * self.einspeiseverguetung_euro_pro_wh[stunde]
|
||||||
|
)
|
||||||
|
|
||||||
# Letzter Akkuzustand speichern
|
# Letzter Akkuzustand speichern
|
||||||
akku_soc_pro_stunde[stunde_since_now] = self.akku.ladezustand_in_prozent()
|
akku_soc_pro_stunde[stunde_since_now] = self.akku.ladezustand_in_prozent()
|
||||||
|
|
||||||
# Gesamtkosten berechnen
|
# Gesamtkosten berechnen
|
||||||
gesamtkosten_euro = np.nansum(kosten_euro_pro_stunde) - np.nansum(einnahmen_euro_pro_stunde)
|
gesamtkosten_euro = np.nansum(kosten_euro_pro_stunde) - np.nansum(
|
||||||
|
einnahmen_euro_pro_stunde
|
||||||
|
)
|
||||||
|
|
||||||
out = {
|
out = {
|
||||||
'Last_Wh_pro_Stunde': last_wh_pro_stunde,
|
"Last_Wh_pro_Stunde": last_wh_pro_stunde,
|
||||||
'Netzeinspeisung_Wh_pro_Stunde': netzeinspeisung_wh_pro_stunde,
|
"Netzeinspeisung_Wh_pro_Stunde": netzeinspeisung_wh_pro_stunde,
|
||||||
'Netzbezug_Wh_pro_Stunde': netzbezug_wh_pro_stunde,
|
"Netzbezug_Wh_pro_Stunde": netzbezug_wh_pro_stunde,
|
||||||
'Kosten_Euro_pro_Stunde': kosten_euro_pro_stunde,
|
"Kosten_Euro_pro_Stunde": kosten_euro_pro_stunde,
|
||||||
'akku_soc_pro_stunde': akku_soc_pro_stunde,
|
"akku_soc_pro_stunde": akku_soc_pro_stunde,
|
||||||
'Einnahmen_Euro_pro_Stunde': einnahmen_euro_pro_stunde,
|
"Einnahmen_Euro_pro_Stunde": einnahmen_euro_pro_stunde,
|
||||||
'Gesamtbilanz_Euro': gesamtkosten_euro,
|
"Gesamtbilanz_Euro": gesamtkosten_euro,
|
||||||
'E-Auto_SoC_pro_Stunde': eauto_soc_pro_stunde,
|
"E-Auto_SoC_pro_Stunde": eauto_soc_pro_stunde,
|
||||||
'Gesamteinnahmen_Euro': np.nansum(einnahmen_euro_pro_stunde),
|
"Gesamteinnahmen_Euro": np.nansum(einnahmen_euro_pro_stunde),
|
||||||
'Gesamtkosten_Euro': np.nansum(kosten_euro_pro_stunde),
|
"Gesamtkosten_Euro": np.nansum(kosten_euro_pro_stunde),
|
||||||
"Verluste_Pro_Stunde": verluste_wh_pro_stunde,
|
"Verluste_Pro_Stunde": verluste_wh_pro_stunde,
|
||||||
"Gesamt_Verluste": np.nansum(verluste_wh_pro_stunde),
|
"Gesamt_Verluste": np.nansum(verluste_wh_pro_stunde),
|
||||||
"Haushaltsgeraet_wh_pro_stunde": haushaltsgeraet_wh_pro_stunde
|
"Haushaltsgeraet_wh_pro_stunde": haushaltsgeraet_wh_pro_stunde,
|
||||||
}
|
}
|
||||||
|
|
||||||
out = replace_nan_with_none(out)
|
out = replace_nan_with_none(out)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
class Haushaltsgeraet:
|
class Haushaltsgeraet:
|
||||||
def __init__(self, hours=None, verbrauch_kwh=None, dauer_h=None):
|
def __init__(self, hours=None, verbrauch_kwh=None, dauer_h=None):
|
||||||
self.hours = hours # Total duration for which the planning is done
|
self.hours = hours # Total duration for which the planning is done
|
||||||
self.verbrauch_kwh = verbrauch_kwh # Total energy consumption of the device in kWh
|
self.verbrauch_kwh = (
|
||||||
|
verbrauch_kwh # Total energy consumption of the device in kWh
|
||||||
|
)
|
||||||
self.dauer_h = dauer_h # Duration of use in hours
|
self.dauer_h = dauer_h # Duration of use in hours
|
||||||
self.lastkurve = np.zeros(self.hours) # Initialize the load curve with zeros
|
self.lastkurve = np.zeros(self.hours) # Initialize the load curve with zeros
|
||||||
|
|
||||||
@ -21,7 +24,7 @@ class Haushaltsgeraet:
|
|||||||
raise ValueError("The start time is earlier than the available time frame.")
|
raise ValueError("The start time is earlier than the available time frame.")
|
||||||
|
|
||||||
# Calculate power per hour based on total consumption and duration
|
# Calculate power per hour based on total consumption and duration
|
||||||
leistung_pro_stunde = (self.verbrauch_kwh / self.dauer_h) # Convert to watt-hours
|
leistung_pro_stunde = self.verbrauch_kwh / self.dauer_h # Convert to watt-hours
|
||||||
|
|
||||||
# Set the power for the duration of use in the load curve array
|
# Set the power for the duration of use in the load curve array
|
||||||
self.lastkurve[start_hour : start_hour + self.dauer_h] = leistung_pro_stunde
|
self.lastkurve[start_hour : start_hour + self.dauer_h] = leistung_pro_stunde
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
class Heatpump:
|
class Heatpump:
|
||||||
MAX_HEIZLEISTUNG = 5000 # Maximum heating power in watts
|
MAX_HEIZLEISTUNG = 5000 # Maximum heating power in watts
|
||||||
BASE_HEIZLEISTUNG = 235.0 # Base heating power value
|
BASE_HEIZLEISTUNG = 235.0 # Base heating power value
|
||||||
@ -17,7 +16,10 @@ class Heatpump:
|
|||||||
|
|
||||||
def heizleistung_berechnen(self, aussentemperatur):
|
def heizleistung_berechnen(self, aussentemperatur):
|
||||||
"""Calculate heating power based on outside temperature."""
|
"""Calculate heating power based on outside temperature."""
|
||||||
heizleistung = ((self.BASE_HEIZLEISTUNG + aussentemperatur * self.TEMPERATURE_COEFFICIENT) * 1000) / 24.0
|
heizleistung = (
|
||||||
|
(self.BASE_HEIZLEISTUNG + aussentemperatur * self.TEMPERATURE_COEFFICIENT)
|
||||||
|
* 1000
|
||||||
|
) / 24.0
|
||||||
return min(self.max_heizleistung, heizleistung)
|
return min(self.max_heizleistung, heizleistung)
|
||||||
|
|
||||||
def elektrische_leistung_berechnen(self, aussentemperatur):
|
def elektrische_leistung_berechnen(self, aussentemperatur):
|
||||||
@ -29,7 +31,11 @@ class Heatpump:
|
|||||||
leistungsdaten = []
|
leistungsdaten = []
|
||||||
|
|
||||||
if len(temperaturen) != self.prediction_hours:
|
if len(temperaturen) != self.prediction_hours:
|
||||||
raise ValueError("The temperature array must contain exactly " + str(self.prediction_hours) + " entries, one for each hour of the day.")
|
raise ValueError(
|
||||||
|
"The temperature array must contain exactly "
|
||||||
|
+ str(self.prediction_hours)
|
||||||
|
+ " entries, one for each hour of the day."
|
||||||
|
)
|
||||||
|
|
||||||
for temp in temperaturen:
|
for temp in temperaturen:
|
||||||
elektrische_leistung = self.elektrische_leistung_berechnen(temp)
|
elektrische_leistung = self.elektrische_leistung_berechnen(temp)
|
||||||
@ -38,7 +44,7 @@ class Heatpump:
|
|||||||
|
|
||||||
|
|
||||||
# Example usage of the class
|
# Example usage of the class
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
max_heizleistung = 5000 # 5 kW heating power
|
max_heizleistung = 5000 # 5 kW heating power
|
||||||
start_innentemperatur = 15 # Initial indoor temperature
|
start_innentemperatur = 15 # Initial indoor temperature
|
||||||
isolationseffizienz = 0.8 # Insulation efficiency
|
isolationseffizienz = 0.8 # Insulation efficiency
|
||||||
@ -49,7 +55,32 @@ if __name__ == '__main__':
|
|||||||
print(wp.cop_berechnen(-10), " ", wp.cop_berechnen(0), " ", wp.cop_berechnen(10))
|
print(wp.cop_berechnen(-10), " ", wp.cop_berechnen(0), " ", wp.cop_berechnen(10))
|
||||||
|
|
||||||
# 24 hours of outside temperatures (example values)
|
# 24 hours of outside temperatures (example values)
|
||||||
temperaturen = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -5, -2, 5]
|
temperaturen = [
|
||||||
|
10,
|
||||||
|
9,
|
||||||
|
8,
|
||||||
|
7,
|
||||||
|
6,
|
||||||
|
5,
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
-1,
|
||||||
|
-2,
|
||||||
|
-3,
|
||||||
|
-4,
|
||||||
|
-5,
|
||||||
|
-6,
|
||||||
|
-7,
|
||||||
|
-8,
|
||||||
|
-9,
|
||||||
|
-10,
|
||||||
|
-5,
|
||||||
|
-2,
|
||||||
|
5,
|
||||||
|
]
|
||||||
|
|
||||||
# Calculate the 24-hour power data
|
# Calculate the 24-hour power data
|
||||||
leistungsdaten = wp.simulate_24h(temperaturen)
|
leistungsdaten = wp.simulate_24h(temperaturen)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
class Wechselrichter:
|
class Wechselrichter:
|
||||||
def __init__(self, max_leistung_wh, akku):
|
def __init__(self, max_leistung_wh, akku):
|
||||||
self.max_leistung_wh = max_leistung_wh # Maximum power that the inverter can handle
|
self.max_leistung_wh = (
|
||||||
|
max_leistung_wh # Maximum power that the inverter can handle
|
||||||
|
)
|
||||||
self.akku = akku # Connection to a battery object
|
self.akku = akku # Connection to a battery object
|
||||||
|
|
||||||
def energie_verarbeiten(self, erzeugung, verbrauch, hour):
|
def energie_verarbeiten(self, erzeugung, verbrauch, hour):
|
||||||
@ -14,15 +16,21 @@ class Wechselrichter:
|
|||||||
# If consumption exceeds maximum inverter power
|
# If consumption exceeds maximum inverter power
|
||||||
verluste += erzeugung - self.max_leistung_wh
|
verluste += erzeugung - self.max_leistung_wh
|
||||||
restleistung_nach_verbrauch = self.max_leistung_wh - verbrauch
|
restleistung_nach_verbrauch = self.max_leistung_wh - verbrauch
|
||||||
netzbezug = -restleistung_nach_verbrauch # Negative indicates feeding into the grid
|
netzbezug = (
|
||||||
|
-restleistung_nach_verbrauch
|
||||||
|
) # Negative indicates feeding into the grid
|
||||||
eigenverbrauch = self.max_leistung_wh
|
eigenverbrauch = self.max_leistung_wh
|
||||||
else:
|
else:
|
||||||
# Remaining power after consumption
|
# Remaining power after consumption
|
||||||
restleistung_nach_verbrauch = erzeugung - verbrauch
|
restleistung_nach_verbrauch = erzeugung - verbrauch
|
||||||
|
|
||||||
# Load battery with excess energy
|
# Load battery with excess energy
|
||||||
geladene_energie, verluste_laden_akku = self.akku.energie_laden(restleistung_nach_verbrauch, hour)
|
geladene_energie, verluste_laden_akku = self.akku.energie_laden(
|
||||||
rest_überschuss = restleistung_nach_verbrauch - (geladene_energie + verluste_laden_akku)
|
restleistung_nach_verbrauch, hour
|
||||||
|
)
|
||||||
|
rest_überschuss = restleistung_nach_verbrauch - (
|
||||||
|
geladene_energie + verluste_laden_akku
|
||||||
|
)
|
||||||
|
|
||||||
# Feed-in to the grid based on remaining capacity
|
# Feed-in to the grid based on remaining capacity
|
||||||
if rest_überschuss > self.max_leistung_wh - verbrauch:
|
if rest_überschuss > self.max_leistung_wh - verbrauch:
|
||||||
@ -35,17 +43,25 @@ class Wechselrichter:
|
|||||||
eigenverbrauch = verbrauch # Self-consumption is equal to the load
|
eigenverbrauch = verbrauch # Self-consumption is equal to the load
|
||||||
|
|
||||||
else:
|
else:
|
||||||
benötigte_energie = verbrauch - erzeugung # Energy needed from external sources
|
benötigte_energie = (
|
||||||
max_akku_leistung = self.akku.max_ladeleistung_w # Maximum battery discharge power
|
verbrauch - erzeugung
|
||||||
|
) # Energy needed from external sources
|
||||||
|
max_akku_leistung = (
|
||||||
|
self.akku.max_ladeleistung_w
|
||||||
|
) # Maximum battery discharge power
|
||||||
|
|
||||||
# Calculate remaining AC power available
|
# Calculate remaining AC power available
|
||||||
rest_ac_leistung = max(self.max_leistung_wh - erzeugung, 0)
|
rest_ac_leistung = max(self.max_leistung_wh - erzeugung, 0)
|
||||||
|
|
||||||
# Discharge energy from the battery based on need
|
# Discharge energy from the battery based on need
|
||||||
if benötigte_energie < rest_ac_leistung:
|
if benötigte_energie < rest_ac_leistung:
|
||||||
aus_akku, akku_entladeverluste = self.akku.energie_abgeben(benötigte_energie, hour)
|
aus_akku, akku_entladeverluste = self.akku.energie_abgeben(
|
||||||
|
benötigte_energie, hour
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
aus_akku, akku_entladeverluste = self.akku.energie_abgeben(rest_ac_leistung, hour)
|
aus_akku, akku_entladeverluste = self.akku.energie_abgeben(
|
||||||
|
rest_ac_leistung, hour
|
||||||
|
)
|
||||||
|
|
||||||
verluste += akku_entladeverluste # Include losses from battery discharge
|
verluste += akku_entladeverluste # Include losses from battery discharge
|
||||||
netzbezug = benötigte_energie - aus_akku # Energy drawn from the grid
|
netzbezug = benötigte_energie - aus_akku # Energy drawn from the grid
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import numpy as np
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pprint import pprint
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
# Load the .npz file when the application starts
|
# Load the .npz file when the application starts
|
||||||
|
|
||||||
|
|
||||||
class LoadForecast:
|
class LoadForecast:
|
||||||
def __init__(self, filepath=None, year_energy=None):
|
def __init__(self, filepath=None, year_energy=None):
|
||||||
self.filepath = filepath
|
self.filepath = filepath
|
||||||
@ -26,7 +27,9 @@ class LoadForecast:
|
|||||||
day_of_year = date.timetuple().tm_yday
|
day_of_year = date.timetuple().tm_yday
|
||||||
|
|
||||||
# Extract the 24-hour profile for the given date
|
# Extract the 24-hour profile for the given date
|
||||||
daily_stats = self.data_year_energy[day_of_year - 1] # -1 because indexing starts at 0
|
daily_stats = self.data_year_energy[
|
||||||
|
day_of_year - 1
|
||||||
|
] # -1 because indexing starts at 0
|
||||||
return daily_stats
|
return daily_stats
|
||||||
|
|
||||||
def get_hourly_stats(self, date_str, hour):
|
def get_hourly_stats(self, date_str, hour):
|
||||||
@ -44,7 +47,9 @@ class LoadForecast:
|
|||||||
day_of_year = date.timetuple().tm_yday
|
day_of_year = date.timetuple().tm_yday
|
||||||
|
|
||||||
# Extract mean and standard deviation for the given hour
|
# Extract mean and standard deviation for the given hour
|
||||||
hourly_stats = self.data_year_energy[day_of_year - 1, :, hour] # Access the specific hour
|
hourly_stats = self.data_year_energy[
|
||||||
|
day_of_year - 1, :, hour
|
||||||
|
] # Access the specific hour
|
||||||
|
|
||||||
return hourly_stats
|
return hourly_stats
|
||||||
|
|
||||||
@ -63,7 +68,9 @@ class LoadForecast:
|
|||||||
end_day_of_year = end_date.timetuple().tm_yday
|
end_day_of_year = end_date.timetuple().tm_yday
|
||||||
|
|
||||||
# Note that in leap years, the day of the year may need adjustment
|
# Note that in leap years, the day of the year may need adjustment
|
||||||
stats_for_range = self.data_year_energy[start_day_of_year:end_day_of_year] # -1 because indexing starts at 0
|
stats_for_range = self.data_year_energy[
|
||||||
|
start_day_of_year:end_day_of_year
|
||||||
|
] # -1 because indexing starts at 0
|
||||||
stats_for_range = stats_for_range.swapaxes(1, 0)
|
stats_for_range = stats_for_range.swapaxes(1, 0)
|
||||||
|
|
||||||
stats_for_range = stats_for_range.reshape(stats_for_range.shape[0], -1)
|
stats_for_range = stats_for_range.reshape(stats_for_range.shape[0], -1)
|
||||||
@ -73,7 +80,9 @@ class LoadForecast:
|
|||||||
"""Loads data from the specified file."""
|
"""Loads data from the specified file."""
|
||||||
try:
|
try:
|
||||||
data = np.load(self.filepath)
|
data = np.load(self.filepath)
|
||||||
self.data = np.array(list(zip(data["yearly_profiles"], data["yearly_profiles_std"])))
|
self.data = np.array(
|
||||||
|
list(zip(data["yearly_profiles"], data["yearly_profiles_std"]))
|
||||||
|
)
|
||||||
self.data_year_energy = self.data * self.year_energy
|
self.data_year_energy = self.data * self.year_energy
|
||||||
# pprint(self.data_year_energy)
|
# pprint(self.data_year_energy)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
@ -89,10 +98,13 @@ class LoadForecast:
|
|||||||
"""Converts a date string to a datetime object."""
|
"""Converts a date string to a datetime object."""
|
||||||
return datetime.strptime(date_str, "%Y-%m-%d")
|
return datetime.strptime(date_str, "%Y-%m-%d")
|
||||||
|
|
||||||
|
|
||||||
# Example usage of the class
|
# Example usage of the class
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
filepath = r'..\load_profiles.npz' # Adjust the path to the .npz file
|
filepath = r"..\load_profiles.npz" # Adjust the path to the .npz file
|
||||||
lf = LoadForecast(filepath=filepath, year_energy=2000)
|
lf = LoadForecast(filepath=filepath, year_energy=2000)
|
||||||
specific_date_prices = lf.get_daily_stats('2024-02-16') # Adjust date as needed
|
specific_date_prices = lf.get_daily_stats("2024-02-16") # Adjust date as needed
|
||||||
specific_hour_stats = lf.get_hourly_stats('2024-02-16', 12) # Adjust date and hour as needed
|
specific_hour_stats = lf.get_hourly_stats(
|
||||||
|
"2024-02-16", 12
|
||||||
|
) # Adjust date and hour as needed
|
||||||
print(specific_hour_stats)
|
print(specific_hour_stats)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from pprint import pprint
|
|
||||||
|
|
||||||
class Gesamtlast:
|
class Gesamtlast:
|
||||||
def __init__(self, prediction_hours=24):
|
def __init__(self, prediction_hours=24):
|
||||||
@ -14,7 +14,9 @@ class Gesamtlast:
|
|||||||
:param last_array: Array of loads, where each entry corresponds to an hour
|
:param last_array: Array of loads, where each entry corresponds to an hour
|
||||||
"""
|
"""
|
||||||
if len(last_array) != self.prediction_hours:
|
if len(last_array) != self.prediction_hours:
|
||||||
raise ValueError(f"Total load inconsistent lengths in arrays: {name} {len(last_array)}")
|
raise ValueError(
|
||||||
|
f"Total load inconsistent lengths in arrays: {name} {len(last_array)}"
|
||||||
|
)
|
||||||
self.lasten[name] = last_array
|
self.lasten[name] = last_array
|
||||||
|
|
||||||
def gesamtlast_berechnen(self):
|
def gesamtlast_berechnen(self):
|
||||||
@ -31,6 +33,9 @@ class Gesamtlast:
|
|||||||
gesamtlast_array = [0] * stunden
|
gesamtlast_array = [0] * stunden
|
||||||
|
|
||||||
for last_array in self.lasten.values():
|
for last_array in self.lasten.values():
|
||||||
gesamtlast_array = [gesamtlast + stundenlast for gesamtlast, stundenlast in zip(gesamtlast_array, last_array)]
|
gesamtlast_array = [
|
||||||
|
gesamtlast + stundenlast
|
||||||
|
for gesamtlast, stundenlast in zip(gesamtlast_array, last_array)
|
||||||
|
]
|
||||||
|
|
||||||
return np.array(gesamtlast_array)
|
return np.array(gesamtlast_array)
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime, timedelta, timezone
|
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from sklearn.metrics import mean_squared_error, r2_score
|
from sklearn.metrics import mean_squared_error, r2_score
|
||||||
|
|
||||||
import mariadb
|
|
||||||
# from sklearn.model_selection import train_test_split, GridSearchCV
|
# from sklearn.model_selection import train_test_split, GridSearchCV
|
||||||
# from sklearn.ensemble import GradientBoostingRegressor
|
# from sklearn.ensemble import GradientBoostingRegressor
|
||||||
# from xgboost import XGBRegressor
|
# from xgboost import XGBRegressor
|
||||||
@ -22,6 +19,7 @@ import mariadb
|
|||||||
# Add the parent directory to sys.path
|
# Add the parent directory to sys.path
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
from config import *
|
from config import *
|
||||||
|
|
||||||
from modules.class_load import *
|
from modules.class_load import *
|
||||||
|
|
||||||
|
|
||||||
@ -38,117 +36,159 @@ class LoadPredictionAdjuster:
|
|||||||
|
|
||||||
def _remove_outliers(self, data, threshold=2):
|
def _remove_outliers(self, data, threshold=2):
|
||||||
# Calculate the Z-Score of the 'Last' data
|
# Calculate the Z-Score of the 'Last' data
|
||||||
data['Z-Score'] = np.abs((data['Last'] - data['Last'].mean()) / data['Last'].std())
|
data["Z-Score"] = np.abs(
|
||||||
|
(data["Last"] - data["Last"].mean()) / data["Last"].std()
|
||||||
|
)
|
||||||
# Filter the data based on the threshold
|
# Filter the data based on the threshold
|
||||||
filtered_data = data[data['Z-Score'] < threshold]
|
filtered_data = data[data["Z-Score"] < threshold]
|
||||||
return filtered_data.drop(columns=['Z-Score'])
|
return filtered_data.drop(columns=["Z-Score"])
|
||||||
|
|
||||||
def _merge_data(self):
|
def _merge_data(self):
|
||||||
# Convert the time column in both DataFrames to datetime
|
# Convert the time column in both DataFrames to datetime
|
||||||
self.predicted_data['time'] = pd.to_datetime(self.predicted_data['time'])
|
self.predicted_data["time"] = pd.to_datetime(self.predicted_data["time"])
|
||||||
self.measured_data['time'] = pd.to_datetime(self.measured_data['time'])
|
self.measured_data["time"] = pd.to_datetime(self.measured_data["time"])
|
||||||
|
|
||||||
# Ensure both time columns have the same timezone
|
# Ensure both time columns have the same timezone
|
||||||
if self.measured_data['time'].dt.tz is None:
|
if self.measured_data["time"].dt.tz is None:
|
||||||
self.measured_data['time'] = self.measured_data['time'].dt.tz_localize('UTC')
|
self.measured_data["time"] = self.measured_data["time"].dt.tz_localize(
|
||||||
|
"UTC"
|
||||||
self.predicted_data['time'] = (
|
)
|
||||||
self.predicted_data['time'].dt.tz_localize('UTC')
|
|
||||||
.dt.tz_convert('Europe/Berlin')
|
self.predicted_data["time"] = (
|
||||||
|
self.predicted_data["time"]
|
||||||
|
.dt.tz_localize("UTC")
|
||||||
|
.dt.tz_convert("Europe/Berlin")
|
||||||
|
)
|
||||||
|
self.measured_data["time"] = self.measured_data["time"].dt.tz_convert(
|
||||||
|
"Europe/Berlin"
|
||||||
)
|
)
|
||||||
self.measured_data['time'] = self.measured_data['time'].dt.tz_convert('Europe/Berlin')
|
|
||||||
|
|
||||||
# Optionally: Remove timezone information if only working locally
|
# Optionally: Remove timezone information if only working locally
|
||||||
self.predicted_data['time'] = self.predicted_data['time'].dt.tz_localize(None)
|
self.predicted_data["time"] = self.predicted_data["time"].dt.tz_localize(None)
|
||||||
self.measured_data['time'] = self.measured_data['time'].dt.tz_localize(None)
|
self.measured_data["time"] = self.measured_data["time"].dt.tz_localize(None)
|
||||||
|
|
||||||
# Now you can perform the merge
|
# Now you can perform the merge
|
||||||
merged_data = pd.merge(self.measured_data, self.predicted_data, on='time', how='inner')
|
merged_data = pd.merge(
|
||||||
|
self.measured_data, self.predicted_data, on="time", how="inner"
|
||||||
|
)
|
||||||
print(merged_data)
|
print(merged_data)
|
||||||
merged_data['Hour'] = merged_data['time'].dt.hour
|
merged_data["Hour"] = merged_data["time"].dt.hour
|
||||||
merged_data['DayOfWeek'] = merged_data['time'].dt.dayofweek
|
merged_data["DayOfWeek"] = merged_data["time"].dt.dayofweek
|
||||||
return merged_data
|
return merged_data
|
||||||
|
|
||||||
def calculate_weighted_mean(self, train_period_weeks=9, test_period_weeks=1):
|
def calculate_weighted_mean(self, train_period_weeks=9, test_period_weeks=1):
|
||||||
self.merged_data = self._remove_outliers(self.merged_data)
|
self.merged_data = self._remove_outliers(self.merged_data)
|
||||||
train_end_date = self.merged_data['time'].max() - pd.Timedelta(weeks=test_period_weeks)
|
train_end_date = self.merged_data["time"].max() - pd.Timedelta(
|
||||||
|
weeks=test_period_weeks
|
||||||
|
)
|
||||||
train_start_date = train_end_date - pd.Timedelta(weeks=train_period_weeks)
|
train_start_date = train_end_date - pd.Timedelta(weeks=train_period_weeks)
|
||||||
|
|
||||||
test_start_date = train_end_date + pd.Timedelta(hours=1)
|
test_start_date = train_end_date + pd.Timedelta(hours=1)
|
||||||
test_end_date = test_start_date + pd.Timedelta(weeks=test_period_weeks) - pd.Timedelta(hours=1)
|
test_end_date = (
|
||||||
|
test_start_date
|
||||||
|
+ pd.Timedelta(weeks=test_period_weeks)
|
||||||
|
- pd.Timedelta(hours=1)
|
||||||
|
)
|
||||||
|
|
||||||
self.train_data = self.merged_data[
|
self.train_data = self.merged_data[
|
||||||
(self.merged_data['time'] >= train_start_date) &
|
(self.merged_data["time"] >= train_start_date)
|
||||||
(self.merged_data['time'] <= train_end_date)
|
& (self.merged_data["time"] <= train_end_date)
|
||||||
]
|
]
|
||||||
|
|
||||||
self.test_data = self.merged_data[
|
self.test_data = self.merged_data[
|
||||||
(self.merged_data['time'] >= test_start_date) &
|
(self.merged_data["time"] >= test_start_date)
|
||||||
(self.merged_data['time'] <= test_end_date)
|
& (self.merged_data["time"] <= test_end_date)
|
||||||
]
|
]
|
||||||
|
|
||||||
self.train_data['Difference'] = self.train_data['Last'] - self.train_data['Last Pred']
|
self.train_data["Difference"] = (
|
||||||
|
self.train_data["Last"] - self.train_data["Last Pred"]
|
||||||
|
)
|
||||||
|
|
||||||
weekdays_train_data = self.train_data[self.train_data['DayOfWeek'] < 5]
|
weekdays_train_data = self.train_data[self.train_data["DayOfWeek"] < 5]
|
||||||
weekends_train_data = self.train_data[self.train_data['DayOfWeek'] >= 5]
|
weekends_train_data = self.train_data[self.train_data["DayOfWeek"] >= 5]
|
||||||
|
|
||||||
self.weekday_diff = weekdays_train_data.groupby('Hour').apply(self._weighted_mean_diff).dropna()
|
self.weekday_diff = (
|
||||||
self.weekend_diff = weekends_train_data.groupby('Hour').apply(self._weighted_mean_diff).dropna()
|
weekdays_train_data.groupby("Hour").apply(self._weighted_mean_diff).dropna()
|
||||||
|
)
|
||||||
|
self.weekend_diff = (
|
||||||
|
weekends_train_data.groupby("Hour").apply(self._weighted_mean_diff).dropna()
|
||||||
|
)
|
||||||
|
|
||||||
def _weighted_mean_diff(self, data):
|
def _weighted_mean_diff(self, data):
|
||||||
train_end_date = self.train_data['time'].max()
|
train_end_date = self.train_data["time"].max()
|
||||||
weights = 1 / (train_end_date - data['time']).dt.days.replace(0, np.nan)
|
weights = 1 / (train_end_date - data["time"]).dt.days.replace(0, np.nan)
|
||||||
weighted_mean = (data['Difference'] * weights).sum() / weights.sum()
|
weighted_mean = (data["Difference"] * weights).sum() / weights.sum()
|
||||||
return weighted_mean
|
return weighted_mean
|
||||||
|
|
||||||
def adjust_predictions(self):
|
def adjust_predictions(self):
|
||||||
self.train_data['Adjusted Pred'] = self.train_data.apply(self._adjust_row, axis=1)
|
self.train_data["Adjusted Pred"] = self.train_data.apply(
|
||||||
self.test_data['Adjusted Pred'] = self.test_data.apply(self._adjust_row, axis=1)
|
self._adjust_row, axis=1
|
||||||
|
)
|
||||||
|
self.test_data["Adjusted Pred"] = self.test_data.apply(self._adjust_row, axis=1)
|
||||||
|
|
||||||
def _adjust_row(self, row):
|
def _adjust_row(self, row):
|
||||||
if row['DayOfWeek'] < 5:
|
if row["DayOfWeek"] < 5:
|
||||||
return row['Last Pred'] + self.weekday_diff.get(row['Hour'], 0)
|
return row["Last Pred"] + self.weekday_diff.get(row["Hour"], 0)
|
||||||
else:
|
else:
|
||||||
return row['Last Pred'] + self.weekend_diff.get(row['Hour'], 0)
|
return row["Last Pred"] + self.weekend_diff.get(row["Hour"], 0)
|
||||||
|
|
||||||
def plot_results(self):
|
def plot_results(self):
|
||||||
self._plot_data(self.train_data, 'Training')
|
self._plot_data(self.train_data, "Training")
|
||||||
self._plot_data(self.test_data, 'Testing')
|
self._plot_data(self.test_data, "Testing")
|
||||||
|
|
||||||
def _plot_data(self, data, data_type):
|
def _plot_data(self, data, data_type):
|
||||||
plt.figure(figsize=(14, 7))
|
plt.figure(figsize=(14, 7))
|
||||||
plt.plot(data['time'], data['Last'], label=f'Actual Last - {data_type}', color='blue')
|
plt.plot(
|
||||||
plt.plot(data['time'], data['Last Pred'], label=f'Predicted Last - {data_type}', color='red', linestyle='--')
|
data["time"], data["Last"], label=f"Actual Last - {data_type}", color="blue"
|
||||||
plt.plot(data['time'], data['Adjusted Pred'], label=f'Adjusted Predicted Last - {data_type}', color='green', linestyle=':')
|
)
|
||||||
plt.xlabel('Time')
|
plt.plot(
|
||||||
plt.ylabel('Load')
|
data["time"],
|
||||||
plt.title(f'Actual vs Predicted vs Adjusted Predicted Load ({data_type} Data)')
|
data["Last Pred"],
|
||||||
|
label=f"Predicted Last - {data_type}",
|
||||||
|
color="red",
|
||||||
|
linestyle="--",
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
data["time"],
|
||||||
|
data["Adjusted Pred"],
|
||||||
|
label=f"Adjusted Predicted Last - {data_type}",
|
||||||
|
color="green",
|
||||||
|
linestyle=":",
|
||||||
|
)
|
||||||
|
plt.xlabel("Time")
|
||||||
|
plt.ylabel("Load")
|
||||||
|
plt.title(f"Actual vs Predicted vs Adjusted Predicted Load ({data_type} Data)")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
def evaluate_model(self):
|
def evaluate_model(self):
|
||||||
mse = mean_squared_error(self.test_data['Last'], self.test_data['Adjusted Pred'])
|
mse = mean_squared_error(
|
||||||
r2 = r2_score(self.test_data['Last'], self.test_data['Adjusted Pred'])
|
self.test_data["Last"], self.test_data["Adjusted Pred"]
|
||||||
print(f'Mean Squared Error: {mse}')
|
)
|
||||||
print(f'R-squared: {r2}')
|
r2 = r2_score(self.test_data["Last"], self.test_data["Adjusted Pred"])
|
||||||
|
print(f"Mean Squared Error: {mse}")
|
||||||
|
print(f"R-squared: {r2}")
|
||||||
|
|
||||||
def predict_next_hours(self, hours_ahead):
|
def predict_next_hours(self, hours_ahead):
|
||||||
last_date = self.merged_data['time'].max()
|
last_date = self.merged_data["time"].max()
|
||||||
future_dates = [last_date + pd.Timedelta(hours=i) for i in range(1, hours_ahead + 1)]
|
future_dates = [
|
||||||
future_df = pd.DataFrame({'time': future_dates})
|
last_date + pd.Timedelta(hours=i) for i in range(1, hours_ahead + 1)
|
||||||
future_df['Hour'] = future_df['time'].dt.hour
|
]
|
||||||
future_df['DayOfWeek'] = future_df['time'].dt.dayofweek
|
future_df = pd.DataFrame({"time": future_dates})
|
||||||
future_df['Last Pred'] = future_df['time'].apply(self._forecast_next_hours)
|
future_df["Hour"] = future_df["time"].dt.hour
|
||||||
future_df['Adjusted Pred'] = future_df.apply(self._adjust_row, axis=1)
|
future_df["DayOfWeek"] = future_df["time"].dt.dayofweek
|
||||||
|
future_df["Last Pred"] = future_df["time"].apply(self._forecast_next_hours)
|
||||||
|
future_df["Adjusted Pred"] = future_df.apply(self._adjust_row, axis=1)
|
||||||
return future_df
|
return future_df
|
||||||
|
|
||||||
def _forecast_next_hours(self, timestamp):
|
def _forecast_next_hours(self, timestamp):
|
||||||
date_str = timestamp.strftime('%Y-%m-%d')
|
date_str = timestamp.strftime("%Y-%m-%d")
|
||||||
hour = timestamp.hour
|
hour = timestamp.hour
|
||||||
daily_forecast = self.load_forecast.get_daily_stats(date_str)
|
daily_forecast = self.load_forecast.get_daily_stats(date_str)
|
||||||
return daily_forecast[0][hour] if hour < len(daily_forecast[0]) else np.nan
|
return daily_forecast[0][hour] if hour < len(daily_forecast[0]) else np.nan
|
||||||
|
|
||||||
|
|
||||||
# if __name__ == '__main__':
|
# if __name__ == '__main__':
|
||||||
# estimator = LastEstimator()
|
# estimator = LastEstimator()
|
||||||
# start_date = "2024-06-01"
|
# start_date = "2024-06-01"
|
||||||
|
@ -1,31 +1,29 @@
|
|||||||
from flask import Flask, jsonify, request
|
import os
|
||||||
import numpy as np
|
|
||||||
from modules.class_load import *
|
|
||||||
from modules.class_ems import *
|
|
||||||
from modules.class_pv_forecast import *
|
|
||||||
from modules.class_akku import *
|
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from modules.class_akku import *
|
||||||
|
from modules.class_ems import *
|
||||||
|
from modules.class_haushaltsgeraet import *
|
||||||
from modules.class_heatpump import *
|
from modules.class_heatpump import *
|
||||||
from modules.class_load_container import *
|
|
||||||
from modules.class_inverter import *
|
from modules.class_inverter import *
|
||||||
|
from modules.class_load import *
|
||||||
|
from modules.class_load_container import *
|
||||||
|
from modules.class_pv_forecast import *
|
||||||
from modules.class_sommerzeit import *
|
from modules.class_sommerzeit import *
|
||||||
from modules.visualize import *
|
from modules.visualize import *
|
||||||
from modules.class_haushaltsgeraet import *
|
|
||||||
import os
|
matplotlib.use("Agg") # Setzt das Backend auf Agg
|
||||||
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
|
|
||||||
import string
|
|
||||||
from datetime import datetime
|
|
||||||
from deap import base, creator, tools, algorithms
|
|
||||||
import numpy as np
|
|
||||||
import random
|
import random
|
||||||
import os
|
from datetime import datetime
|
||||||
|
|
||||||
|
from deap import algorithms, base, creator, tools
|
||||||
|
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
from config import *
|
from config import *
|
||||||
|
|
||||||
|
|
||||||
def isfloat(num):
|
def isfloat(num):
|
||||||
try:
|
try:
|
||||||
float(num)
|
float(num)
|
||||||
@ -33,7 +31,17 @@ def isfloat(num):
|
|||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def differential_evolution(population, toolbox, cxpb, mutpb, ngen, stats=None, halloffame=None, verbose=__debug__):
|
|
||||||
|
def differential_evolution(
|
||||||
|
population,
|
||||||
|
toolbox,
|
||||||
|
cxpb,
|
||||||
|
mutpb,
|
||||||
|
ngen,
|
||||||
|
stats=None,
|
||||||
|
halloffame=None,
|
||||||
|
verbose=__debug__,
|
||||||
|
):
|
||||||
"""Differential Evolution Algorithm"""
|
"""Differential Evolution Algorithm"""
|
||||||
|
|
||||||
# Evaluate the entire population
|
# Evaluate the entire population
|
||||||
@ -45,7 +53,7 @@ def differential_evolution(population, toolbox, cxpb, mutpb, ngen, stats=None, h
|
|||||||
halloffame.update(population)
|
halloffame.update(population)
|
||||||
|
|
||||||
logbook = tools.Logbook()
|
logbook = tools.Logbook()
|
||||||
logbook.header = ['gen', 'nevals'] + (stats.fields if stats else [])
|
logbook.header = ["gen", "nevals"] + (stats.fields if stats else [])
|
||||||
|
|
||||||
for gen in range(ngen):
|
for gen in range(ngen):
|
||||||
# Generate the next generation by mutation and recombination
|
# Generate the next generation by mutation and recombination
|
||||||
@ -77,7 +85,6 @@ def differential_evolution(population, toolbox, cxpb, mutpb, ngen, stats=None, h
|
|||||||
return population, logbook
|
return population, logbook
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class optimization_problem:
|
class optimization_problem:
|
||||||
def __init__(self, prediction_hours=24, strafe=10, optimization_hours=24):
|
def __init__(self, prediction_hours=24, strafe=10, optimization_hours=24):
|
||||||
self.prediction_hours = prediction_hours #
|
self.prediction_hours = prediction_hours #
|
||||||
@ -86,7 +93,6 @@ class optimization_problem:
|
|||||||
self.fixed_eauto_hours = prediction_hours - optimization_hours
|
self.fixed_eauto_hours = prediction_hours - optimization_hours
|
||||||
self.possible_charge_values = moegliche_ladestroeme_in_prozent
|
self.possible_charge_values = moegliche_ladestroeme_in_prozent
|
||||||
|
|
||||||
|
|
||||||
def split_individual(self, individual):
|
def split_individual(self, individual):
|
||||||
"""
|
"""
|
||||||
Teilt das gegebene Individuum in die verschiedenen Parameter auf:
|
Teilt das gegebene Individuum in die verschiedenen Parameter auf:
|
||||||
@ -95,20 +101,24 @@ class optimization_problem:
|
|||||||
- Haushaltsgeräte (spuelstart_int, falls vorhanden)
|
- Haushaltsgeräte (spuelstart_int, falls vorhanden)
|
||||||
"""
|
"""
|
||||||
# Extrahiere die Entlade- und Ladeparameter direkt aus dem Individuum
|
# Extrahiere die Entlade- und Ladeparameter direkt aus dem Individuum
|
||||||
discharge_hours_bin = individual[:self.prediction_hours] # Erste 24 Werte sind Bool (Entladen)
|
discharge_hours_bin = individual[
|
||||||
eautocharge_hours_float = individual[self.prediction_hours:self.prediction_hours * 2] # Nächste 24 Werte sind Float (Laden)
|
: self.prediction_hours
|
||||||
|
] # Erste 24 Werte sind Bool (Entladen)
|
||||||
|
eautocharge_hours_float = individual[
|
||||||
|
self.prediction_hours : self.prediction_hours * 2
|
||||||
|
] # Nächste 24 Werte sind Float (Laden)
|
||||||
|
|
||||||
spuelstart_int = None
|
spuelstart_int = None
|
||||||
if self.opti_param and self.opti_param.get("haushaltsgeraete", 0) > 0:
|
if self.opti_param and self.opti_param.get("haushaltsgeraete", 0) > 0:
|
||||||
spuelstart_int = individual[-1] # Letzter Wert ist Startzeit für Haushaltsgerät
|
spuelstart_int = individual[
|
||||||
|
-1
|
||||||
|
] # Letzter Wert ist Startzeit für Haushaltsgerät
|
||||||
|
|
||||||
return discharge_hours_bin, eautocharge_hours_float, spuelstart_int
|
return discharge_hours_bin, eautocharge_hours_float, spuelstart_int
|
||||||
|
|
||||||
|
|
||||||
def setup_deap_environment(self, opti_param, start_hour):
|
def setup_deap_environment(self, opti_param, start_hour):
|
||||||
self.opti_param = opti_param
|
self.opti_param = opti_param
|
||||||
|
|
||||||
|
|
||||||
if "FitnessMin" in creator.__dict__:
|
if "FitnessMin" in creator.__dict__:
|
||||||
del creator.FitnessMin
|
del creator.FitnessMin
|
||||||
if "Individual" in creator.__dict__:
|
if "Individual" in creator.__dict__:
|
||||||
@ -120,38 +130,48 @@ class optimization_problem:
|
|||||||
# PARAMETER
|
# PARAMETER
|
||||||
self.toolbox = base.Toolbox()
|
self.toolbox = base.Toolbox()
|
||||||
self.toolbox.register("attr_bool", random.randint, 0, 1)
|
self.toolbox.register("attr_bool", random.randint, 0, 1)
|
||||||
self.toolbox.register("attr_float", random.uniform, 0, 1) # Für kontinuierliche Werte zwischen 0 und 1 (z.B. für E-Auto-Ladeleistung)
|
self.toolbox.register(
|
||||||
|
"attr_float", random.uniform, 0, 1
|
||||||
|
) # Für kontinuierliche Werte zwischen 0 und 1 (z.B. für E-Auto-Ladeleistung)
|
||||||
# self.toolbox.register("attr_choice", random.choice, self.possible_charge_values) # Für diskrete Ladeströme
|
# self.toolbox.register("attr_choice", random.choice, self.possible_charge_values) # Für diskrete Ladeströme
|
||||||
|
|
||||||
self.toolbox.register("attr_int", random.randint, start_hour, 23)
|
self.toolbox.register("attr_int", random.randint, start_hour, 23)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# Haushaltsgeraete
|
# Haushaltsgeraete
|
||||||
# print("Haushalt:",opti_param["haushaltsgeraete"])
|
# print("Haushalt:",opti_param["haushaltsgeraete"])
|
||||||
if opti_param["haushaltsgeraete"] > 0:
|
if opti_param["haushaltsgeraete"] > 0:
|
||||||
|
|
||||||
def create_individual():
|
def create_individual():
|
||||||
attrs = [self.toolbox.attr_bool() for _ in range(self.prediction_hours)] # 24 Bool-Werte für Entladen
|
attrs = [
|
||||||
attrs += [self.toolbox.attr_float() for _ in range(self.prediction_hours)] # 24 Float-Werte für Laden
|
self.toolbox.attr_bool() for _ in range(self.prediction_hours)
|
||||||
|
] # 24 Bool-Werte für Entladen
|
||||||
|
attrs += [
|
||||||
|
self.toolbox.attr_float() for _ in range(self.prediction_hours)
|
||||||
|
] # 24 Float-Werte für Laden
|
||||||
attrs.append(self.toolbox.attr_int()) # Haushaltsgerät-Startzeit
|
attrs.append(self.toolbox.attr_int()) # Haushaltsgerät-Startzeit
|
||||||
return creator.Individual(attrs)
|
return creator.Individual(attrs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
def create_individual():
|
def create_individual():
|
||||||
attrs = [self.toolbox.attr_bool() for _ in range(self.prediction_hours)] # 24 Bool-Werte für Entladen
|
attrs = [
|
||||||
attrs += [self.toolbox.attr_float() for _ in range(self.prediction_hours)] # 24 Float-Werte für Laden
|
self.toolbox.attr_bool() for _ in range(self.prediction_hours)
|
||||||
|
] # 24 Bool-Werte für Entladen
|
||||||
|
attrs += [
|
||||||
|
self.toolbox.attr_float() for _ in range(self.prediction_hours)
|
||||||
|
] # 24 Float-Werte für Laden
|
||||||
return creator.Individual(attrs)
|
return creator.Individual(attrs)
|
||||||
|
|
||||||
|
self.toolbox.register(
|
||||||
|
"individual", create_individual
|
||||||
self.toolbox.register("individual", create_individual)#tools.initCycle, creator.Individual, (self.toolbox.attr_bool,self.toolbox.attr_bool), n=self.prediction_hours+1)
|
) # tools.initCycle, creator.Individual, (self.toolbox.attr_bool,self.toolbox.attr_bool), n=self.prediction_hours+1)
|
||||||
self.toolbox.register("population", tools.initRepeat, list, self.toolbox.individual)
|
self.toolbox.register(
|
||||||
|
"population", tools.initRepeat, list, self.toolbox.individual
|
||||||
|
)
|
||||||
self.toolbox.register("mate", tools.cxTwoPoint)
|
self.toolbox.register("mate", tools.cxTwoPoint)
|
||||||
self.toolbox.register("mutate", tools.mutFlipBit, indpb=0.1)
|
self.toolbox.register("mutate", tools.mutFlipBit, indpb=0.1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# self.toolbox.register("mutate", mutate_choice, self.possible_charge_values, indpb=0.1)
|
# self.toolbox.register("mutate", mutate_choice, self.possible_charge_values, indpb=0.1)
|
||||||
# self.toolbox.register("mutate", tools.mutUniformInt, low=0, up=len(self.possible_charge_values)-1, indpb=0.1)
|
# self.toolbox.register("mutate", tools.mutUniformInt, low=0, up=len(self.possible_charge_values)-1, indpb=0.1)
|
||||||
|
|
||||||
@ -162,33 +182,35 @@ class optimization_problem:
|
|||||||
|
|
||||||
# print("Spuel:",self.opti_param)
|
# print("Spuel:",self.opti_param)
|
||||||
|
|
||||||
discharge_hours_bin, eautocharge_hours_float, spuelstart_int = self.split_individual(individual)
|
discharge_hours_bin, eautocharge_hours_float, spuelstart_int = (
|
||||||
|
self.split_individual(individual)
|
||||||
|
)
|
||||||
|
|
||||||
# Haushaltsgeraete
|
# Haushaltsgeraete
|
||||||
if self.opti_param["haushaltsgeraete"] > 0:
|
if self.opti_param["haushaltsgeraete"] > 0:
|
||||||
ems.set_haushaltsgeraet_start(spuelstart_int, global_start_hour=start_hour)
|
ems.set_haushaltsgeraet_start(spuelstart_int, global_start_hour=start_hour)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# discharge_hours_bin = np.full(self.prediction_hours,0)
|
# discharge_hours_bin = np.full(self.prediction_hours,0)
|
||||||
ems.set_akku_discharge_hours(discharge_hours_bin)
|
ems.set_akku_discharge_hours(discharge_hours_bin)
|
||||||
|
|
||||||
# Setze die festen Werte für die letzten x Stunden
|
# Setze die festen Werte für die letzten x Stunden
|
||||||
for i in range(self.prediction_hours - self.fixed_eauto_hours, self.prediction_hours):
|
for i in range(
|
||||||
eautocharge_hours_float[i] = 0.0 # Setze die letzten x Stunden auf einen festen Wert (oder vorgegebenen Wert)
|
self.prediction_hours - self.fixed_eauto_hours, self.prediction_hours
|
||||||
|
):
|
||||||
|
eautocharge_hours_float[i] = (
|
||||||
|
0.0 # Setze die letzten x Stunden auf einen festen Wert (oder vorgegebenen Wert)
|
||||||
|
)
|
||||||
|
|
||||||
# print(eautocharge_hours_float)
|
# print(eautocharge_hours_float)
|
||||||
|
|
||||||
ems.set_eauto_charge_hours(eautocharge_hours_float)
|
ems.set_eauto_charge_hours(eautocharge_hours_float)
|
||||||
|
|
||||||
|
|
||||||
o = ems.simuliere(start_hour)
|
o = ems.simuliere(start_hour)
|
||||||
|
|
||||||
return o
|
return o
|
||||||
|
|
||||||
# Fitness-Funktion (muss Ihre EnergieManagementSystem-Logik integrieren)
|
# Fitness-Funktion (muss Ihre EnergieManagementSystem-Logik integrieren)
|
||||||
def evaluate(self, individual, ems, parameter, start_hour, worst_case):
|
def evaluate(self, individual, ems, parameter, start_hour, worst_case):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
o = self.evaluate_inner(individual, ems, start_hour)
|
o = self.evaluate_inner(individual, ems, start_hour)
|
||||||
except:
|
except:
|
||||||
@ -198,7 +220,9 @@ class optimization_problem:
|
|||||||
if worst_case:
|
if worst_case:
|
||||||
gesamtbilanz = gesamtbilanz * -1.0
|
gesamtbilanz = gesamtbilanz * -1.0
|
||||||
|
|
||||||
discharge_hours_bin, eautocharge_hours_float, spuelstart_int = self.split_individual(individual)
|
discharge_hours_bin, eautocharge_hours_float, spuelstart_int = (
|
||||||
|
self.split_individual(individual)
|
||||||
|
)
|
||||||
max_ladeleistung = np.max(moegliche_ladestroeme_in_prozent)
|
max_ladeleistung = np.max(moegliche_ladestroeme_in_prozent)
|
||||||
|
|
||||||
strafe_überschreitung = 0.0
|
strafe_überschreitung = 0.0
|
||||||
@ -209,34 +233,45 @@ class optimization_problem:
|
|||||||
# Berechne die Überschreitung
|
# Berechne die Überschreitung
|
||||||
überschreitung = ladeleistung - max_ladeleistung
|
überschreitung = ladeleistung - max_ladeleistung
|
||||||
# Füge eine Strafe hinzu (z.B. 10 Einheiten Strafe pro Prozentpunkt Überschreitung)
|
# Füge eine Strafe hinzu (z.B. 10 Einheiten Strafe pro Prozentpunkt Überschreitung)
|
||||||
strafe_überschreitung += self.strafe * 10 # Hier ist die Strafe proportional zur Überschreitung
|
strafe_überschreitung += (
|
||||||
|
self.strafe * 10
|
||||||
|
) # Hier ist die Strafe proportional zur Überschreitung
|
||||||
|
|
||||||
# Für jeden Discharge 0, eine kleine Strafe von 1 Cent, da die Lastvertelung noch fehlt. Also wenn es egal ist, soll er den Akku entladen lassen
|
# Für jeden Discharge 0, eine kleine Strafe von 1 Cent, da die Lastvertelung noch fehlt. Also wenn es egal ist, soll er den Akku entladen lassen
|
||||||
for i in range(0, self.prediction_hours):
|
for i in range(0, self.prediction_hours):
|
||||||
if discharge_hours_bin[i] == 0.0: # Wenn die letzten x Stunden von einem festen Wert abweichen
|
if (
|
||||||
|
discharge_hours_bin[i] == 0.0
|
||||||
|
): # Wenn die letzten x Stunden von einem festen Wert abweichen
|
||||||
gesamtbilanz += 0.01 # Bestrafe den Optimierer
|
gesamtbilanz += 0.01 # Bestrafe den Optimierer
|
||||||
|
|
||||||
|
|
||||||
# E-Auto nur die ersten self.fixed_eauto_hours
|
# E-Auto nur die ersten self.fixed_eauto_hours
|
||||||
for i in range(self.prediction_hours - self.fixed_eauto_hours, self.prediction_hours):
|
for i in range(
|
||||||
if eautocharge_hours_float[i] != 0.0: # Wenn die letzten x Stunden von einem festen Wert abweichen
|
self.prediction_hours - self.fixed_eauto_hours, self.prediction_hours
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
eautocharge_hours_float[i] != 0.0
|
||||||
|
): # Wenn die letzten x Stunden von einem festen Wert abweichen
|
||||||
gesamtbilanz += self.strafe # Bestrafe den Optimierer
|
gesamtbilanz += self.strafe # Bestrafe den Optimierer
|
||||||
|
|
||||||
|
|
||||||
# Überprüfung, ob der Mindest-SoC erreicht wird
|
# Überprüfung, ob der Mindest-SoC erreicht wird
|
||||||
final_soc = ems.eauto.ladezustand_in_prozent() # Nimmt den SoC am Ende des Optimierungszeitraums
|
final_soc = (
|
||||||
|
ems.eauto.ladezustand_in_prozent()
|
||||||
|
) # Nimmt den SoC am Ende des Optimierungszeitraums
|
||||||
|
|
||||||
if (parameter['eauto_min_soc']-ems.eauto.ladezustand_in_prozent()) <= 0.0:
|
if (parameter["eauto_min_soc"] - ems.eauto.ladezustand_in_prozent()) <= 0.0:
|
||||||
# print (parameter['eauto_min_soc']," " ,ems.eauto.ladezustand_in_prozent()," ",(parameter['eauto_min_soc']-ems.eauto.ladezustand_in_prozent()))
|
# print (parameter['eauto_min_soc']," " ,ems.eauto.ladezustand_in_prozent()," ",(parameter['eauto_min_soc']-ems.eauto.ladezustand_in_prozent()))
|
||||||
for i in range(0, self.prediction_hours):
|
for i in range(0, self.prediction_hours):
|
||||||
if eautocharge_hours_float[i] != 0.0: # Wenn die letzten x Stunden von einem festen Wert abweichen
|
if (
|
||||||
|
eautocharge_hours_float[i] != 0.0
|
||||||
|
): # Wenn die letzten x Stunden von einem festen Wert abweichen
|
||||||
gesamtbilanz += self.strafe # Bestrafe den Optimierer
|
gesamtbilanz += self.strafe # Bestrafe den Optimierer
|
||||||
|
|
||||||
|
eauto_roi = parameter["eauto_min_soc"] - ems.eauto.ladezustand_in_prozent()
|
||||||
eauto_roi = (parameter['eauto_min_soc']-ems.eauto.ladezustand_in_prozent())
|
individual.extra_data = (
|
||||||
individual.extra_data = (o["Gesamtbilanz_Euro"],o["Gesamt_Verluste"], eauto_roi )
|
o["Gesamtbilanz_Euro"],
|
||||||
|
o["Gesamt_Verluste"],
|
||||||
|
eauto_roi,
|
||||||
|
)
|
||||||
|
|
||||||
restenergie_akku = ems.akku.aktueller_energieinhalt()
|
restenergie_akku = ems.akku.aktueller_energieinhalt()
|
||||||
restwert_akku = restenergie_akku * parameter["preis_euro_pro_wh_akku"]
|
restwert_akku = restenergie_akku * parameter["preis_euro_pro_wh_akku"]
|
||||||
@ -245,20 +280,18 @@ class optimization_problem:
|
|||||||
# print(restwert_akku)
|
# print(restwert_akku)
|
||||||
# print()
|
# print()
|
||||||
strafe = 0.0
|
strafe = 0.0
|
||||||
strafe = max(0,(parameter['eauto_min_soc']-ems.eauto.ladezustand_in_prozent()) * self.strafe )
|
strafe = max(
|
||||||
|
0,
|
||||||
|
(parameter["eauto_min_soc"] - ems.eauto.ladezustand_in_prozent())
|
||||||
|
* self.strafe,
|
||||||
|
)
|
||||||
gesamtbilanz += strafe - restwert_akku + strafe_überschreitung
|
gesamtbilanz += strafe - restwert_akku + strafe_überschreitung
|
||||||
# gesamtbilanz += o["Gesamt_Verluste"]/10000.0
|
# gesamtbilanz += o["Gesamt_Verluste"]/10000.0
|
||||||
|
|
||||||
return (gesamtbilanz,)
|
return (gesamtbilanz,)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Genetischer Algorithmus
|
# Genetischer Algorithmus
|
||||||
def optimize(self, start_solution=None):
|
def optimize(self, start_solution=None):
|
||||||
|
|
||||||
|
|
||||||
population = self.toolbox.population(n=300)
|
population = self.toolbox.population(n=300)
|
||||||
hof = tools.HallOfFame(1)
|
hof = tools.HallOfFame(1)
|
||||||
|
|
||||||
@ -274,73 +307,95 @@ class optimization_problem:
|
|||||||
population.insert(1, creator.Individual(start_solution))
|
population.insert(1, creator.Individual(start_solution))
|
||||||
population.insert(2, creator.Individual(start_solution))
|
population.insert(2, creator.Individual(start_solution))
|
||||||
|
|
||||||
algorithms.eaMuPlusLambda(population, self.toolbox, mu=100, lambda_=200, cxpb=0.5, mutpb=0.3, ngen=400, stats=stats, halloffame=hof, verbose=True)
|
algorithms.eaMuPlusLambda(
|
||||||
|
population,
|
||||||
|
self.toolbox,
|
||||||
|
mu=100,
|
||||||
|
lambda_=200,
|
||||||
|
cxpb=0.5,
|
||||||
|
mutpb=0.3,
|
||||||
|
ngen=400,
|
||||||
|
stats=stats,
|
||||||
|
halloffame=hof,
|
||||||
|
verbose=True,
|
||||||
|
)
|
||||||
# algorithms.eaSimple(population, self.toolbox, cxpb=0.3, mutpb=0.3, ngen=200, stats=stats, halloffame=hof, verbose=True)
|
# algorithms.eaSimple(population, self.toolbox, cxpb=0.3, mutpb=0.3, ngen=200, stats=stats, halloffame=hof, verbose=True)
|
||||||
# algorithms.eaMuCommaLambda(population, self.toolbox, mu=100, lambda_=200, cxpb=0.2, mutpb=0.4, ngen=300, stats=stats, halloffame=hof, verbose=True)
|
# algorithms.eaMuCommaLambda(population, self.toolbox, mu=100, lambda_=200, cxpb=0.2, mutpb=0.4, ngen=300, stats=stats, halloffame=hof, verbose=True)
|
||||||
# population, log = differential_evolution(population, self.toolbox, cxpb=0.2, mutpb=0.5, ngen=200, stats=stats, halloffame=hof, verbose=True)
|
# population, log = differential_evolution(population, self.toolbox, cxpb=0.2, mutpb=0.5, ngen=200, stats=stats, halloffame=hof, verbose=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
member = {"bilanz": [], "verluste": [], "nebenbedingung": []}
|
member = {"bilanz": [], "verluste": [], "nebenbedingung": []}
|
||||||
for ind in population:
|
for ind in population:
|
||||||
if hasattr(ind, 'extra_data'):
|
if hasattr(ind, "extra_data"):
|
||||||
extra_value1, extra_value2, extra_value3 = ind.extra_data
|
extra_value1, extra_value2, extra_value3 = ind.extra_data
|
||||||
member["bilanz"].append(extra_value1)
|
member["bilanz"].append(extra_value1)
|
||||||
member["verluste"].append(extra_value2)
|
member["verluste"].append(extra_value2)
|
||||||
member["nebenbedingung"].append(extra_value3)
|
member["nebenbedingung"].append(extra_value3)
|
||||||
|
|
||||||
|
|
||||||
return hof[0], member
|
return hof[0], member
|
||||||
|
|
||||||
|
def optimierung_ems(
|
||||||
def optimierung_ems(self,parameter=None, start_hour=None,worst_case=False, startdate=None):
|
self, parameter=None, start_hour=None, worst_case=False, startdate=None
|
||||||
|
):
|
||||||
|
|
||||||
############
|
############
|
||||||
# Parameter
|
# Parameter
|
||||||
############
|
############
|
||||||
if startdate == None:
|
if startdate == None:
|
||||||
date = (datetime.now().date() + timedelta(hours = self.prediction_hours)).strftime("%Y-%m-%d")
|
date = (
|
||||||
|
datetime.now().date() + timedelta(hours=self.prediction_hours)
|
||||||
|
).strftime("%Y-%m-%d")
|
||||||
date_now = datetime.now().strftime("%Y-%m-%d")
|
date_now = datetime.now().strftime("%Y-%m-%d")
|
||||||
else:
|
else:
|
||||||
date = (startdate + timedelta(hours = self.prediction_hours)).strftime("%Y-%m-%d")
|
date = (startdate + timedelta(hours=self.prediction_hours)).strftime(
|
||||||
|
"%Y-%m-%d"
|
||||||
|
)
|
||||||
date_now = startdate.strftime("%Y-%m-%d")
|
date_now = startdate.strftime("%Y-%m-%d")
|
||||||
# print("Start_date:",date_now)
|
# print("Start_date:",date_now)
|
||||||
|
|
||||||
akku_size = parameter['pv_akku_cap'] # Wh
|
akku_size = parameter["pv_akku_cap"] # Wh
|
||||||
|
|
||||||
einspeiseverguetung_euro_pro_wh = np.full(self.prediction_hours, parameter["einspeiseverguetung_euro_pro_wh"]) #= # € / Wh 7/(1000.0*100.0)
|
einspeiseverguetung_euro_pro_wh = np.full(
|
||||||
discharge_array = np.full(self.prediction_hours,1) #np.array([1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]) #
|
self.prediction_hours, parameter["einspeiseverguetung_euro_pro_wh"]
|
||||||
akku = PVAkku(kapazitaet_wh=akku_size,hours=self.prediction_hours,start_soc_prozent=parameter["pv_soc"], max_ladeleistung_w=5000)
|
) # = # € / Wh 7/(1000.0*100.0)
|
||||||
|
discharge_array = np.full(
|
||||||
|
self.prediction_hours, 1
|
||||||
|
) # np.array([1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]) #
|
||||||
|
akku = PVAkku(
|
||||||
|
kapazitaet_wh=akku_size,
|
||||||
|
hours=self.prediction_hours,
|
||||||
|
start_soc_prozent=parameter["pv_soc"],
|
||||||
|
max_ladeleistung_w=5000,
|
||||||
|
)
|
||||||
akku.set_charge_per_hour(discharge_array)
|
akku.set_charge_per_hour(discharge_array)
|
||||||
|
|
||||||
|
laden_moeglich = np.full(
|
||||||
laden_moeglich = np.full(self.prediction_hours,1) # np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0])
|
self.prediction_hours, 1
|
||||||
eauto = PVAkku(kapazitaet_wh=parameter["eauto_cap"], hours=self.prediction_hours, lade_effizienz=parameter["eauto_charge_efficiency"], entlade_effizienz=1.0, max_ladeleistung_w=parameter["eauto_charge_power"] ,start_soc_prozent=parameter["eauto_soc"])
|
) # np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0])
|
||||||
|
eauto = PVAkku(
|
||||||
|
kapazitaet_wh=parameter["eauto_cap"],
|
||||||
|
hours=self.prediction_hours,
|
||||||
|
lade_effizienz=parameter["eauto_charge_efficiency"],
|
||||||
|
entlade_effizienz=1.0,
|
||||||
|
max_ladeleistung_w=parameter["eauto_charge_power"],
|
||||||
|
start_soc_prozent=parameter["eauto_soc"],
|
||||||
|
)
|
||||||
eauto.set_charge_per_hour(laden_moeglich)
|
eauto.set_charge_per_hour(laden_moeglich)
|
||||||
min_soc_eauto = parameter['eauto_min_soc']
|
min_soc_eauto = parameter["eauto_min_soc"]
|
||||||
start_params = parameter['start_solution']
|
start_params = parameter["start_solution"]
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# spuelmaschine
|
# spuelmaschine
|
||||||
##############
|
##############
|
||||||
print(parameter)
|
print(parameter)
|
||||||
if parameter["haushaltsgeraet_dauer"] > 0:
|
if parameter["haushaltsgeraet_dauer"] > 0:
|
||||||
spuelmaschine = Haushaltsgeraet(hours=self.prediction_hours, verbrauch_kwh=parameter["haushaltsgeraet_wh"], dauer_h=parameter["haushaltsgeraet_dauer"])
|
spuelmaschine = Haushaltsgeraet(
|
||||||
|
hours=self.prediction_hours,
|
||||||
|
verbrauch_kwh=parameter["haushaltsgeraet_wh"],
|
||||||
|
dauer_h=parameter["haushaltsgeraet_dauer"],
|
||||||
|
)
|
||||||
spuelmaschine.set_startzeitpunkt(start_hour) # Startet jetzt
|
spuelmaschine.set_startzeitpunkt(start_hour) # Startet jetzt
|
||||||
else:
|
else:
|
||||||
spuelmaschine = None
|
spuelmaschine = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# PV Forecast
|
# PV Forecast
|
||||||
###############
|
###############
|
||||||
@ -350,9 +405,12 @@ class optimization_problem:
|
|||||||
# if isfloat(parameter['pvpowernow']):
|
# if isfloat(parameter['pvpowernow']):
|
||||||
# PVforecast.update_ac_power_measurement(date_time=datetime.now(), ac_power_measurement=float(parameter['pvpowernow']))
|
# PVforecast.update_ac_power_measurement(date_time=datetime.now(), ac_power_measurement=float(parameter['pvpowernow']))
|
||||||
# #PVforecast.print_ac_power_and_measurement()
|
# #PVforecast.print_ac_power_and_measurement()
|
||||||
pv_forecast = parameter['pv_forecast'] #PVforecast.get_pv_forecast_for_date_range(date_now,date) #get_forecast_for_date(date)
|
pv_forecast = parameter[
|
||||||
temperature_forecast = parameter['temperature_forecast'] #PVforecast.get_temperature_for_date_range(date_now,date)
|
"pv_forecast"
|
||||||
|
] # PVforecast.get_pv_forecast_for_date_range(date_now,date) #get_forecast_for_date(date)
|
||||||
|
temperature_forecast = parameter[
|
||||||
|
"temperature_forecast"
|
||||||
|
] # PVforecast.get_temperature_for_date_range(date_now,date)
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Strompreise
|
# Strompreise
|
||||||
@ -361,10 +419,17 @@ class optimization_problem:
|
|||||||
print(specific_date_prices)
|
print(specific_date_prices)
|
||||||
# print("https://api.akkudoktor.net/prices?start="+date_now+"&end="+date)
|
# print("https://api.akkudoktor.net/prices?start="+date_now+"&end="+date)
|
||||||
|
|
||||||
|
|
||||||
wr = Wechselrichter(10000, akku)
|
wr = Wechselrichter(10000, akku)
|
||||||
|
|
||||||
ems = EnergieManagementSystem(gesamtlast = parameter["gesamtlast"], pv_prognose_wh=pv_forecast, strompreis_euro_pro_wh=specific_date_prices, einspeiseverguetung_euro_pro_wh=einspeiseverguetung_euro_pro_wh, eauto=eauto, haushaltsgeraet=spuelmaschine,wechselrichter=wr)
|
ems = EnergieManagementSystem(
|
||||||
|
gesamtlast=parameter["gesamtlast"],
|
||||||
|
pv_prognose_wh=pv_forecast,
|
||||||
|
strompreis_euro_pro_wh=specific_date_prices,
|
||||||
|
einspeiseverguetung_euro_pro_wh=einspeiseverguetung_euro_pro_wh,
|
||||||
|
eauto=eauto,
|
||||||
|
haushaltsgeraet=spuelmaschine,
|
||||||
|
wechselrichter=wr,
|
||||||
|
)
|
||||||
o = ems.simuliere(start_hour)
|
o = ems.simuliere(start_hour)
|
||||||
|
|
||||||
###############
|
###############
|
||||||
@ -386,12 +451,25 @@ class optimization_problem:
|
|||||||
o = self.evaluate_inner(best_solution, ems, start_hour)
|
o = self.evaluate_inner(best_solution, ems, start_hour)
|
||||||
eauto = ems.eauto.to_dict()
|
eauto = ems.eauto.to_dict()
|
||||||
spuelstart_int = None
|
spuelstart_int = None
|
||||||
discharge_hours_bin, eautocharge_hours_float, spuelstart_int = self.split_individual(best_solution)
|
discharge_hours_bin, eautocharge_hours_float, spuelstart_int = (
|
||||||
|
self.split_individual(best_solution)
|
||||||
|
)
|
||||||
|
|
||||||
print(parameter)
|
print(parameter)
|
||||||
print(best_solution)
|
print(best_solution)
|
||||||
visualisiere_ergebnisse(parameter["gesamtlast"], pv_forecast, specific_date_prices, o,discharge_hours_bin,eautocharge_hours_float , temperature_forecast, start_hour, self.prediction_hours,einspeiseverguetung_euro_pro_wh,extra_data=extra_data)
|
visualisiere_ergebnisse(
|
||||||
|
parameter["gesamtlast"],
|
||||||
|
pv_forecast,
|
||||||
|
specific_date_prices,
|
||||||
|
o,
|
||||||
|
discharge_hours_bin,
|
||||||
|
eautocharge_hours_float,
|
||||||
|
temperature_forecast,
|
||||||
|
start_hour,
|
||||||
|
self.prediction_hours,
|
||||||
|
einspeiseverguetung_euro_pro_wh,
|
||||||
|
extra_data=extra_data,
|
||||||
|
)
|
||||||
os.system("cp visualisierungsergebnisse.pdf ~/")
|
os.system("cp visualisierungsergebnisse.pdf ~/")
|
||||||
|
|
||||||
# 'Eigenverbrauch_Wh_pro_Stunde': eigenverbrauch_wh_pro_stunde,
|
# 'Eigenverbrauch_Wh_pro_Stunde': eigenverbrauch_wh_pro_stunde,
|
||||||
@ -409,9 +487,12 @@ class optimization_problem:
|
|||||||
# "Haushaltsgeraet_wh_pro_stunde":haushaltsgeraet_wh_pro_stunde
|
# "Haushaltsgeraet_wh_pro_stunde":haushaltsgeraet_wh_pro_stunde
|
||||||
|
|
||||||
# print(eauto)
|
# print(eauto)
|
||||||
return {"discharge_hours_bin":discharge_hours_bin, "eautocharge_hours_float":eautocharge_hours_float ,"result":o ,"eauto_obj":eauto,"start_solution":best_solution,"spuelstart":spuelstart_int,"simulation_data":o}
|
return {
|
||||||
|
"discharge_hours_bin": discharge_hours_bin,
|
||||||
|
"eautocharge_hours_float": eautocharge_hours_float,
|
||||||
|
"result": o,
|
||||||
|
"eauto_obj": eauto,
|
||||||
|
"start_solution": best_solution,
|
||||||
|
"spuelstart": spuelstart_int,
|
||||||
|
"simulation_data": o,
|
||||||
|
}
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
from flask import Flask, jsonify, request
|
import hashlib
|
||||||
import numpy as np
|
import json
|
||||||
from datetime import datetime, timedelta
|
import os
|
||||||
|
from datetime import datetime
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
import json, sys, os
|
|
||||||
import requests, hashlib
|
import numpy as np
|
||||||
from dateutil import parser
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
import requests
|
||||||
|
from dateutil import parser
|
||||||
|
|
||||||
|
|
||||||
class ForecastData:
|
class ForecastData:
|
||||||
def __init__(self, date_time, dc_power, ac_power, windspeed_10m=None, temperature=None, ac_power_measurement=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
date_time,
|
||||||
|
dc_power,
|
||||||
|
ac_power,
|
||||||
|
windspeed_10m=None,
|
||||||
|
temperature=None,
|
||||||
|
ac_power_measurement=None,
|
||||||
|
):
|
||||||
self.date_time = date_time
|
self.date_time = date_time
|
||||||
self.dc_power = dc_power
|
self.dc_power = dc_power
|
||||||
self.ac_power = ac_power
|
self.ac_power = ac_power
|
||||||
@ -38,8 +48,9 @@ class ForecastData:
|
|||||||
def get_temperature(self):
|
def get_temperature(self):
|
||||||
return self.temperature
|
return self.temperature
|
||||||
|
|
||||||
|
|
||||||
class PVForecast:
|
class PVForecast:
|
||||||
def __init__(self, filepath=None, url=None, cache_dir='cache', prediction_hours=48):
|
def __init__(self, filepath=None, url=None, cache_dir="cache", prediction_hours=48):
|
||||||
self.meta = {}
|
self.meta = {}
|
||||||
self.forecast_data = []
|
self.forecast_data = []
|
||||||
self.cache_dir = cache_dir
|
self.cache_dir = cache_dir
|
||||||
@ -54,29 +65,35 @@ class PVForecast:
|
|||||||
self.load_data_with_caching(url)
|
self.load_data_with_caching(url)
|
||||||
|
|
||||||
if len(self.forecast_data) < self.prediction_hours:
|
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.")
|
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):
|
def update_ac_power_measurement(self, date_time=None, ac_power_measurement=None):
|
||||||
found = False
|
found = False
|
||||||
input_date_hour = date_time.replace(minute=0, second=0, microsecond=0)
|
input_date_hour = date_time.replace(minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
for forecast in self.forecast_data:
|
for forecast in self.forecast_data:
|
||||||
forecast_date_hour = parser.parse(forecast.date_time).replace(minute=0, second=0, microsecond=0)
|
forecast_date_hour = parser.parse(forecast.date_time).replace(
|
||||||
|
minute=0, second=0, microsecond=0
|
||||||
|
)
|
||||||
if forecast_date_hour == input_date_hour:
|
if forecast_date_hour == input_date_hour:
|
||||||
forecast.ac_power_measurement = ac_power_measurement
|
forecast.ac_power_measurement = ac_power_measurement
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
|
|
||||||
def process_data(self, data):
|
def process_data(self, data):
|
||||||
self.meta = data.get('meta', {})
|
self.meta = data.get("meta", {})
|
||||||
all_values = data.get('values', [])
|
all_values = data.get("values", [])
|
||||||
|
|
||||||
for i in range(len(all_values[0])): # Annahme, dass alle Listen gleich lang sind
|
for i in range(
|
||||||
sum_dc_power = sum(values[i]['dcPower'] for values in all_values)
|
len(all_values[0])
|
||||||
sum_ac_power = sum(values[i]['power'] for values in all_values)
|
): # Annahme, dass alle Listen gleich lang sind
|
||||||
|
sum_dc_power = sum(values[i]["dcPower"] for values in all_values)
|
||||||
|
sum_ac_power = sum(values[i]["power"] for values in all_values)
|
||||||
|
|
||||||
# Zeige die ursprünglichen und berechneten Zeitstempel an
|
# Zeige die ursprünglichen und berechneten Zeitstempel an
|
||||||
original_datetime = all_values[0][i].get('datetime')
|
original_datetime = all_values[0][i].get("datetime")
|
||||||
# print(original_datetime," ",sum_dc_power," ",all_values[0][i]['dcPower'])
|
# print(original_datetime," ",sum_dc_power," ",all_values[0][i]['dcPower'])
|
||||||
dt = datetime.strptime(original_datetime, "%Y-%m-%dT%H:%M:%S.%f%z")
|
dt = datetime.strptime(original_datetime, "%Y-%m-%dT%H:%M:%S.%f%z")
|
||||||
dt = dt.replace(tzinfo=None)
|
dt = dt.replace(tzinfo=None)
|
||||||
@ -90,15 +107,14 @@ class PVForecast:
|
|||||||
date_time=dt, # Verwende angepassten Zeitstempel
|
date_time=dt, # Verwende angepassten Zeitstempel
|
||||||
dc_power=sum_dc_power,
|
dc_power=sum_dc_power,
|
||||||
ac_power=sum_ac_power,
|
ac_power=sum_ac_power,
|
||||||
windspeed_10m=all_values[0][i].get('windspeed_10m'),
|
windspeed_10m=all_values[0][i].get("windspeed_10m"),
|
||||||
temperature=all_values[0][i].get('temperature')
|
temperature=all_values[0][i].get("temperature"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
self.forecast_data.append(forecast)
|
self.forecast_data.append(forecast)
|
||||||
|
|
||||||
def load_data_from_file(self, filepath):
|
def load_data_from_file(self, filepath):
|
||||||
with open(filepath, 'r') as file:
|
with open(filepath, "r") as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
self.process_data(data)
|
self.process_data(data)
|
||||||
|
|
||||||
@ -109,31 +125,37 @@ class PVForecast:
|
|||||||
pprint(data)
|
pprint(data)
|
||||||
self.process_data(data)
|
self.process_data(data)
|
||||||
else:
|
else:
|
||||||
print(f"Failed to load data from {url}. Status Code: {response.status_code}")
|
print(
|
||||||
|
f"Failed to load data from {url}. Status Code: {response.status_code}"
|
||||||
|
)
|
||||||
self.load_data_from_url(url)
|
self.load_data_from_url(url)
|
||||||
|
|
||||||
def load_data_with_caching(self, url):
|
def load_data_with_caching(self, url):
|
||||||
date = datetime.now().strftime("%Y-%m-%d")
|
date = datetime.now().strftime("%Y-%m-%d")
|
||||||
|
|
||||||
cache_file = os.path.join(self.cache_dir, self.generate_cache_filename(url, date))
|
cache_file = os.path.join(
|
||||||
|
self.cache_dir, self.generate_cache_filename(url, date)
|
||||||
|
)
|
||||||
if os.path.exists(cache_file):
|
if os.path.exists(cache_file):
|
||||||
with open(cache_file, 'r') as file:
|
with open(cache_file, "r") as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
print("Loading data from cache.")
|
print("Loading data from cache.")
|
||||||
else:
|
else:
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
data = response.json()
|
data = response.json()
|
||||||
with open(cache_file, 'w') as file:
|
with open(cache_file, "w") as file:
|
||||||
json.dump(data, file)
|
json.dump(data, file)
|
||||||
print("Data fetched from URL and cached.")
|
print("Data fetched from URL and cached.")
|
||||||
else:
|
else:
|
||||||
print(f"Failed to load data from {url}. Status Code: {response.status_code}")
|
print(
|
||||||
|
f"Failed to load data from {url}. Status Code: {response.status_code}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
self.process_data(data)
|
self.process_data(data)
|
||||||
|
|
||||||
def generate_cache_filename(self, url, date):
|
def generate_cache_filename(self, url, date):
|
||||||
cache_key = hashlib.sha256(f"{url}{date}".encode('utf-8')).hexdigest()
|
cache_key = hashlib.sha256(f"{url}{date}".encode("utf-8")).hexdigest()
|
||||||
return f"cache_{cache_key}.json"
|
return f"cache_{cache_key}.json"
|
||||||
|
|
||||||
def get_forecast_data(self):
|
def get_forecast_data(self):
|
||||||
@ -141,7 +163,11 @@ class PVForecast:
|
|||||||
|
|
||||||
def get_temperature_forecast_for_date(self, input_date_str):
|
def get_temperature_forecast_for_date(self, input_date_str):
|
||||||
input_date = datetime.strptime(input_date_str, "%Y-%m-%d")
|
input_date = datetime.strptime(input_date_str, "%Y-%m-%d")
|
||||||
daily_forecast_obj = [data for data in self.forecast_data if parser.parse(data.get_date_time()).date() == input_date.date()]
|
daily_forecast_obj = [
|
||||||
|
data
|
||||||
|
for data in self.forecast_data
|
||||||
|
if parser.parse(data.get_date_time()).date() == input_date.date()
|
||||||
|
]
|
||||||
daily_forecast = []
|
daily_forecast = []
|
||||||
for d in daily_forecast_obj:
|
for d in daily_forecast_obj:
|
||||||
daily_forecast.append(d.get_temperature())
|
daily_forecast.append(d.get_temperature())
|
||||||
@ -154,12 +180,16 @@ class PVForecast:
|
|||||||
date_range_forecast = []
|
date_range_forecast = []
|
||||||
|
|
||||||
for data in self.forecast_data:
|
for data in self.forecast_data:
|
||||||
data_date = data.get_date_time().date()#parser.parse(data.get_date_time()).date()
|
data_date = (
|
||||||
|
data.get_date_time().date()
|
||||||
|
) # parser.parse(data.get_date_time()).date()
|
||||||
if start_date <= data_date <= end_date:
|
if start_date <= data_date <= end_date:
|
||||||
date_range_forecast.append(data)
|
date_range_forecast.append(data)
|
||||||
print(data.get_date_time(), " ", data.get_ac_power())
|
print(data.get_date_time(), " ", data.get_ac_power())
|
||||||
|
|
||||||
ac_power_forecast = np.array([data.get_ac_power() for data in date_range_forecast])
|
ac_power_forecast = np.array(
|
||||||
|
[data.get_ac_power() for data in date_range_forecast]
|
||||||
|
)
|
||||||
|
|
||||||
return np.array(ac_power_forecast)[: self.prediction_hours]
|
return np.array(ac_power_forecast)[: self.prediction_hours]
|
||||||
|
|
||||||
@ -178,27 +208,37 @@ class PVForecast:
|
|||||||
|
|
||||||
def get_forecast_dataframe(self):
|
def get_forecast_dataframe(self):
|
||||||
# Wandelt die Vorhersagedaten in ein Pandas DataFrame um
|
# Wandelt die Vorhersagedaten in ein Pandas DataFrame um
|
||||||
data = [{
|
data = [
|
||||||
'date_time': f.get_date_time(),
|
{
|
||||||
'dc_power': f.get_dc_power(),
|
"date_time": f.get_date_time(),
|
||||||
'ac_power': f.get_ac_power(),
|
"dc_power": f.get_dc_power(),
|
||||||
'windspeed_10m': f.get_windspeed_10m(),
|
"ac_power": f.get_ac_power(),
|
||||||
'temperature': f.get_temperature()
|
"windspeed_10m": f.get_windspeed_10m(),
|
||||||
} for f in self.forecast_data]
|
"temperature": f.get_temperature(),
|
||||||
|
}
|
||||||
|
for f in self.forecast_data
|
||||||
|
]
|
||||||
|
|
||||||
# Erstelle ein DataFrame
|
# Erstelle ein DataFrame
|
||||||
df = pd.DataFrame(data)
|
df = pd.DataFrame(data)
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
def print_ac_power_and_measurement(self):
|
def print_ac_power_and_measurement(self):
|
||||||
"""Druckt die DC-Leistung und den Messwert für jede Stunde."""
|
"""Druckt die DC-Leistung und den Messwert für jede Stunde."""
|
||||||
for forecast in self.forecast_data:
|
for forecast in self.forecast_data:
|
||||||
date_time = forecast.date_time
|
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()}")
|
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
|
# Beispiel für die Verwendung der Klasse
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
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 = PVForecast(
|
||||||
forecast.update_ac_power_measurement(date_time=datetime.now(), ac_power_measurement=1000)
|
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()
|
forecast.print_ac_power_and_measurement()
|
||||||
|
@ -1,13 +1,21 @@
|
|||||||
import mariadb
|
|
||||||
import pandas as pd
|
|
||||||
import numpy as np
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from scipy.integrate import simpson
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
import mariadb
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
class BatteryDataProcessor:
|
class BatteryDataProcessor:
|
||||||
def __init__(self, config, voltage_high_threshold, voltage_low_threshold, current_low_threshold, gap, battery_capacity_ah):
|
def __init__(
|
||||||
|
self,
|
||||||
|
config,
|
||||||
|
voltage_high_threshold,
|
||||||
|
voltage_low_threshold,
|
||||||
|
current_low_threshold,
|
||||||
|
gap,
|
||||||
|
battery_capacity_ah,
|
||||||
|
):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.voltage_high_threshold = voltage_high_threshold
|
self.voltage_high_threshold = voltage_high_threshold
|
||||||
self.voltage_low_threshold = voltage_low_threshold
|
self.voltage_low_threshold = voltage_low_threshold
|
||||||
@ -35,32 +43,34 @@ class BatteryDataProcessor:
|
|||||||
"""
|
"""
|
||||||
self.cursor.execute(query, (start_time,))
|
self.cursor.execute(query, (start_time,))
|
||||||
rows = self.cursor.fetchall()
|
rows = self.cursor.fetchall()
|
||||||
self.data = pd.DataFrame(rows, columns=['timestamp', 'data', 'topic'])
|
self.data = pd.DataFrame(rows, columns=["timestamp", "data", "topic"])
|
||||||
self.data['timestamp'] = pd.to_datetime(self.data['timestamp'])
|
self.data["timestamp"] = pd.to_datetime(self.data["timestamp"])
|
||||||
self.data['data'] = self.data['data'].astype(float)
|
self.data["data"] = self.data["data"].astype(float)
|
||||||
|
|
||||||
def process_data(self):
|
def process_data(self):
|
||||||
self.data.drop_duplicates(subset=['timestamp', 'topic'], inplace=True)
|
self.data.drop_duplicates(subset=["timestamp", "topic"], inplace=True)
|
||||||
|
|
||||||
data_pivot = self.data.pivot(index='timestamp', columns='topic', values='data')
|
data_pivot = self.data.pivot(index="timestamp", columns="topic", values="data")
|
||||||
data_pivot = data_pivot.resample('1T').mean().interpolate()
|
data_pivot = data_pivot.resample("1T").mean().interpolate()
|
||||||
data_pivot.columns.name = None
|
data_pivot.columns.name = None
|
||||||
data_pivot.reset_index(inplace=True)
|
data_pivot.reset_index(inplace=True)
|
||||||
self.data = data_pivot
|
self.data = data_pivot
|
||||||
|
|
||||||
def group_points(self, df):
|
def group_points(self, df):
|
||||||
df = df.sort_values('timestamp')
|
df = df.sort_values("timestamp")
|
||||||
groups = []
|
groups = []
|
||||||
group = []
|
group = []
|
||||||
last_time = None
|
last_time = None
|
||||||
|
|
||||||
for _, row in df.iterrows():
|
for _, row in df.iterrows():
|
||||||
if last_time is None or (row['timestamp'] - last_time) <= pd.Timedelta(minutes=self.gap):
|
if last_time is None or (row["timestamp"] - last_time) <= pd.Timedelta(
|
||||||
|
minutes=self.gap
|
||||||
|
):
|
||||||
group.append(row)
|
group.append(row)
|
||||||
else:
|
else:
|
||||||
groups.append(group)
|
groups.append(group)
|
||||||
group = [row]
|
group = [row]
|
||||||
last_time = row['timestamp']
|
last_time = row["timestamp"]
|
||||||
|
|
||||||
if group:
|
if group:
|
||||||
groups.append(group)
|
groups.append(group)
|
||||||
@ -69,11 +79,19 @@ class BatteryDataProcessor:
|
|||||||
return last_points
|
return last_points
|
||||||
|
|
||||||
def find_soc_points(self):
|
def find_soc_points(self):
|
||||||
condition_soc_100 = (self.data['battery_voltage'] >= self.voltage_high_threshold) & (self.data['battery_current'].abs() <= self.current_low_threshold)
|
condition_soc_100 = (
|
||||||
condition_soc_0 = (self.data['battery_voltage'] <= self.voltage_low_threshold) & (self.data['battery_current'].abs() <= self.current_low_threshold)
|
self.data["battery_voltage"] >= self.voltage_high_threshold
|
||||||
|
) & (self.data["battery_current"].abs() <= self.current_low_threshold)
|
||||||
|
condition_soc_0 = (
|
||||||
|
self.data["battery_voltage"] <= self.voltage_low_threshold
|
||||||
|
) & (self.data["battery_current"].abs() <= self.current_low_threshold)
|
||||||
|
|
||||||
times_soc_100_all = self.data[condition_soc_100][['timestamp', 'battery_voltage', 'battery_current']]
|
times_soc_100_all = self.data[condition_soc_100][
|
||||||
times_soc_0_all = self.data[condition_soc_0][['timestamp', 'battery_voltage', 'battery_current']]
|
["timestamp", "battery_voltage", "battery_current"]
|
||||||
|
]
|
||||||
|
times_soc_0_all = self.data[condition_soc_0][
|
||||||
|
["timestamp", "battery_voltage", "battery_current"]
|
||||||
|
]
|
||||||
|
|
||||||
last_points_100 = self.group_points(times_soc_100_all)
|
last_points_100 = self.group_points(times_soc_100_all)
|
||||||
last_points_0 = self.group_points(times_soc_0_all)
|
last_points_0 = self.group_points(times_soc_0_all)
|
||||||
@ -86,35 +104,46 @@ class BatteryDataProcessor:
|
|||||||
def calculate_resetting_soc(self, last_points_100_df, last_points_0_df):
|
def calculate_resetting_soc(self, last_points_100_df, last_points_0_df):
|
||||||
soc_values = []
|
soc_values = []
|
||||||
integration_results = []
|
integration_results = []
|
||||||
reset_points = pd.concat([last_points_100_df, last_points_0_df]).sort_values('timestamp')
|
reset_points = pd.concat([last_points_100_df, last_points_0_df]).sort_values(
|
||||||
|
"timestamp"
|
||||||
|
)
|
||||||
|
|
||||||
# Initialisieren der SoC-Liste
|
# Initialisieren der SoC-Liste
|
||||||
self.data['calculated_soc'] = np.nan
|
self.data["calculated_soc"] = np.nan
|
||||||
|
|
||||||
for i in range(len(reset_points)):
|
for i in range(len(reset_points)):
|
||||||
start_point = reset_points.iloc[i]
|
start_point = reset_points.iloc[i]
|
||||||
if i < len(reset_points) - 1:
|
if i < len(reset_points) - 1:
|
||||||
end_point = reset_points.iloc[i + 1]
|
end_point = reset_points.iloc[i + 1]
|
||||||
else:
|
else:
|
||||||
end_point = self.data.iloc[-1] # Verwenden des letzten Datensatzes als Endpunkt
|
end_point = self.data.iloc[
|
||||||
|
-1
|
||||||
|
] # Verwenden des letzten Datensatzes als Endpunkt
|
||||||
|
|
||||||
if start_point['timestamp'] in last_points_100_df['timestamp'].values:
|
if start_point["timestamp"] in last_points_100_df["timestamp"].values:
|
||||||
initial_soc = 100
|
initial_soc = 100
|
||||||
elif start_point['timestamp'] in last_points_0_df['timestamp'].values:
|
elif start_point["timestamp"] in last_points_0_df["timestamp"].values:
|
||||||
initial_soc = 0
|
initial_soc = 0
|
||||||
|
|
||||||
cut_data = self.data[(self.data['timestamp'] >= start_point['timestamp']) & (self.data['timestamp'] <= end_point['timestamp'])].copy()
|
cut_data = self.data[
|
||||||
cut_data['time_diff_hours'] = cut_data['timestamp'].diff().dt.total_seconds() / 3600
|
(self.data["timestamp"] >= start_point["timestamp"])
|
||||||
cut_data.dropna(subset=['time_diff_hours'], inplace=True)
|
& (self.data["timestamp"] <= end_point["timestamp"])
|
||||||
|
].copy()
|
||||||
|
cut_data["time_diff_hours"] = (
|
||||||
|
cut_data["timestamp"].diff().dt.total_seconds() / 3600
|
||||||
|
)
|
||||||
|
cut_data.dropna(subset=["time_diff_hours"], inplace=True)
|
||||||
|
|
||||||
calculated_soc = initial_soc
|
calculated_soc = initial_soc
|
||||||
calculated_soc_list = [calculated_soc]
|
calculated_soc_list = [calculated_soc]
|
||||||
integrated_current = 0
|
integrated_current = 0
|
||||||
|
|
||||||
for j in range(1, len(cut_data)):
|
for j in range(1, len(cut_data)):
|
||||||
current = cut_data.iloc[j]['battery_current']
|
current = cut_data.iloc[j]["battery_current"]
|
||||||
delta_t = cut_data.iloc[j]['time_diff_hours']
|
delta_t = cut_data.iloc[j]["time_diff_hours"]
|
||||||
delta_soc = (current * delta_t) / self.battery_capacity_ah * 100 # Convert to percentage
|
delta_soc = (
|
||||||
|
(current * delta_t) / self.battery_capacity_ah * 100
|
||||||
|
) # Convert to percentage
|
||||||
|
|
||||||
calculated_soc += delta_soc
|
calculated_soc += delta_soc
|
||||||
calculated_soc = min(max(calculated_soc, 0), 100) # Clip to 0-100%
|
calculated_soc = min(max(calculated_soc, 0), 100) # Clip to 0-100%
|
||||||
@ -123,41 +152,51 @@ class BatteryDataProcessor:
|
|||||||
# Integration des Stroms aufaddieren
|
# Integration des Stroms aufaddieren
|
||||||
integrated_current += current * delta_t
|
integrated_current += current * delta_t
|
||||||
|
|
||||||
cut_data['calculated_soc'] = calculated_soc_list
|
cut_data["calculated_soc"] = calculated_soc_list
|
||||||
soc_values.append(cut_data[['timestamp', 'calculated_soc']])
|
soc_values.append(cut_data[["timestamp", "calculated_soc"]])
|
||||||
|
|
||||||
integration_results.append({
|
integration_results.append(
|
||||||
'start_time': start_point['timestamp'],
|
{
|
||||||
'end_time': end_point['timestamp'],
|
"start_time": start_point["timestamp"],
|
||||||
'integrated_current': integrated_current,
|
"end_time": end_point["timestamp"],
|
||||||
'start_soc': initial_soc,
|
"integrated_current": integrated_current,
|
||||||
'end_soc': calculated_soc_list[-1]
|
"start_soc": initial_soc,
|
||||||
})
|
"end_soc": calculated_soc_list[-1],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
soc_df = pd.concat(soc_values).drop_duplicates(subset=['timestamp']).reset_index(drop=True)
|
soc_df = (
|
||||||
|
pd.concat(soc_values)
|
||||||
|
.drop_duplicates(subset=["timestamp"])
|
||||||
|
.reset_index(drop=True)
|
||||||
|
)
|
||||||
return soc_df, integration_results
|
return soc_df, integration_results
|
||||||
|
|
||||||
|
|
||||||
def calculate_soh(self, integration_results):
|
def calculate_soh(self, integration_results):
|
||||||
soh_values = []
|
soh_values = []
|
||||||
|
|
||||||
for result in integration_results:
|
for result in integration_results:
|
||||||
delta_soc = abs(result['start_soc'] - result['end_soc']) # Use the actual change in SoC
|
delta_soc = abs(
|
||||||
|
result["start_soc"] - result["end_soc"]
|
||||||
|
) # Use the actual change in SoC
|
||||||
if delta_soc > 0: # Avoid division by zero
|
if delta_soc > 0: # Avoid division by zero
|
||||||
effective_capacity_ah = result['integrated_current']
|
effective_capacity_ah = result["integrated_current"]
|
||||||
soh = (effective_capacity_ah / self.battery_capacity_ah) * 100
|
soh = (effective_capacity_ah / self.battery_capacity_ah) * 100
|
||||||
soh_values.append({'timestamp': result['end_time'], 'soh': soh})
|
soh_values.append({"timestamp": result["end_time"], "soh": soh})
|
||||||
|
|
||||||
soh_df = pd.DataFrame(soh_values)
|
soh_df = pd.DataFrame(soh_values)
|
||||||
return soh_df
|
return soh_df
|
||||||
|
|
||||||
|
|
||||||
def delete_existing_soc_entries(self, soc_df):
|
def delete_existing_soc_entries(self, soc_df):
|
||||||
delete_query = """
|
delete_query = """
|
||||||
DELETE FROM pip
|
DELETE FROM pip
|
||||||
WHERE timestamp = %s AND topic = 'calculated_soc'
|
WHERE timestamp = %s AND topic = 'calculated_soc'
|
||||||
"""
|
"""
|
||||||
timestamps = [(row['timestamp'].strftime('%Y-%m-%d %H:%M:%S'),) for _, row in soc_df.iterrows() if pd.notna(row['timestamp'])]
|
timestamps = [
|
||||||
|
(row["timestamp"].strftime("%Y-%m-%d %H:%M:%S"),)
|
||||||
|
for _, row in soc_df.iterrows()
|
||||||
|
if pd.notna(row["timestamp"])
|
||||||
|
]
|
||||||
|
|
||||||
self.cursor.executemany(delete_query, timestamps)
|
self.cursor.executemany(delete_query, timestamps)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
@ -167,8 +206,8 @@ class BatteryDataProcessor:
|
|||||||
self.delete_existing_soc_entries(soc_df)
|
self.delete_existing_soc_entries(soc_df)
|
||||||
|
|
||||||
# Resample `soc_df` auf 5-Minuten-Intervalle und berechnen des Mittelwerts
|
# Resample `soc_df` auf 5-Minuten-Intervalle und berechnen des Mittelwerts
|
||||||
soc_df.set_index('timestamp', inplace=True)
|
soc_df.set_index("timestamp", inplace=True)
|
||||||
soc_df_resampled = soc_df.resample('5T').mean().dropna().reset_index()
|
soc_df_resampled = soc_df.resample("5T").mean().dropna().reset_index()
|
||||||
# soc_df_resampled['timestamp'] = soc_df_resampled['timestamp'].apply(lambda x: x.strftime('%Y-%m-%d %H:%M:%S'))
|
# soc_df_resampled['timestamp'] = soc_df_resampled['timestamp'].apply(lambda x: x.strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
print(soc_df_resampled)
|
print(soc_df_resampled)
|
||||||
|
|
||||||
@ -179,8 +218,11 @@ class BatteryDataProcessor:
|
|||||||
"""
|
"""
|
||||||
for _, row in soc_df_resampled.iterrows():
|
for _, row in soc_df_resampled.iterrows():
|
||||||
print(row)
|
print(row)
|
||||||
print(row['timestamp'])
|
print(row["timestamp"])
|
||||||
record = (row['timestamp'].strftime('%Y-%m-%d %H:%M:%S'), row['calculated_soc'])
|
record = (
|
||||||
|
row["timestamp"].strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
row["calculated_soc"],
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
self.cursor.execute(insert_query, record)
|
self.cursor.execute(insert_query, record)
|
||||||
except mariadb.OperationalError as e:
|
except mariadb.OperationalError as e:
|
||||||
@ -188,38 +230,57 @@ class BatteryDataProcessor:
|
|||||||
|
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def plot_data(self, last_points_100_df, last_points_0_df, soc_df):
|
def plot_data(self, last_points_100_df, last_points_0_df, soc_df):
|
||||||
plt.figure(figsize=(14, 10))
|
plt.figure(figsize=(14, 10))
|
||||||
|
|
||||||
plt.subplot(4, 1, 1)
|
plt.subplot(4, 1, 1)
|
||||||
plt.plot(self.data['timestamp'], self.data['battery_voltage'], label='Battery Voltage', color='blue')
|
plt.plot(
|
||||||
plt.scatter(last_points_100_df['timestamp'], last_points_100_df['battery_voltage'], color='green', marker='o', label='100% SoC Points')
|
self.data["timestamp"],
|
||||||
|
self.data["battery_voltage"],
|
||||||
|
label="Battery Voltage",
|
||||||
|
color="blue",
|
||||||
|
)
|
||||||
|
plt.scatter(
|
||||||
|
last_points_100_df["timestamp"],
|
||||||
|
last_points_100_df["battery_voltage"],
|
||||||
|
color="green",
|
||||||
|
marker="o",
|
||||||
|
label="100% SoC Points",
|
||||||
|
)
|
||||||
# plt.scatter(last_points_0_df['timestamp'], last_points_0_df['battery_voltage'], color='red', marker='x', label='0% SoC Points')
|
# plt.scatter(last_points_0_df['timestamp'], last_points_0_df['battery_voltage'], color='red', marker='x', label='0% SoC Points')
|
||||||
plt.xlabel('Timestamp')
|
plt.xlabel("Timestamp")
|
||||||
plt.ylabel('Voltage (V)')
|
plt.ylabel("Voltage (V)")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.title('Battery Voltage over Time')
|
plt.title("Battery Voltage over Time")
|
||||||
|
|
||||||
plt.subplot(4, 1, 2)
|
plt.subplot(4, 1, 2)
|
||||||
plt.plot(self.data['timestamp'], self.data['battery_current'], label='Battery Current', color='orange')
|
plt.plot(
|
||||||
plt.scatter(last_points_100_df['timestamp'], last_points_100_df['battery_current'], color='green', marker='o', label='100% SoC Points')
|
self.data["timestamp"],
|
||||||
|
self.data["battery_current"],
|
||||||
|
label="Battery Current",
|
||||||
|
color="orange",
|
||||||
|
)
|
||||||
|
plt.scatter(
|
||||||
|
last_points_100_df["timestamp"],
|
||||||
|
last_points_100_df["battery_current"],
|
||||||
|
color="green",
|
||||||
|
marker="o",
|
||||||
|
label="100% SoC Points",
|
||||||
|
)
|
||||||
# plt.scatter(last_points_0_df['timestamp'], last_points_0_df['battery_current'], color='red', marker='x', label='0% SoC Points')
|
# plt.scatter(last_points_0_df['timestamp'], last_points_0_df['battery_current'], color='red', marker='x', label='0% SoC Points')
|
||||||
plt.xlabel('Timestamp')
|
plt.xlabel("Timestamp")
|
||||||
plt.ylabel('Current (A)')
|
plt.ylabel("Current (A)")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.title('Battery Current over Time')
|
plt.title("Battery Current over Time")
|
||||||
|
|
||||||
plt.subplot(4, 1, 3)
|
plt.subplot(4, 1, 3)
|
||||||
plt.plot(soc_df['timestamp'], soc_df['calculated_soc'], label='SoC', color='purple')
|
plt.plot(
|
||||||
plt.xlabel('Timestamp')
|
soc_df["timestamp"], soc_df["calculated_soc"], label="SoC", color="purple"
|
||||||
plt.ylabel('SoC (%)')
|
)
|
||||||
|
plt.xlabel("Timestamp")
|
||||||
|
plt.ylabel("SoC (%)")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.title('State of Charge (SoC) over Time')
|
plt.title("State of Charge (SoC) over Time")
|
||||||
|
|
||||||
# plt.subplot(4, 1, 4)
|
# plt.subplot(4, 1, 4)
|
||||||
# plt.plot(soh_df['timestamp'], soh_df['soh'], label='SoH', color='brown')
|
# plt.plot(soh_df['timestamp'], soh_df['soh'], label='SoH', color='brown')
|
||||||
@ -228,16 +289,13 @@ class BatteryDataProcessor:
|
|||||||
# plt.legend()
|
# plt.legend()
|
||||||
# plt.title('State of Health (SoH) over Time')
|
# plt.title('State of Health (SoH) over Time')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
# MariaDB Verbindungsdetails
|
# MariaDB Verbindungsdetails
|
||||||
|
|
||||||
|
|
||||||
# Parameter festlegen
|
# Parameter festlegen
|
||||||
voltage_high_threshold = 55.4 # 100% SoC
|
voltage_high_threshold = 55.4 # 100% SoC
|
||||||
voltage_low_threshold = 46.5 # 0% SoC
|
voltage_low_threshold = 46.5 # 0% SoC
|
||||||
@ -246,26 +304,27 @@ if __name__ == '__main__':
|
|||||||
bat_capacity = 33 * 1000 / 48
|
bat_capacity = 33 * 1000 / 48
|
||||||
|
|
||||||
# Zeitpunkt X definieren
|
# Zeitpunkt X definieren
|
||||||
zeitpunkt_x = (datetime.now() - timedelta(weeks=100)).strftime('%Y-%m-%d %H:%M:%S')
|
zeitpunkt_x = (datetime.now() - timedelta(weeks=100)).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
|
||||||
# BatteryDataProcessor instanziieren und verwenden
|
# BatteryDataProcessor instanziieren und verwenden
|
||||||
processor = BatteryDataProcessor(config, voltage_high_threshold, voltage_low_threshold, current_low_threshold, gap,bat_capacity)
|
processor = BatteryDataProcessor(
|
||||||
|
config,
|
||||||
|
voltage_high_threshold,
|
||||||
|
voltage_low_threshold,
|
||||||
|
current_low_threshold,
|
||||||
|
gap,
|
||||||
|
bat_capacity,
|
||||||
|
)
|
||||||
processor.connect_db()
|
processor.connect_db()
|
||||||
processor.fetch_data(zeitpunkt_x)
|
processor.fetch_data(zeitpunkt_x)
|
||||||
processor.process_data()
|
processor.process_data()
|
||||||
last_points_100_df, last_points_0_df = processor.find_soc_points()
|
last_points_100_df, last_points_0_df = processor.find_soc_points()
|
||||||
soc_df, integration_results = processor.calculate_resetting_soc(last_points_100_df, last_points_0_df)
|
soc_df, integration_results = processor.calculate_resetting_soc(
|
||||||
|
last_points_100_df, last_points_0_df
|
||||||
|
)
|
||||||
# soh_df = processor.calculate_soh(integration_results)
|
# soh_df = processor.calculate_soh(integration_results)
|
||||||
processor.update_database_with_soc(soc_df)
|
processor.update_database_with_soc(soc_df)
|
||||||
|
|
||||||
|
|
||||||
processor.plot_data(last_points_100_df, last_points_0_df, soc_df)
|
processor.plot_data(last_points_100_df, last_points_0_df, soc_df)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
processor.disconnect_db()
|
processor.disconnect_db()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
|
|
||||||
def ist_dst_wechsel(tag, timezone="Europe/Berlin"):
|
def ist_dst_wechsel(tag, timezone="Europe/Berlin"):
|
||||||
"""Checks if Daylight Saving Time (DST) starts or ends on a given day."""
|
"""Checks if Daylight Saving Time (DST) starts or ends on a given day."""
|
||||||
tz = pytz.timezone(timezone)
|
tz = pytz.timezone(timezone)
|
||||||
@ -17,6 +19,7 @@ def ist_dst_wechsel(tag, timezone="Europe/Berlin"):
|
|||||||
|
|
||||||
return dst_change
|
return dst_change
|
||||||
|
|
||||||
|
|
||||||
# # Example usage
|
# # Example usage
|
||||||
# start_date = datetime.datetime(2024, 3, 31) # Date of the DST change
|
# start_date = datetime.datetime(2024, 3, 31) # Date of the DST change
|
||||||
# if ist_dst_wechsel(start_date):
|
# if ist_dst_wechsel(start_date):
|
||||||
|
@ -1,23 +1,27 @@
|
|||||||
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import hashlib
|
|
||||||
import requests
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pytz
|
import pytz
|
||||||
|
import requests
|
||||||
|
|
||||||
# Example: Converting a UTC timestamp to local time
|
# Example: Converting a UTC timestamp to local time
|
||||||
utc_time = datetime.strptime('2024-03-28T01:00:00.000Z', '%Y-%m-%dT%H:%M:%S.%fZ')
|
utc_time = datetime.strptime("2024-03-28T01:00:00.000Z", "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||||
utc_time = utc_time.replace(tzinfo=pytz.utc)
|
utc_time = utc_time.replace(tzinfo=pytz.utc)
|
||||||
|
|
||||||
# Replace 'Europe/Berlin' with your own timezone
|
# Replace 'Europe/Berlin' with your own timezone
|
||||||
local_time = utc_time.astimezone(pytz.timezone('Europe/Berlin'))
|
local_time = utc_time.astimezone(pytz.timezone("Europe/Berlin"))
|
||||||
print(local_time)
|
print(local_time)
|
||||||
|
|
||||||
|
|
||||||
def repeat_to_shape(array, target_shape):
|
def repeat_to_shape(array, target_shape):
|
||||||
# Check if the array fits the target shape
|
# Check if the array fits the target shape
|
||||||
if len(target_shape) != array.ndim:
|
if len(target_shape) != array.ndim:
|
||||||
raise ValueError("Array and target shape must have the same number of dimensions")
|
raise ValueError(
|
||||||
|
"Array and target shape must have the same number of dimensions"
|
||||||
|
)
|
||||||
|
|
||||||
# Number of repetitions per dimension
|
# Number of repetitions per dimension
|
||||||
repeats = tuple(target_shape[i] // array.shape[i] for i in range(array.ndim))
|
repeats = tuple(target_shape[i] // array.shape[i] for i in range(array.ndim))
|
||||||
@ -26,36 +30,39 @@ def repeat_to_shape(array, target_shape):
|
|||||||
expanded_array = np.tile(array, repeats)
|
expanded_array = np.tile(array, repeats)
|
||||||
return expanded_array
|
return expanded_array
|
||||||
|
|
||||||
|
|
||||||
class HourlyElectricityPriceForecast:
|
class HourlyElectricityPriceForecast:
|
||||||
def __init__(self, source, cache_dir='cache', charges=0.000228, prediction_hours=24): # 228
|
def __init__(
|
||||||
|
self, source, cache_dir="cache", charges=0.000228, prediction_hours=24
|
||||||
|
): # 228
|
||||||
self.cache_dir = cache_dir
|
self.cache_dir = cache_dir
|
||||||
os.makedirs(self.cache_dir, exist_ok=True)
|
os.makedirs(self.cache_dir, exist_ok=True)
|
||||||
self.cache_time_file = os.path.join(self.cache_dir, 'cache_timestamp.txt')
|
self.cache_time_file = os.path.join(self.cache_dir, "cache_timestamp.txt")
|
||||||
self.prices = self.load_data(source)
|
self.prices = self.load_data(source)
|
||||||
self.charges = charges
|
self.charges = charges
|
||||||
self.prediction_hours = prediction_hours
|
self.prediction_hours = prediction_hours
|
||||||
|
|
||||||
def load_data(self, source):
|
def load_data(self, source):
|
||||||
cache_filename = self.get_cache_filename(source)
|
cache_filename = self.get_cache_filename(source)
|
||||||
if source.startswith('http'):
|
if source.startswith("http"):
|
||||||
if os.path.exists(cache_filename) and not self.is_cache_expired():
|
if os.path.exists(cache_filename) and not self.is_cache_expired():
|
||||||
print("Loading data from cache...")
|
print("Loading data from cache...")
|
||||||
with open(cache_filename, 'r') as file:
|
with open(cache_filename, "r") as file:
|
||||||
json_data = json.load(file)
|
json_data = json.load(file)
|
||||||
else:
|
else:
|
||||||
print("Loading data from the URL...")
|
print("Loading data from the URL...")
|
||||||
response = requests.get(source)
|
response = requests.get(source)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
json_data = response.json()
|
json_data = response.json()
|
||||||
with open(cache_filename, 'w') as file:
|
with open(cache_filename, "w") as file:
|
||||||
json.dump(json_data, file)
|
json.dump(json_data, file)
|
||||||
self.update_cache_timestamp()
|
self.update_cache_timestamp()
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Error fetching data: {response.status_code}")
|
raise Exception(f"Error fetching data: {response.status_code}")
|
||||||
else:
|
else:
|
||||||
with open(source, 'r') as file:
|
with open(source, "r") as file:
|
||||||
json_data = json.load(file)
|
json_data = json.load(file)
|
||||||
return json_data['values']
|
return json_data["values"]
|
||||||
|
|
||||||
def get_cache_filename(self, url):
|
def get_cache_filename(self, url):
|
||||||
hash_object = hashlib.sha256(url.encode())
|
hash_object = hashlib.sha256(url.encode())
|
||||||
@ -65,34 +72,36 @@ class HourlyElectricityPriceForecast:
|
|||||||
def is_cache_expired(self):
|
def is_cache_expired(self):
|
||||||
if not os.path.exists(self.cache_time_file):
|
if not os.path.exists(self.cache_time_file):
|
||||||
return True
|
return True
|
||||||
with open(self.cache_time_file, 'r') as file:
|
with open(self.cache_time_file, "r") as file:
|
||||||
timestamp_str = file.read()
|
timestamp_str = file.read()
|
||||||
last_cache_time = datetime.strptime(timestamp_str, '%Y-%m-%d %H:%M:%S')
|
last_cache_time = datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S")
|
||||||
return datetime.now() - last_cache_time > timedelta(hours=1)
|
return datetime.now() - last_cache_time > timedelta(hours=1)
|
||||||
|
|
||||||
def update_cache_timestamp(self):
|
def update_cache_timestamp(self):
|
||||||
with open(self.cache_time_file, 'w') as file:
|
with open(self.cache_time_file, "w") as file:
|
||||||
file.write(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
|
file.write(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||||
|
|
||||||
def get_price_for_date(self, date_str):
|
def get_price_for_date(self, date_str):
|
||||||
"""Returns all prices for the specified date, including the price from 00:00 of the previous day."""
|
"""Returns all prices for the specified date, including the price from 00:00 of the previous day."""
|
||||||
# Convert date string to datetime object
|
# Convert date string to datetime object
|
||||||
date_obj = datetime.strptime(date_str, '%Y-%m-%d')
|
date_obj = datetime.strptime(date_str, "%Y-%m-%d")
|
||||||
|
|
||||||
# Calculate the previous day
|
# Calculate the previous day
|
||||||
previous_day = date_obj - timedelta(days=1)
|
previous_day = date_obj - timedelta(days=1)
|
||||||
previous_day_str = previous_day.strftime('%Y-%m-%d')
|
previous_day_str = previous_day.strftime("%Y-%m-%d")
|
||||||
|
|
||||||
# Extract the price from 00:00 of the previous day
|
# Extract the price from 00:00 of the previous day
|
||||||
last_price_of_previous_day = [
|
last_price_of_previous_day = [
|
||||||
entry["marketpriceEurocentPerKWh"] + self.charges
|
entry["marketpriceEurocentPerKWh"] + self.charges
|
||||||
for entry in self.prices if previous_day_str in entry['end']
|
for entry in self.prices
|
||||||
|
if previous_day_str in entry["end"]
|
||||||
][-1]
|
][-1]
|
||||||
|
|
||||||
# Extract all prices for the specified date
|
# Extract all prices for the specified date
|
||||||
date_prices = [
|
date_prices = [
|
||||||
entry["marketpriceEurocentPerKWh"] + self.charges
|
entry["marketpriceEurocentPerKWh"] + self.charges
|
||||||
for entry in self.prices if date_str in entry['end']
|
for entry in self.prices
|
||||||
|
if date_str in entry["end"]
|
||||||
]
|
]
|
||||||
print(f"getPrice: {len(date_prices)}")
|
print(f"getPrice: {len(date_prices)}")
|
||||||
|
|
||||||
@ -106,10 +115,14 @@ class HourlyElectricityPriceForecast:
|
|||||||
"""Returns all prices between the start and end dates."""
|
"""Returns all prices between the start and end dates."""
|
||||||
print(start_date_str)
|
print(start_date_str)
|
||||||
print(end_date_str)
|
print(end_date_str)
|
||||||
start_date_utc = datetime.strptime(start_date_str, "%Y-%m-%d").replace(tzinfo=pytz.utc)
|
start_date_utc = datetime.strptime(start_date_str, "%Y-%m-%d").replace(
|
||||||
end_date_utc = datetime.strptime(end_date_str, "%Y-%m-%d").replace(tzinfo=pytz.utc)
|
tzinfo=pytz.utc
|
||||||
start_date = start_date_utc.astimezone(pytz.timezone('Europe/Berlin'))
|
)
|
||||||
end_date = end_date_utc.astimezone(pytz.timezone('Europe/Berlin'))
|
end_date_utc = datetime.strptime(end_date_str, "%Y-%m-%d").replace(
|
||||||
|
tzinfo=pytz.utc
|
||||||
|
)
|
||||||
|
start_date = start_date_utc.astimezone(pytz.timezone("Europe/Berlin"))
|
||||||
|
end_date = end_date_utc.astimezone(pytz.timezone("Europe/Berlin"))
|
||||||
|
|
||||||
price_list = []
|
price_list = []
|
||||||
|
|
||||||
|
@ -1,21 +1,34 @@
|
|||||||
import numpy as np
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from matplotlib.backends.backend_pdf import PdfPages
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from modules.class_sommerzeit import * # Ensure this matches the actual import path
|
|
||||||
from modules.class_load_container import Gesamtlast # Ensure this matches the actual import path
|
|
||||||
|
|
||||||
# Set the backend for matplotlib to Agg
|
# Set the backend for matplotlib to Agg
|
||||||
import matplotlib
|
import matplotlib
|
||||||
matplotlib.use('Agg')
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
from matplotlib.backends.backend_pdf import PdfPages
|
||||||
|
|
||||||
def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, discharge_hours, laden_moeglich, temperature, start_hour, prediction_hours, einspeiseverguetung_euro_pro_wh, filename="visualization_results.pdf", extra_data=None):
|
from modules.class_sommerzeit import * # Ensure this matches the actual import path
|
||||||
|
|
||||||
|
matplotlib.use("Agg")
|
||||||
|
|
||||||
|
|
||||||
|
def visualisiere_ergebnisse(
|
||||||
|
gesamtlast,
|
||||||
|
pv_forecast,
|
||||||
|
strompreise,
|
||||||
|
ergebnisse,
|
||||||
|
discharge_hours,
|
||||||
|
laden_moeglich,
|
||||||
|
temperature,
|
||||||
|
start_hour,
|
||||||
|
prediction_hours,
|
||||||
|
einspeiseverguetung_euro_pro_wh,
|
||||||
|
filename="visualization_results.pdf",
|
||||||
|
extra_data=None,
|
||||||
|
):
|
||||||
#####################
|
#####################
|
||||||
# 24-hour visualization
|
# 24-hour visualization
|
||||||
#####################
|
#####################
|
||||||
with PdfPages(filename) as pdf:
|
with PdfPages(filename) as pdf:
|
||||||
|
|
||||||
# Load and PV generation
|
# Load and PV generation
|
||||||
plt.figure(figsize=(14, 14))
|
plt.figure(figsize=(14, 14))
|
||||||
plt.subplot(3, 3, 1)
|
plt.subplot(3, 3, 1)
|
||||||
@ -23,50 +36,68 @@ def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, di
|
|||||||
|
|
||||||
gesamtlast_array = np.array(gesamtlast)
|
gesamtlast_array = np.array(gesamtlast)
|
||||||
# Plot individual loads
|
# Plot individual loads
|
||||||
plt.plot(hours, gesamtlast_array, label='Load (Wh)', marker='o')
|
plt.plot(hours, gesamtlast_array, label="Load (Wh)", marker="o")
|
||||||
|
|
||||||
# Calculate and plot total load
|
# Calculate and plot total load
|
||||||
plt.plot(hours, gesamtlast_array, label='Total Load (Wh)', marker='o', linewidth=2, linestyle='--')
|
plt.plot(
|
||||||
plt.xlabel('Hour')
|
hours,
|
||||||
plt.ylabel('Load (Wh)')
|
gesamtlast_array,
|
||||||
plt.title('Load Profiles')
|
label="Total Load (Wh)",
|
||||||
|
marker="o",
|
||||||
|
linewidth=2,
|
||||||
|
linestyle="--",
|
||||||
|
)
|
||||||
|
plt.xlabel("Hour")
|
||||||
|
plt.ylabel("Load (Wh)")
|
||||||
|
plt.title("Load Profiles")
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
plt.legend()
|
plt.legend()
|
||||||
|
|
||||||
# Electricity prices
|
# Electricity prices
|
||||||
hours_p = np.arange(0, len(strompreise))
|
hours_p = np.arange(0, len(strompreise))
|
||||||
plt.subplot(3, 2, 2)
|
plt.subplot(3, 2, 2)
|
||||||
plt.plot(hours_p, strompreise, label='Electricity Price (€/Wh)', color='purple', marker='s')
|
plt.plot(
|
||||||
plt.title('Electricity Prices')
|
hours_p,
|
||||||
plt.xlabel('Hour of the Day')
|
strompreise,
|
||||||
plt.ylabel('Price (€/Wh)')
|
label="Electricity Price (€/Wh)",
|
||||||
|
color="purple",
|
||||||
|
marker="s",
|
||||||
|
)
|
||||||
|
plt.title("Electricity Prices")
|
||||||
|
plt.xlabel("Hour of the Day")
|
||||||
|
plt.ylabel("Price (€/Wh)")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
|
|
||||||
# PV forecast
|
# PV forecast
|
||||||
plt.subplot(3, 2, 3)
|
plt.subplot(3, 2, 3)
|
||||||
plt.plot(hours, pv_forecast, label='PV Generation (Wh)', marker='x')
|
plt.plot(hours, pv_forecast, label="PV Generation (Wh)", marker="x")
|
||||||
plt.title('PV Forecast')
|
plt.title("PV Forecast")
|
||||||
plt.xlabel('Hour of the Day')
|
plt.xlabel("Hour of the Day")
|
||||||
plt.ylabel('Wh')
|
plt.ylabel("Wh")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
|
|
||||||
# Feed-in remuneration
|
# Feed-in remuneration
|
||||||
plt.subplot(3, 2, 4)
|
plt.subplot(3, 2, 4)
|
||||||
plt.plot(hours, einspeiseverguetung_euro_pro_wh, label='Remuneration (€/Wh)', marker='x')
|
plt.plot(
|
||||||
plt.title('Remuneration')
|
hours,
|
||||||
plt.xlabel('Hour of the Day')
|
einspeiseverguetung_euro_pro_wh,
|
||||||
plt.ylabel('€/Wh')
|
label="Remuneration (€/Wh)",
|
||||||
|
marker="x",
|
||||||
|
)
|
||||||
|
plt.title("Remuneration")
|
||||||
|
plt.xlabel("Hour of the Day")
|
||||||
|
plt.ylabel("€/Wh")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
|
|
||||||
# Temperature forecast
|
# Temperature forecast
|
||||||
plt.subplot(3, 2, 5)
|
plt.subplot(3, 2, 5)
|
||||||
plt.title('Temperature Forecast (°C)')
|
plt.title("Temperature Forecast (°C)")
|
||||||
plt.plot(hours, temperature, label='Temperature (°C)', marker='x')
|
plt.plot(hours, temperature, label="Temperature (°C)", marker="x")
|
||||||
plt.xlabel('Hour of the Day')
|
plt.xlabel("Hour of the Day")
|
||||||
plt.ylabel('°C')
|
plt.ylabel("°C")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
plt.grid(True)
|
plt.grid(True)
|
||||||
|
|
||||||
@ -86,29 +117,69 @@ def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, di
|
|||||||
|
|
||||||
# Energy flow, grid feed-in, and grid consumption
|
# Energy flow, grid feed-in, and grid consumption
|
||||||
plt.subplot(3, 2, 1)
|
plt.subplot(3, 2, 1)
|
||||||
plt.plot(hours, ergebnisse['Last_Wh_pro_Stunde'], label='Load (Wh)', marker='o')
|
plt.plot(hours, ergebnisse["Last_Wh_pro_Stunde"], label="Load (Wh)", marker="o")
|
||||||
plt.plot(hours, ergebnisse['Haushaltsgeraet_wh_pro_stunde'], label='Household Device (Wh)', marker='o')
|
plt.plot(
|
||||||
plt.plot(hours, ergebnisse['Netzeinspeisung_Wh_pro_Stunde'], label='Grid Feed-in (Wh)', marker='x')
|
hours,
|
||||||
plt.plot(hours, ergebnisse['Netzbezug_Wh_pro_Stunde'], label='Grid Consumption (Wh)', marker='^')
|
ergebnisse["Haushaltsgeraet_wh_pro_stunde"],
|
||||||
plt.plot(hours, ergebnisse['Verluste_Pro_Stunde'], label='Losses (Wh)', marker='^')
|
label="Household Device (Wh)",
|
||||||
plt.title('Energy Flow per Hour')
|
marker="o",
|
||||||
plt.xlabel('Hour')
|
)
|
||||||
plt.ylabel('Energy (Wh)')
|
plt.plot(
|
||||||
|
hours,
|
||||||
|
ergebnisse["Netzeinspeisung_Wh_pro_Stunde"],
|
||||||
|
label="Grid Feed-in (Wh)",
|
||||||
|
marker="x",
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
hours,
|
||||||
|
ergebnisse["Netzbezug_Wh_pro_Stunde"],
|
||||||
|
label="Grid Consumption (Wh)",
|
||||||
|
marker="^",
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
hours, ergebnisse["Verluste_Pro_Stunde"], label="Losses (Wh)", marker="^"
|
||||||
|
)
|
||||||
|
plt.title("Energy Flow per Hour")
|
||||||
|
plt.xlabel("Hour")
|
||||||
|
plt.ylabel("Energy (Wh)")
|
||||||
plt.legend()
|
plt.legend()
|
||||||
|
|
||||||
# State of charge for batteries
|
# State of charge for batteries
|
||||||
plt.subplot(3, 2, 2)
|
plt.subplot(3, 2, 2)
|
||||||
plt.plot(hours, ergebnisse['akku_soc_pro_stunde'], label='PV Battery (%)', marker='x')
|
plt.plot(
|
||||||
plt.plot(hours, ergebnisse['E-Auto_SoC_pro_Stunde'], label='E-Car Battery (%)', marker='x')
|
hours, ergebnisse["akku_soc_pro_stunde"], label="PV Battery (%)", marker="x"
|
||||||
plt.legend(loc='upper left', bbox_to_anchor=(1, 1)) # Place legend outside the plot
|
)
|
||||||
plt.grid(True, which='both', axis='x') # Grid for every hour
|
plt.plot(
|
||||||
|
hours,
|
||||||
|
ergebnisse["E-Auto_SoC_pro_Stunde"],
|
||||||
|
label="E-Car Battery (%)",
|
||||||
|
marker="x",
|
||||||
|
)
|
||||||
|
plt.legend(
|
||||||
|
loc="upper left", bbox_to_anchor=(1, 1)
|
||||||
|
) # Place legend outside the plot
|
||||||
|
plt.grid(True, which="both", axis="x") # Grid for every hour
|
||||||
|
|
||||||
ax1 = plt.subplot(3, 2, 3)
|
ax1 = plt.subplot(3, 2, 3)
|
||||||
for hour, value in enumerate(discharge_hours):
|
for hour, value in enumerate(discharge_hours):
|
||||||
ax1.axvspan(hour, hour + 1, color='red', ymax=value, alpha=0.3, label='Discharge Possibility' if hour == 0 else "")
|
ax1.axvspan(
|
||||||
|
hour,
|
||||||
|
hour + 1,
|
||||||
|
color="red",
|
||||||
|
ymax=value,
|
||||||
|
alpha=0.3,
|
||||||
|
label="Discharge Possibility" if hour == 0 else "",
|
||||||
|
)
|
||||||
for hour, value in enumerate(laden_moeglich):
|
for hour, value in enumerate(laden_moeglich):
|
||||||
ax1.axvspan(hour, hour + 1, color='green', ymax=value, alpha=0.3, label='Charging Possibility' if hour == 0 else "")
|
ax1.axvspan(
|
||||||
ax1.legend(loc='upper left')
|
hour,
|
||||||
|
hour + 1,
|
||||||
|
color="green",
|
||||||
|
ymax=value,
|
||||||
|
alpha=0.3,
|
||||||
|
label="Charging Possibility" if hour == 0 else "",
|
||||||
|
)
|
||||||
|
ax1.legend(loc="upper left")
|
||||||
ax1.set_xlim(0, prediction_hours)
|
ax1.set_xlim(0, prediction_hours)
|
||||||
|
|
||||||
pdf.savefig() # Save the current figure state to the PDF
|
pdf.savefig() # Save the current figure state to the PDF
|
||||||
@ -116,33 +187,45 @@ def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, di
|
|||||||
|
|
||||||
# Financial overview
|
# Financial overview
|
||||||
fig, axs = plt.subplots(1, 2, figsize=(14, 10)) # Create a 1x2 grid of subplots
|
fig, axs = plt.subplots(1, 2, figsize=(14, 10)) # Create a 1x2 grid of subplots
|
||||||
total_costs = ergebnisse['Gesamtkosten_Euro']
|
total_costs = ergebnisse["Gesamtkosten_Euro"]
|
||||||
total_revenue = ergebnisse['Gesamteinnahmen_Euro']
|
total_revenue = ergebnisse["Gesamteinnahmen_Euro"]
|
||||||
total_balance = ergebnisse['Gesamtbilanz_Euro']
|
total_balance = ergebnisse["Gesamtbilanz_Euro"]
|
||||||
losses = ergebnisse['Gesamt_Verluste']
|
losses = ergebnisse["Gesamt_Verluste"]
|
||||||
|
|
||||||
# Costs and revenues per hour on the first axis (axs[0])
|
# Costs and revenues per hour on the first axis (axs[0])
|
||||||
axs[0].plot(hours, ergebnisse['Kosten_Euro_pro_Stunde'], label='Costs (Euro)', marker='o', color='red')
|
axs[0].plot(
|
||||||
axs[0].plot(hours, ergebnisse['Einnahmen_Euro_pro_Stunde'], label='Revenue (Euro)', marker='x', color='green')
|
hours,
|
||||||
axs[0].set_title('Financial Balance per Hour')
|
ergebnisse["Kosten_Euro_pro_Stunde"],
|
||||||
axs[0].set_xlabel('Hour')
|
label="Costs (Euro)",
|
||||||
axs[0].set_ylabel('Euro')
|
marker="o",
|
||||||
|
color="red",
|
||||||
|
)
|
||||||
|
axs[0].plot(
|
||||||
|
hours,
|
||||||
|
ergebnisse["Einnahmen_Euro_pro_Stunde"],
|
||||||
|
label="Revenue (Euro)",
|
||||||
|
marker="x",
|
||||||
|
color="green",
|
||||||
|
)
|
||||||
|
axs[0].set_title("Financial Balance per Hour")
|
||||||
|
axs[0].set_xlabel("Hour")
|
||||||
|
axs[0].set_ylabel("Euro")
|
||||||
axs[0].legend()
|
axs[0].legend()
|
||||||
axs[0].grid(True)
|
axs[0].grid(True)
|
||||||
|
|
||||||
# Summary of finances on the second axis (axs[1])
|
# Summary of finances on the second axis (axs[1])
|
||||||
labels = ['Total Costs [€]', 'Total Revenue [€]', 'Total Balance [€]']
|
labels = ["Total Costs [€]", "Total Revenue [€]", "Total Balance [€]"]
|
||||||
values = [total_costs, total_revenue, total_balance]
|
values = [total_costs, total_revenue, total_balance]
|
||||||
colors = ['red' if value > 0 else 'green' for value in values]
|
colors = ["red" if value > 0 else "green" for value in values]
|
||||||
axs[1].bar(labels, values, color=colors)
|
axs[1].bar(labels, values, color=colors)
|
||||||
axs[1].set_title('Financial Overview')
|
axs[1].set_title("Financial Overview")
|
||||||
axs[1].set_ylabel('Euro')
|
axs[1].set_ylabel("Euro")
|
||||||
|
|
||||||
# Second axis (ax2) for losses, shared with axs[1]
|
# Second axis (ax2) for losses, shared with axs[1]
|
||||||
ax2 = axs[1].twinx()
|
ax2 = axs[1].twinx()
|
||||||
ax2.bar('Total Losses', losses, color='blue')
|
ax2.bar("Total Losses", losses, color="blue")
|
||||||
ax2.set_ylabel('Losses [Wh]', color='blue')
|
ax2.set_ylabel("Losses [Wh]", color="blue")
|
||||||
ax2.tick_params(axis='y', labelcolor='blue')
|
ax2.tick_params(axis="y", labelcolor="blue")
|
||||||
|
|
||||||
pdf.savefig() # Save the complete figure to the PDF
|
pdf.savefig() # Save the complete figure to the PDF
|
||||||
plt.close() # Close the figure
|
plt.close() # Close the figure
|
||||||
@ -154,17 +237,31 @@ def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, di
|
|||||||
f1 = np.array(extra_data["verluste"])
|
f1 = np.array(extra_data["verluste"])
|
||||||
f2 = np.array(extra_data["bilanz"])
|
f2 = np.array(extra_data["bilanz"])
|
||||||
n1 = np.array(extra_data["nebenbedingung"])
|
n1 = np.array(extra_data["nebenbedingung"])
|
||||||
scatter = plt.scatter(f1, f2, c=n1, cmap='viridis')
|
scatter = plt.scatter(f1, f2, c=n1, cmap="viridis")
|
||||||
|
|
||||||
# Add color legend
|
# Add color legend
|
||||||
plt.colorbar(scatter, label='Constraint')
|
plt.colorbar(scatter, label="Constraint")
|
||||||
|
|
||||||
pdf.savefig() # Save the complete figure to the PDF
|
pdf.savefig() # Save the complete figure to the PDF
|
||||||
plt.close() # Close the figure
|
plt.close() # Close the figure
|
||||||
|
|
||||||
plt.figure(figsize=(14, 10))
|
plt.figure(figsize=(14, 10))
|
||||||
filtered_losses = np.array([v for v, n in zip(extra_data["verluste"], extra_data["nebenbedingung"]) if n < 0.01])
|
filtered_losses = np.array(
|
||||||
filtered_balance = np.array([b for b, n in zip(extra_data["bilanz"], extra_data["nebenbedingung"]) if n < 0.01])
|
[
|
||||||
|
v
|
||||||
|
for v, n in zip(
|
||||||
|
extra_data["verluste"], extra_data["nebenbedingung"]
|
||||||
|
)
|
||||||
|
if n < 0.01
|
||||||
|
]
|
||||||
|
)
|
||||||
|
filtered_balance = np.array(
|
||||||
|
[
|
||||||
|
b
|
||||||
|
for b, n in zip(extra_data["bilanz"], extra_data["nebenbedingung"])
|
||||||
|
if n < 0.01
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
best_loss = min(filtered_losses)
|
best_loss = min(filtered_losses)
|
||||||
worst_loss = max(filtered_losses)
|
worst_loss = max(filtered_losses)
|
||||||
@ -172,19 +269,21 @@ def visualisiere_ergebnisse(gesamtlast, pv_forecast, strompreise, ergebnisse, di
|
|||||||
worst_balance = max(filtered_balance)
|
worst_balance = max(filtered_balance)
|
||||||
|
|
||||||
data = [filtered_losses, filtered_balance]
|
data = [filtered_losses, filtered_balance]
|
||||||
labels = ['Losses', 'Balance']
|
labels = ["Losses", "Balance"]
|
||||||
# Create plots
|
# Create plots
|
||||||
fig, axs = plt.subplots(1, 2, figsize=(10, 6), sharey=False) # Two subplots, separate y-axes
|
fig, axs = plt.subplots(
|
||||||
|
1, 2, figsize=(10, 6), sharey=False
|
||||||
|
) # Two subplots, separate y-axes
|
||||||
|
|
||||||
# First violin plot for losses
|
# First violin plot for losses
|
||||||
axs[0].violinplot(data[0], showmeans=True, showmedians=True)
|
axs[0].violinplot(data[0], showmeans=True, showmedians=True)
|
||||||
axs[0].set_title('Losses')
|
axs[0].set_title("Losses")
|
||||||
axs[0].set_xticklabels(['Losses'])
|
axs[0].set_xticklabels(["Losses"])
|
||||||
|
|
||||||
# Second violin plot for balance
|
# Second violin plot for balance
|
||||||
axs[1].violinplot(data[1], showmeans=True, showmedians=True)
|
axs[1].violinplot(data[1], showmeans=True, showmedians=True)
|
||||||
axs[1].set_title('Balance')
|
axs[1].set_title("Balance")
|
||||||
axs[1].set_xticklabels(['Balance'])
|
axs[1].set_xticklabels(["Balance"])
|
||||||
|
|
||||||
# Fine-tuning
|
# Fine-tuning
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
|
@ -1,35 +1,43 @@
|
|||||||
from flask import Flask, jsonify, request
|
|
||||||
import numpy as np
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import modules.class_load as cl
|
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
|
||||||
|
from flask import Flask, jsonify, request
|
||||||
|
|
||||||
|
import modules.class_load as cl
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
DATE_FORMAT = '%Y-%m-%d'
|
DATE_FORMAT = "%Y-%m-%d"
|
||||||
EXPECTED_ARRAY_SHAPE = (2, 24)
|
EXPECTED_ARRAY_SHAPE = (2, 24)
|
||||||
FILEPATH = r'.\load_profiles.npz'
|
FILEPATH = r".\load_profiles.npz"
|
||||||
|
|
||||||
|
|
||||||
def get_load_forecast(year_energy):
|
def get_load_forecast(year_energy):
|
||||||
"""Initialize LoadForecast with the given year_energy."""
|
"""Initialize LoadForecast with the given year_energy."""
|
||||||
return cl.LoadForecast(filepath=FILEPATH, year_energy=float(year_energy))
|
return cl.LoadForecast(filepath=FILEPATH, year_energy=float(year_energy))
|
||||||
|
|
||||||
|
|
||||||
def validate_date(date_str):
|
def validate_date(date_str):
|
||||||
"""Validate the date string and return a datetime object."""
|
"""Validate the date string and return a datetime object."""
|
||||||
try:
|
try:
|
||||||
return datetime.strptime(date_str, DATE_FORMAT)
|
return datetime.strptime(date_str, DATE_FORMAT)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError("Date is not in the correct format. Expected format: YYYY-MM-DD.")
|
raise ValueError(
|
||||||
|
"Date is not in the correct format. Expected format: YYYY-MM-DD."
|
||||||
|
)
|
||||||
|
|
||||||
@app.route('/getdata', methods=['GET'])
|
|
||||||
|
@app.route("/getdata", methods=["GET"])
|
||||||
def get_data():
|
def get_data():
|
||||||
# Retrieve the date and year_energy from query parameters
|
# Retrieve the date and year_energy from query parameters
|
||||||
date_str = request.args.get('date')
|
date_str = request.args.get("date")
|
||||||
year_energy = request.args.get('year_energy')
|
year_energy = request.args.get("year_energy")
|
||||||
|
|
||||||
if not date_str or not year_energy:
|
if not date_str or not year_energy:
|
||||||
return jsonify({"error": "Missing 'date' or 'year_energy' query parameter."}), 400
|
return jsonify(
|
||||||
|
{"error": "Missing 'date' or 'year_energy' query parameter."}
|
||||||
|
), 400
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Validate and convert the date
|
# Validate and convert the date
|
||||||
@ -54,5 +62,6 @@ def get_data():
|
|||||||
# Return a generic error message for unexpected errors
|
# Return a generic error message for unexpected errors
|
||||||
return jsonify({"error": "An unexpected error occurred."}), 500
|
return jsonify({"error": "An unexpected error occurred."}), 500
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
app.run(debug=True)
|
app.run(debug=True)
|
||||||
|
382
test.py
382
test.py
@ -1,10 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
from datetime import datetime
|
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from deap import base, creator, tools, algorithms
|
|
||||||
|
|
||||||
# Import necessary modules from the project
|
# Import necessary modules from the project
|
||||||
from modules.class_optimize import optimization_problem
|
from modules.class_optimize import optimization_problem
|
||||||
@ -14,75 +10,355 @@ start_hour = 10
|
|||||||
|
|
||||||
# PV Forecast (in W)
|
# PV Forecast (in W)
|
||||||
pv_forecast = [
|
pv_forecast = [
|
||||||
0, 0, 0, 0, 0, 0, 0, 8.05, 352.91, 728.51, 930.28, 1043.25,
|
0,
|
||||||
1106.74, 1161.69, 6018.82, 5519.07, 3969.88, 3017.96, 1943.07,
|
0,
|
||||||
1007.17, 319.67, 7.88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5.04, 335.59,
|
0,
|
||||||
705.32, 1121.12, 1604.79, 2157.38, 1433.25, 5718.49, 4553.96,
|
0,
|
||||||
3027.55, 2574.46, 1720.4, 963.4, 383.3, 0, 0, 0
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
8.05,
|
||||||
|
352.91,
|
||||||
|
728.51,
|
||||||
|
930.28,
|
||||||
|
1043.25,
|
||||||
|
1106.74,
|
||||||
|
1161.69,
|
||||||
|
6018.82,
|
||||||
|
5519.07,
|
||||||
|
3969.88,
|
||||||
|
3017.96,
|
||||||
|
1943.07,
|
||||||
|
1007.17,
|
||||||
|
319.67,
|
||||||
|
7.88,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
5.04,
|
||||||
|
335.59,
|
||||||
|
705.32,
|
||||||
|
1121.12,
|
||||||
|
1604.79,
|
||||||
|
2157.38,
|
||||||
|
1433.25,
|
||||||
|
5718.49,
|
||||||
|
4553.96,
|
||||||
|
3027.55,
|
||||||
|
2574.46,
|
||||||
|
1720.4,
|
||||||
|
963.4,
|
||||||
|
383.3,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Temperature Forecast (in degree C)
|
# Temperature Forecast (in degree C)
|
||||||
temperature_forecast = [
|
temperature_forecast = [
|
||||||
18.3, 17.8, 16.9, 16.2, 15.6, 15.1, 14.6, 14.2, 14.3, 14.8, 15.7,
|
18.3,
|
||||||
16.7, 17.4, 18.0, 18.6, 19.2, 19.1, 18.7, 18.5, 17.7, 16.2, 14.6,
|
17.8,
|
||||||
13.6, 13.0, 12.6, 12.2, 11.7, 11.6, 11.3, 11.0, 10.7, 10.2, 11.4,
|
16.9,
|
||||||
14.4, 16.4, 18.3, 19.5, 20.7, 21.9, 22.7, 23.1, 23.1, 22.8, 21.8,
|
16.2,
|
||||||
20.2, 19.1, 18.0, 17.4
|
15.6,
|
||||||
|
15.1,
|
||||||
|
14.6,
|
||||||
|
14.2,
|
||||||
|
14.3,
|
||||||
|
14.8,
|
||||||
|
15.7,
|
||||||
|
16.7,
|
||||||
|
17.4,
|
||||||
|
18.0,
|
||||||
|
18.6,
|
||||||
|
19.2,
|
||||||
|
19.1,
|
||||||
|
18.7,
|
||||||
|
18.5,
|
||||||
|
17.7,
|
||||||
|
16.2,
|
||||||
|
14.6,
|
||||||
|
13.6,
|
||||||
|
13.0,
|
||||||
|
12.6,
|
||||||
|
12.2,
|
||||||
|
11.7,
|
||||||
|
11.6,
|
||||||
|
11.3,
|
||||||
|
11.0,
|
||||||
|
10.7,
|
||||||
|
10.2,
|
||||||
|
11.4,
|
||||||
|
14.4,
|
||||||
|
16.4,
|
||||||
|
18.3,
|
||||||
|
19.5,
|
||||||
|
20.7,
|
||||||
|
21.9,
|
||||||
|
22.7,
|
||||||
|
23.1,
|
||||||
|
23.1,
|
||||||
|
22.8,
|
||||||
|
21.8,
|
||||||
|
20.2,
|
||||||
|
19.1,
|
||||||
|
18.0,
|
||||||
|
17.4,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Electricity Price (in Euro per Wh)
|
# Electricity Price (in Euro per Wh)
|
||||||
strompreis_euro_pro_wh = [
|
strompreis_euro_pro_wh = [
|
||||||
0.0003384, 0.0003318, 0.0003284, 0.0003283, 0.0003289, 0.0003334,
|
0.0003384,
|
||||||
0.0003290, 0.0003302, 0.0003042, 0.0002430, 0.0002280, 0.0002212,
|
0.0003318,
|
||||||
0.0002093, 0.0001879, 0.0001838, 0.0002004, 0.0002198, 0.0002270,
|
0.0003284,
|
||||||
0.0002997, 0.0003195, 0.0003081, 0.0002969, 0.0002921, 0.0002780,
|
0.0003283,
|
||||||
0.0003384, 0.0003318, 0.0003284, 0.0003283, 0.0003289, 0.0003334,
|
0.0003289,
|
||||||
0.0003290, 0.0003302, 0.0003042, 0.0002430, 0.0002280, 0.0002212,
|
0.0003334,
|
||||||
0.0002093, 0.0001879, 0.0001838, 0.0002004, 0.0002198, 0.0002270,
|
0.0003290,
|
||||||
0.0002997, 0.0003195, 0.0003081, 0.0002969, 0.0002921, 0.0002780
|
0.0003302,
|
||||||
|
0.0003042,
|
||||||
|
0.0002430,
|
||||||
|
0.0002280,
|
||||||
|
0.0002212,
|
||||||
|
0.0002093,
|
||||||
|
0.0001879,
|
||||||
|
0.0001838,
|
||||||
|
0.0002004,
|
||||||
|
0.0002198,
|
||||||
|
0.0002270,
|
||||||
|
0.0002997,
|
||||||
|
0.0003195,
|
||||||
|
0.0003081,
|
||||||
|
0.0002969,
|
||||||
|
0.0002921,
|
||||||
|
0.0002780,
|
||||||
|
0.0003384,
|
||||||
|
0.0003318,
|
||||||
|
0.0003284,
|
||||||
|
0.0003283,
|
||||||
|
0.0003289,
|
||||||
|
0.0003334,
|
||||||
|
0.0003290,
|
||||||
|
0.0003302,
|
||||||
|
0.0003042,
|
||||||
|
0.0002430,
|
||||||
|
0.0002280,
|
||||||
|
0.0002212,
|
||||||
|
0.0002093,
|
||||||
|
0.0001879,
|
||||||
|
0.0001838,
|
||||||
|
0.0002004,
|
||||||
|
0.0002198,
|
||||||
|
0.0002270,
|
||||||
|
0.0002997,
|
||||||
|
0.0003195,
|
||||||
|
0.0003081,
|
||||||
|
0.0002969,
|
||||||
|
0.0002921,
|
||||||
|
0.0002780,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Overall System Load (in W)
|
# Overall System Load (in W)
|
||||||
gesamtlast = [
|
gesamtlast = [
|
||||||
676.71, 876.19, 527.13, 468.88, 531.38, 517.95, 483.15, 472.28,
|
676.71,
|
||||||
1011.68, 995.00, 1053.07, 1063.91, 1320.56, 1132.03, 1163.67,
|
876.19,
|
||||||
1176.82, 1216.22, 1103.78, 1129.12, 1178.71, 1050.98, 988.56,
|
527.13,
|
||||||
912.38, 704.61, 516.37, 868.05, 694.34, 608.79, 556.31, 488.89,
|
468.88,
|
||||||
506.91, 804.89, 1141.98, 1056.97, 992.46, 1155.99, 827.01,
|
531.38,
|
||||||
1257.98, 1232.67, 871.26, 860.88, 1158.03, 1222.72, 1221.04,
|
517.95,
|
||||||
949.99, 987.01, 733.99, 592.97
|
483.15,
|
||||||
|
472.28,
|
||||||
|
1011.68,
|
||||||
|
995.00,
|
||||||
|
1053.07,
|
||||||
|
1063.91,
|
||||||
|
1320.56,
|
||||||
|
1132.03,
|
||||||
|
1163.67,
|
||||||
|
1176.82,
|
||||||
|
1216.22,
|
||||||
|
1103.78,
|
||||||
|
1129.12,
|
||||||
|
1178.71,
|
||||||
|
1050.98,
|
||||||
|
988.56,
|
||||||
|
912.38,
|
||||||
|
704.61,
|
||||||
|
516.37,
|
||||||
|
868.05,
|
||||||
|
694.34,
|
||||||
|
608.79,
|
||||||
|
556.31,
|
||||||
|
488.89,
|
||||||
|
506.91,
|
||||||
|
804.89,
|
||||||
|
1141.98,
|
||||||
|
1056.97,
|
||||||
|
992.46,
|
||||||
|
1155.99,
|
||||||
|
827.01,
|
||||||
|
1257.98,
|
||||||
|
1232.67,
|
||||||
|
871.26,
|
||||||
|
860.88,
|
||||||
|
1158.03,
|
||||||
|
1222.72,
|
||||||
|
1221.04,
|
||||||
|
949.99,
|
||||||
|
987.01,
|
||||||
|
733.99,
|
||||||
|
592.97,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Start Solution (binary)
|
# Start Solution (binary)
|
||||||
start_solution = [
|
start_solution = [
|
||||||
1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
1,
|
||||||
1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0,
|
1,
|
||||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
1,
|
||||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
1,
|
||||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Define parameters for the optimization problem
|
# Define parameters for the optimization problem
|
||||||
parameter = {
|
parameter = {
|
||||||
"preis_euro_pro_wh_akku": 10e-05, # Cost of storing energy in battery (per Wh)
|
# Cost of storing energy in battery (per Wh)
|
||||||
'pv_soc': 80, # Initial state of charge (SOC) of PV battery (%)
|
"preis_euro_pro_wh_akku": 10e-05,
|
||||||
'pv_akku_cap': 26400, # Battery capacity (in Wh)
|
# Initial state of charge (SOC) of PV battery (%)
|
||||||
'year_energy': 4100000, # Yearly energy consumption (in Wh)
|
"pv_soc": 80,
|
||||||
'einspeiseverguetung_euro_pro_wh': 7e-05, # Feed-in tariff for exporting electricity (per Wh)
|
# Battery capacity (in Wh)
|
||||||
'max_heizleistung': 1000, # Maximum heating power (in W)
|
"pv_akku_cap": 26400,
|
||||||
"gesamtlast": gesamtlast, # Overall load on the system
|
# Yearly energy consumption (in Wh)
|
||||||
'pv_forecast': pv_forecast, # PV generation forecast (48 hours)
|
"year_energy": 4100000,
|
||||||
"temperature_forecast": temperature_forecast, # Temperature forecast (48 hours)
|
# Feed-in tariff for exporting electricity (per Wh)
|
||||||
"strompreis_euro_pro_wh": strompreis_euro_pro_wh, # Electricity price forecast (48 hours)
|
"einspeiseverguetung_euro_pro_wh": 7e-05,
|
||||||
'eauto_min_soc': 0, # Minimum SOC for electric car
|
# Maximum heating power (in W)
|
||||||
'eauto_cap': 60000, # Electric car battery capacity (Wh)
|
"max_heizleistung": 1000,
|
||||||
'eauto_charge_efficiency': 0.95, # Charging efficiency of the electric car
|
# Overall load on the system
|
||||||
'eauto_charge_power': 11040, # Charging power of the electric car (W)
|
"gesamtlast": gesamtlast,
|
||||||
'eauto_soc': 54, # Current SOC of the electric car (%)
|
# PV generation forecast (48 hours)
|
||||||
'pvpowernow': 211.137503624, # Current PV power generation (W)
|
"pv_forecast": pv_forecast,
|
||||||
'start_solution': start_solution, # Initial solution for the optimization
|
# Temperature forecast (48 hours)
|
||||||
'haushaltsgeraet_wh': 937, # Household appliance consumption (Wh)
|
"temperature_forecast": temperature_forecast,
|
||||||
'haushaltsgeraet_dauer': 0 # Duration of appliance usage (hours)
|
# Electricity price forecast (48 hours)
|
||||||
|
"strompreis_euro_pro_wh": strompreis_euro_pro_wh,
|
||||||
|
# Minimum SOC for electric car
|
||||||
|
"eauto_min_soc": 0,
|
||||||
|
# Electric car battery capacity (Wh)
|
||||||
|
"eauto_cap": 60000,
|
||||||
|
# Charging efficiency of the electric car
|
||||||
|
"eauto_charge_efficiency": 0.95,
|
||||||
|
# Charging power of the electric car (W)
|
||||||
|
"eauto_charge_power": 11040,
|
||||||
|
# Current SOC of the electric car (%)
|
||||||
|
"eauto_soc": 54,
|
||||||
|
# Current PV power generation (W)
|
||||||
|
"pvpowernow": 211.137503624,
|
||||||
|
# Initial solution for the optimization
|
||||||
|
"start_solution": start_solution,
|
||||||
|
# Household appliance consumption (Wh)
|
||||||
|
"haushaltsgeraet_wh": 937,
|
||||||
|
# Duration of appliance usage (hours)
|
||||||
|
"haushaltsgeraet_dauer": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize the optimization problem
|
# Initialize the optimization problem
|
||||||
|
@ -1 +0,0 @@
|
|||||||
from test_heatpump import heatpump
|
|
@ -1,9 +1,9 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import numpy as np
|
|
||||||
from modules.class_akku import PVAkku
|
from modules.class_akku import PVAkku
|
||||||
|
|
||||||
class TestPVAkku(unittest.TestCase):
|
|
||||||
|
|
||||||
|
class TestPVAkku(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Initializing common parameters for tests
|
# Initializing common parameters for tests
|
||||||
self.kapazitaet_wh = 10000 # 10,000 Wh capacity
|
self.kapazitaet_wh = 10000 # 10,000 Wh capacity
|
||||||
@ -13,54 +13,129 @@ class TestPVAkku(unittest.TestCase):
|
|||||||
self.max_soc_prozent = 80 # Maximum SoC is 80%
|
self.max_soc_prozent = 80 # Maximum SoC is 80%
|
||||||
|
|
||||||
def test_initial_state_of_charge(self):
|
def test_initial_state_of_charge(self):
|
||||||
akku = PVAkku(self.kapazitaet_wh, hours=1, start_soc_prozent=50, min_soc_prozent=self.min_soc_prozent, max_soc_prozent=self.max_soc_prozent)
|
akku = PVAkku(
|
||||||
self.assertEqual(akku.ladezustand_in_prozent(), 50.0, "Initial SoC should be 50%")
|
self.kapazitaet_wh,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=50,
|
||||||
|
min_soc_prozent=self.min_soc_prozent,
|
||||||
|
max_soc_prozent=self.max_soc_prozent,
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
akku.ladezustand_in_prozent(), 50.0, "Initial SoC should be 50%"
|
||||||
|
)
|
||||||
|
|
||||||
def test_discharge_below_min_soc(self):
|
def test_discharge_below_min_soc(self):
|
||||||
akku = PVAkku(self.kapazitaet_wh, hours=1, start_soc_prozent=50, min_soc_prozent=self.min_soc_prozent, max_soc_prozent=self.max_soc_prozent)
|
akku = PVAkku(
|
||||||
|
self.kapazitaet_wh,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=50,
|
||||||
|
min_soc_prozent=self.min_soc_prozent,
|
||||||
|
max_soc_prozent=self.max_soc_prozent,
|
||||||
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
# Try to discharge more energy than available above min_soc
|
# Try to discharge more energy than available above min_soc
|
||||||
abgegeben_wh, verlust_wh = akku.energie_abgeben(5000, 0) # Try to discharge 5000 Wh
|
abgegeben_wh, verlust_wh = akku.energie_abgeben(
|
||||||
|
5000, 0
|
||||||
|
) # Try to discharge 5000 Wh
|
||||||
expected_soc = self.min_soc_prozent # SoC should not drop below min_soc
|
expected_soc = self.min_soc_prozent # SoC should not drop below min_soc
|
||||||
self.assertEqual(akku.ladezustand_in_prozent(), expected_soc, "SoC should not drop below min_soc after discharge")
|
self.assertEqual(
|
||||||
self.assertEqual(abgegeben_wh, 2640.0, "The energy discharged should be limited by min_soc")
|
akku.ladezustand_in_prozent(),
|
||||||
|
expected_soc,
|
||||||
|
"SoC should not drop below min_soc after discharge",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
abgegeben_wh, 2640.0, "The energy discharged should be limited by min_soc"
|
||||||
|
)
|
||||||
|
|
||||||
def test_charge_above_max_soc(self):
|
def test_charge_above_max_soc(self):
|
||||||
akku = PVAkku(self.kapazitaet_wh, hours=1, start_soc_prozent=50, min_soc_prozent=self.min_soc_prozent, max_soc_prozent=self.max_soc_prozent)
|
akku = PVAkku(
|
||||||
|
self.kapazitaet_wh,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=50,
|
||||||
|
min_soc_prozent=self.min_soc_prozent,
|
||||||
|
max_soc_prozent=self.max_soc_prozent,
|
||||||
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
# Try to charge more energy than available up to max_soc
|
# Try to charge more energy than available up to max_soc
|
||||||
geladen_wh, verlust_wh = akku.energie_laden(5000, 0) # Try to charge 5000 Wh
|
geladen_wh, verlust_wh = akku.energie_laden(5000, 0) # Try to charge 5000 Wh
|
||||||
expected_soc = self.max_soc_prozent # SoC should not exceed max_soc
|
expected_soc = self.max_soc_prozent # SoC should not exceed max_soc
|
||||||
self.assertEqual(akku.ladezustand_in_prozent(), expected_soc, "SoC should not exceed max_soc after charge")
|
self.assertEqual(
|
||||||
self.assertEqual(geladen_wh, 3000.0, "The energy charged should be limited by max_soc")
|
akku.ladezustand_in_prozent(),
|
||||||
|
expected_soc,
|
||||||
|
"SoC should not exceed max_soc after charge",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
geladen_wh, 3000.0, "The energy charged should be limited by max_soc"
|
||||||
|
)
|
||||||
|
|
||||||
def test_charging_at_max_soc(self):
|
def test_charging_at_max_soc(self):
|
||||||
akku = PVAkku(self.kapazitaet_wh, hours=1, start_soc_prozent=80, min_soc_prozent=self.min_soc_prozent, max_soc_prozent=self.max_soc_prozent)
|
akku = PVAkku(
|
||||||
|
self.kapazitaet_wh,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=80,
|
||||||
|
min_soc_prozent=self.min_soc_prozent,
|
||||||
|
max_soc_prozent=self.max_soc_prozent,
|
||||||
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
# Try to charge when SoC is already at max_soc
|
# Try to charge when SoC is already at max_soc
|
||||||
geladen_wh, verlust_wh = akku.energie_laden(5000, 0)
|
geladen_wh, verlust_wh = akku.energie_laden(5000, 0)
|
||||||
self.assertEqual(geladen_wh, 0.0, "No energy should be charged when at max_soc")
|
self.assertEqual(geladen_wh, 0.0, "No energy should be charged when at max_soc")
|
||||||
self.assertEqual(akku.ladezustand_in_prozent(), self.max_soc_prozent, "SoC should remain at max_soc")
|
self.assertEqual(
|
||||||
|
akku.ladezustand_in_prozent(),
|
||||||
|
self.max_soc_prozent,
|
||||||
|
"SoC should remain at max_soc",
|
||||||
|
)
|
||||||
|
|
||||||
def test_discharging_at_min_soc(self):
|
def test_discharging_at_min_soc(self):
|
||||||
akku = PVAkku(self.kapazitaet_wh, hours=1, start_soc_prozent=20, min_soc_prozent=self.min_soc_prozent, max_soc_prozent=self.max_soc_prozent)
|
akku = PVAkku(
|
||||||
|
self.kapazitaet_wh,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=20,
|
||||||
|
min_soc_prozent=self.min_soc_prozent,
|
||||||
|
max_soc_prozent=self.max_soc_prozent,
|
||||||
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
# Try to discharge when SoC is already at min_soc
|
# Try to discharge when SoC is already at min_soc
|
||||||
abgegeben_wh, verlust_wh = akku.energie_abgeben(5000, 0)
|
abgegeben_wh, verlust_wh = akku.energie_abgeben(5000, 0)
|
||||||
self.assertEqual(abgegeben_wh, 0.0, "No energy should be discharged when at min_soc")
|
self.assertEqual(
|
||||||
self.assertEqual(akku.ladezustand_in_prozent(), self.min_soc_prozent, "SoC should remain at min_soc")
|
abgegeben_wh, 0.0, "No energy should be discharged when at min_soc"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
akku.ladezustand_in_prozent(),
|
||||||
|
self.min_soc_prozent,
|
||||||
|
"SoC should remain at min_soc",
|
||||||
|
)
|
||||||
|
|
||||||
def test_soc_limits(self):
|
def test_soc_limits(self):
|
||||||
# Test to ensure that SoC never exceeds max_soc or drops below min_soc
|
# Test to ensure that SoC never exceeds max_soc or drops below min_soc
|
||||||
akku = PVAkku(self.kapazitaet_wh, hours=1, start_soc_prozent=50, min_soc_prozent=self.min_soc_prozent, max_soc_prozent=self.max_soc_prozent)
|
akku = PVAkku(
|
||||||
|
self.kapazitaet_wh,
|
||||||
|
hours=1,
|
||||||
|
start_soc_prozent=50,
|
||||||
|
min_soc_prozent=self.min_soc_prozent,
|
||||||
|
max_soc_prozent=self.max_soc_prozent,
|
||||||
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
akku.soc_wh = (self.max_soc_prozent / 100) * self.kapazitaet_wh + 1000 # Manually set SoC above max limit
|
akku.soc_wh = (
|
||||||
|
self.max_soc_prozent / 100
|
||||||
|
) * self.kapazitaet_wh + 1000 # Manually set SoC above max limit
|
||||||
akku.soc_wh = min(akku.soc_wh, akku.max_soc_wh)
|
akku.soc_wh = min(akku.soc_wh, akku.max_soc_wh)
|
||||||
self.assertLessEqual(akku.ladezustand_in_prozent(), self.max_soc_prozent, "SoC should not exceed max_soc")
|
self.assertLessEqual(
|
||||||
|
akku.ladezustand_in_prozent(),
|
||||||
|
self.max_soc_prozent,
|
||||||
|
"SoC should not exceed max_soc",
|
||||||
|
)
|
||||||
|
|
||||||
akku.soc_wh = (self.min_soc_prozent / 100) * self.kapazitaet_wh - 1000 # Manually set SoC below min limit
|
akku.soc_wh = (
|
||||||
|
self.min_soc_prozent / 100
|
||||||
|
) * self.kapazitaet_wh - 1000 # Manually set SoC below min limit
|
||||||
akku.soc_wh = max(akku.soc_wh, akku.min_soc_wh)
|
akku.soc_wh = max(akku.soc_wh, akku.min_soc_wh)
|
||||||
self.assertGreaterEqual(akku.ladezustand_in_prozent(), self.min_soc_prozent, "SoC should not drop below min_soc")
|
self.assertGreaterEqual(
|
||||||
|
akku.ladezustand_in_prozent(),
|
||||||
|
self.min_soc_prozent,
|
||||||
|
"SoC should not drop below min_soc",
|
||||||
|
)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from modules.class_heatpump import Heatpump
|
from modules.class_heatpump import Heatpump
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
@pytest.fixture(scope="function")
|
||||||
def heatpump() -> Heatpump:
|
def heatpump() -> Heatpump:
|
||||||
""" Heatpump with 5 kw heating power and 24 h prediction
|
"""Heatpump with 5 kw heating power and 24 h prediction"""
|
||||||
"""
|
|
||||||
return Heatpump(5000, 24)
|
return Heatpump(5000, 24)
|
||||||
|
|
||||||
|
|
||||||
class TestHeatpump:
|
class TestHeatpump:
|
||||||
def test_cop(self, heatpump):
|
def test_cop(self, heatpump):
|
||||||
"""Testing calculate COP for variouse outside temperatures"""
|
"""Testing calculate COP for variouse outside temperatures"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user