PreCommit Fixed

This commit is contained in:
Andreas 2024-10-22 10:29:57 +02:00 committed by Andreas
parent 45a3bcdb09
commit c47f071f55
8 changed files with 216 additions and 132 deletions

View File

@ -1,8 +1,10 @@
from datetime import datetime
from typing import Dict, List, Optional, Union
from akkudoktoreos.config import *
import numpy as np
from akkudoktoreos.config import prediction_hours
class EnergieManagementSystem:
def __init__(
@ -52,11 +54,11 @@ class EnergieManagementSystem:
return self.simuliere(start_stunde)
def simuliere(self, start_stunde: int) -> dict:
'''
"""
hour:
akku_soc_pro_stunde begin of the hour, initial hour state!
last_wh_pro_stunde integral of last hour (end state)
'''
"""
lastkurve_wh = self.gesamtlast
assert (
@ -96,7 +98,9 @@ class EnergieManagementSystem:
# E-Auto handling
if self.eauto and self.ev_charge_hours[stunde] > 0:
geladene_menge_eauto, verluste_eauto = self.eauto.energie_laden(None, stunde, relative_power=self.ev_charge_hours[stunde])
geladene_menge_eauto, verluste_eauto = self.eauto.energie_laden(
None, stunde, relative_power=self.ev_charge_hours[stunde]
)
verbrauch += geladene_menge_eauto
verluste_wh_pro_stunde[stunde_since_now] += verluste_eauto
@ -112,7 +116,9 @@ class EnergieManagementSystem:
# AC PV Battery Charge
if self.ac_charge_hours[stunde] > 0.0:
self.akku.set_charge_allowed_for_hour(1, stunde)
geladene_menge, verluste_wh = self.akku.energie_laden(None,stunde,relative_power=self.ac_charge_hours[stunde])
geladene_menge, verluste_wh = self.akku.energie_laden(
None, stunde, relative_power=self.ac_charge_hours[stunde]
)
# print(stunde, " ", geladene_menge, " ",self.ac_charge_hours[stunde]," ",self.akku.ladezustand_in_prozent())
verbrauch += geladene_menge
netzbezug += geladene_menge

View File

@ -1,6 +1,8 @@
import json
import numpy as np
class NumpyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, np.ndarray):
@ -21,4 +23,3 @@ class NumpyEncoder(json.JSONEncoder):
str: A JSON string representation of the object.
"""
return json.dumps(data, cls=NumpyEncoder)

View File

@ -12,7 +12,6 @@ from akkudoktoreos.config import possible_ev_charge_currents
from akkudoktoreos.visualize import visualisiere_ergebnisse
class optimization_problem:
def __init__(
self,
@ -37,8 +36,9 @@ class optimization_problem:
if fixed_seed is not None:
random.seed(fixed_seed)
def decode_charge_discharge(self, discharge_hours_bin: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
def decode_charge_discharge(
self, discharge_hours_bin: np.ndarray
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Decode the input array `discharge_hours_bin` into three separate arrays for AC charging, DC charging, and discharge.
The function maps AC and DC charging values to relative power levels (0 to 1), while the discharge remains binary (0 or 1).
@ -60,7 +60,9 @@ class optimization_problem:
discharge_hours_bin = np.array(discharge_hours_bin)
# Create ac_charge array: Only consider values between 2 and 6 (AC charging power levels), set the rest to 0
ac_charge = np.where((discharge_hours_bin >= 2) & (discharge_hours_bin <= 6), discharge_hours_bin - 1, 0)
ac_charge = np.where(
(discharge_hours_bin >= 2) & (discharge_hours_bin <= 6), discharge_hours_bin - 1, 0
)
ac_charge = ac_charge / 5.0 # Normalize AC charge to range between 0 and 1
# Create dc_charge array: 7 = Not allowed (mapped to 0), 8 = Allowed (mapped to 1)
@ -68,16 +70,15 @@ class optimization_problem:
if self.optimize_dc_charge:
dc_charge = np.where(discharge_hours_bin == 8, 1, 0)
else:
dc_charge = np.ones_like(discharge_hours_bin) # Set DC charge to 0 if optimization is disabled
dc_charge = np.ones_like(
discharge_hours_bin
) # Set DC charge to 0 if optimization is disabled
# Create discharge array: Only consider value 1 (Discharge), set the rest to 0 (binary output)
discharge = np.where(discharge_hours_bin == 1, 1, 0)
return ac_charge, dc_charge, discharge
# Custom mutation function that applies type-specific mutations
def mutate(self, individual):
"""
@ -98,7 +99,7 @@ class optimization_problem:
charge_discharge_part = individual[: self.prediction_hours]
# Apply the mutation to the charge/discharge part
charge_discharge_mutated, = self.toolbox.mutate_charge_discharge(charge_discharge_part)
(charge_discharge_mutated,) = self.toolbox.mutate_charge_discharge(charge_discharge_part)
# Ensure that no invalid states are introduced during mutation (valid values: 0-8)
if self.optimize_dc_charge:
@ -121,10 +122,12 @@ class optimization_problem:
ev_charge_part = individual[self.prediction_hours : self.prediction_hours * 2]
# Apply mutation on the EV charging schedule
ev_charge_part_mutated, = self.toolbox.mutate_ev_charge_index(ev_charge_part)
(ev_charge_part_mutated,) = self.toolbox.mutate_ev_charge_index(ev_charge_part)
# Ensure the EV does not charge during fixed hours (set those hours to 0)
ev_charge_part_mutated[self.prediction_hours - self.fixed_eauto_hours :] = [0] * self.fixed_eauto_hours
ev_charge_part_mutated[self.prediction_hours - self.fixed_eauto_hours :] = [
0
] * self.fixed_eauto_hours
# Reassign the mutated EV charging part back to the individual
individual[self.prediction_hours : self.prediction_hours * 2] = ev_charge_part_mutated
@ -135,22 +138,25 @@ class optimization_problem:
appliance_part = [individual[-1]]
# Apply mutation on the appliance start hour
appliance_part_mutated, = self.toolbox.mutate_hour(appliance_part)
(appliance_part_mutated,) = self.toolbox.mutate_hour(appliance_part)
# Reassign the mutated appliance part back to the individual
individual[-1] = appliance_part_mutated[0]
return (individual,)
# Method to create an individual based on the conditions
def create_individual(self):
# Start with discharge states for the individual
individual_components = [self.toolbox.attr_discharge_state() for _ in range(self.prediction_hours)]
individual_components = [
self.toolbox.attr_discharge_state() for _ in range(self.prediction_hours)
]
# Add EV charge index values if optimize_ev is True
if self.optimize_ev:
individual_components += [self.toolbox.attr_ev_charge_index() for _ in range(self.prediction_hours)]
individual_components += [
self.toolbox.attr_ev_charge_index() for _ in range(self.prediction_hours)
]
# Add the start time of the household appliance if it's being optimized
if self.opti_param["haushaltsgeraete"] > 0:
@ -205,10 +211,11 @@ class optimization_problem:
self.toolbox.register("attr_discharge_state", random.randint, 0, 6)
if self.optimize_ev:
self.toolbox.register("attr_ev_charge_index", random.randint, 0, len(possible_ev_charge_currents) - 1)
self.toolbox.register(
"attr_ev_charge_index", random.randint, 0, len(possible_ev_charge_currents) - 1
)
self.toolbox.register("attr_int", random.randint, start_hour, 23)
# Register individual creation function
self.toolbox.register("individual", self.create_individual)
@ -219,11 +226,21 @@ class optimization_problem:
# Register separate mutation functions for each type of value:
# - Discharge state mutation (-5, 0, 1)
if self.optimize_dc_charge:
self.toolbox.register("mutate_charge_discharge", tools.mutUniformInt, low=0, up=8, indpb=0.2)
self.toolbox.register(
"mutate_charge_discharge", tools.mutUniformInt, low=0, up=8, indpb=0.2
)
else:
self.toolbox.register("mutate_charge_discharge", tools.mutUniformInt, low=0, up=6, indpb=0.2)
self.toolbox.register(
"mutate_charge_discharge", tools.mutUniformInt, low=0, up=6, indpb=0.2
)
# - Float mutation for EV charging values
self.toolbox.register("mutate_ev_charge_index", tools.mutUniformInt, low=0, up=len(possible_ev_charge_currents) - 1, indpb=0.2)
self.toolbox.register(
"mutate_ev_charge_index",
tools.mutUniformInt,
low=0,
up=len(possible_ev_charge_currents) - 1,
indpb=0.2,
)
# - Start hour mutation for household devices
self.toolbox.register("mutate_hour", tools.mutUniformInt, low=start_hour, up=23, indpb=0.2)
@ -248,7 +265,6 @@ class optimization_problem:
ac, dc, discharge = self.decode_charge_discharge(discharge_hours_bin)
ems.set_akku_discharge_hours(discharge)
# Set DC charge hours only if DC optimization is enabled
if self.optimize_dc_charge:
@ -313,7 +329,6 @@ class optimization_problem:
(parameter["eauto_min_soc"] - ems.eauto.ladezustand_in_prozent()) * self.strafe,
)
return (gesamtbilanz,)
def optimize(
@ -434,7 +449,9 @@ class optimization_problem:
start_solution
)
if self.optimize_ev:
eautocharge_hours_float = [possible_ev_charge_currents[i] for i in eautocharge_hours_float]
eautocharge_hours_float = [
possible_ev_charge_currents[i] for i in eautocharge_hours_float
]
ac_charge, dc_charge, discharge = self.decode_charge_discharge(discharge_hours_bin)
# Visualize the results
@ -493,4 +510,3 @@ class optimization_problem:
"spuelstart": spuelstart_int,
"simulation_data": o,
}

View File

@ -5,6 +5,7 @@ import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
class BatteryDataProcessor:
def __init__(
self,
@ -115,7 +116,10 @@ class BatteryDataProcessor:
else:
end_point = self.data.iloc[-1] # Verwenden des letzten Datensatzes als Endpunkt
if not last_points_100_df.empty and start_point["timestamp"] in last_points_100_df["timestamp"].values:
if (
not last_points_100_df.empty
and start_point["timestamp"] in last_points_100_df["timestamp"].values
):
initial_soc = 100
elif start_point["timestamp"] in last_points_0_df["timestamp"].values:
initial_soc = 0
@ -235,7 +239,13 @@ class BatteryDataProcessor:
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.ylabel("Voltage (V)")
plt.legend()
@ -256,7 +266,13 @@ class BatteryDataProcessor:
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.ylabel("Current (A)")
plt.legend()
@ -284,10 +300,10 @@ if __name__ == "__main__":
# MariaDB Verbindungsdetails
config = {
'user': 'soc',
'password': 'Rayoflight123!',
'host': '192.168.1.135',
'database': 'sensor'
"user": "soc",
"password": "Rayoflight123!",
"host": "192.168.1.135",
"database": "sensor",
}
# Parameter festlegen

View File

@ -22,7 +22,9 @@ def repeat_to_shape(array, target_shape):
class HourlyElectricityPriceForecast:
def __init__(self, source, cache_dir="cache", charges=0.000228, prediction_hours=24, cache=True): # 228
def __init__(
self, source, cache_dir="cache", charges=0.000228, prediction_hours=24, cache=True
): # 228
self.cache_dir = cache_dir
self.cache = cache
os.makedirs(self.cache_dir, exist_ok=True)
@ -31,11 +33,10 @@ class HourlyElectricityPriceForecast:
self.charges = charges
self.prediction_hours = prediction_hours
def load_data(self, source):
cache_filename = self.get_cache_filename(source)
if source.startswith("http"):
if os.path.exists(cache_filename) and not self.is_cache_expired() and self.cache==True:
if os.path.exists(cache_filename) and not self.is_cache_expired() and self.cache:
print("Loading data from cache...")
with open(cache_filename, "r") as file:
json_data = json.load(file)

View File

@ -59,8 +59,6 @@ def visualisiere_ergebnisse(
plt.grid(True)
plt.legend()
# PV forecast
plt.subplot(3, 2, 3)
plt.plot(hours, pv_forecast, label="PV Generation (Wh)", marker="x")
@ -111,19 +109,44 @@ def visualisiere_ergebnisse(
plt.subplot(3, 2, 1)
# Plot with transparency (alpha) and different linestyles
plt.plot(
hours, ergebnisse["Last_Wh_pro_Stunde"], label="Load (Wh)", marker="o", linestyle="-", alpha=0.8
hours,
ergebnisse["Last_Wh_pro_Stunde"],
label="Load (Wh)",
marker="o",
linestyle="-",
alpha=0.8,
)
plt.plot(
hours, ergebnisse["Haushaltsgeraet_wh_pro_stunde"], label="Household Device (Wh)", marker="o", linestyle="--", alpha=0.8
hours,
ergebnisse["Haushaltsgeraet_wh_pro_stunde"],
label="Household Device (Wh)",
marker="o",
linestyle="--",
alpha=0.8,
)
plt.plot(
hours, ergebnisse["Netzeinspeisung_Wh_pro_Stunde"], label="Grid Feed-in (Wh)", marker="x", linestyle=":", alpha=0.8
hours,
ergebnisse["Netzeinspeisung_Wh_pro_Stunde"],
label="Grid Feed-in (Wh)",
marker="x",
linestyle=":",
alpha=0.8,
)
plt.plot(
hours, ergebnisse["Netzbezug_Wh_pro_Stunde"], label="Grid Consumption (Wh)", marker="^", linestyle="-.", alpha=0.8
hours,
ergebnisse["Netzbezug_Wh_pro_Stunde"],
label="Grid Consumption (Wh)",
marker="^",
linestyle="-.",
alpha=0.8,
)
plt.plot(
hours, ergebnisse["Verluste_Pro_Stunde"], label="Losses (Wh)", marker="^", linestyle="-", alpha=0.8
hours,
ergebnisse["Verluste_Pro_Stunde"],
label="Losses (Wh)",
marker="^",
linestyle="-",
alpha=0.8,
)
# Title and labels
@ -134,8 +157,6 @@ def visualisiere_ergebnisse(
# Show legend with a higher number of columns to avoid overlap
plt.legend(ncol=2)
# Electricity prices
hours_p = np.arange(0, len(strompreise))
plt.subplot(3, 2, 3)
@ -164,8 +185,6 @@ def visualisiere_ergebnisse(
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
# Plot for AC, DC charging, and Discharge status using bar charts
ax1 = plt.subplot(3, 2, 5)
hours = np.arange(0, prediction_hours)
@ -173,10 +192,20 @@ def visualisiere_ergebnisse(
plt.bar(hours, ac, width=0.4, label="AC Charging (relative)", color="blue", alpha=0.6)
# Plot DC charging as bars (relative values between 0 and 1)
plt.bar(hours + 0.4, dc, width=0.4, label="DC Charging (relative)", color="green", alpha=0.6)
plt.bar(
hours + 0.4, dc, width=0.4, label="DC Charging (relative)", color="green", alpha=0.6
)
# Plot Discharge as bars (0 or 1, binary values)
plt.bar(hours, discharge, width=0.4, label="Discharge Allowed", color="red", alpha=0.6, bottom=np.maximum(ac, dc))
plt.bar(
hours,
discharge,
width=0.4,
label="Discharge Allowed",
color="red",
alpha=0.6,
bottom=np.maximum(ac, dc),
)
# Configure the plot
ax1.legend(loc="upper left")
@ -186,13 +215,11 @@ def visualisiere_ergebnisse(
ax1.set_title("AC/DC Charging and Discharge Overview")
ax1.grid(True)
if ist_dst_wechsel(datetime.datetime.now()):
hours = np.arange(start_hour, prediction_hours - 1)
else:
hours = np.arange(start_hour, prediction_hours)
pdf.savefig() # Save the current figure state to the PDF
plt.close() # Close the current figure to free up memory
@ -217,9 +244,17 @@ def visualisiere_ergebnisse(
)
# Annotate costs
for hour, value in enumerate(costs):
if value == None or np.isnan(value):
if value is None or np.isnan(value):
value = 0
axs[0].annotate(f'{value:.2f}', (hour, value), textcoords="offset points", xytext=(0,5), ha='center', fontsize=8, color='red')
axs[0].annotate(
f"{value:.2f}",
(hour, value),
textcoords="offset points",
xytext=(0, 5),
ha="center",
fontsize=8,
color="red",
)
# Plot revenues
axs[0].plot(
@ -231,9 +266,17 @@ def visualisiere_ergebnisse(
)
# Annotate revenues
for hour, value in enumerate(revenues):
if value == None or np.isnan(value):
if value is None or np.isnan(value):
value = 0
axs[0].annotate(f'{value:.2f}', (hour, value), textcoords="offset points", xytext=(0,5), ha='center', fontsize=8, color='green')
axs[0].annotate(
f"{value:.2f}",
(hour, value),
textcoords="offset points",
xytext=(0, 5),
ha="center",
fontsize=8,
color="green",
)
# Title and labels
axs[0].set_title("Financial Balance per Hour")
@ -242,8 +285,6 @@ def visualisiere_ergebnisse(
axs[0].legend()
axs[0].grid(True)
# Summary of finances on the second axis (axs[1])
labels = ["Total Costs [€]", "Total Revenue [€]", "Total Balance [€]"]
values = [total_costs, total_revenue, total_balance]

View File

@ -15,9 +15,9 @@ from flask import Flask, jsonify, redirect, request, send_from_directory, url_fo
from akkudoktoreos.class_load import LoadForecast
from akkudoktoreos.class_load_container import Gesamtlast
from akkudoktoreos.class_load_corrector import LoadPredictionAdjuster
from akkudoktoreos.class_numpy_encoder import NumpyEncoder
from akkudoktoreos.class_optimize import optimization_problem
from akkudoktoreos.class_pv_forecast import PVForecast
from akkudoktoreos.class_numpy_encoder import *
from akkudoktoreos.class_strompreis import HourlyElectricityPriceForecast
from akkudoktoreos.config import (
get_start_enddate,
@ -29,7 +29,10 @@ from akkudoktoreos.config import (
app = Flask(__name__)
opt_class = optimization_problem(
prediction_hours=prediction_hours, strafe=10, optimization_hours=optimization_hours, verbose=True
prediction_hours=prediction_hours,
strafe=10,
optimization_hours=optimization_hours,
verbose=True,
)
@ -62,7 +65,7 @@ def flask_strompreis():
price_forecast = HourlyElectricityPriceForecast(
source=f"https://api.akkudoktor.net/prices?start={date_now}&end={date}",
prediction_hours=prediction_hours,
cache=False
cache=False,
)
specific_date_prices = price_forecast.get_price_for_daterange(
date_now, date