mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 00:45:22 +00:00
class_ems: AC / DC Charging
class_optimize: Timing Bugs fixed class_numpy_encoder: JSON Encoder with Numpy support visualize: AC / DC / Discharge test_class_ems_2: New Test for AC / DC charging decision
This commit is contained in:
parent
cafed7eaca
commit
87ec02a90e
@ -5,60 +5,63 @@ import time
|
||||
|
||||
# Import necessary modules from the project
|
||||
from akkudoktoreos.class_optimize import optimization_problem
|
||||
|
||||
start_hour = 10
|
||||
from akkudoktoreos.visualize import *
|
||||
from akkudoktoreos.class_numpy_encoder import *
|
||||
start_hour = 0
|
||||
|
||||
# PV Forecast (in W)
|
||||
pv_forecast = [
|
||||
0,
|
||||
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,
|
||||
]
|
||||
pv_forecast = np.zeros(48)
|
||||
pv_forecast[12] = 5000
|
||||
# [
|
||||
# 0,
|
||||
# 0,
|
||||
# 0,
|
||||
# 0,
|
||||
# 0,
|
||||
# 0,
|
||||
# 0,
|
||||
# 8.05,
|
||||
# 352.91,
|
||||
# 728.51,
|
||||
# 930.28,
|
||||
# 1043.25,
|
||||
# 1106.74,
|
||||
# 1161.69,
|
||||
# 1018.82,
|
||||
# 1519.07,
|
||||
# 1969.88,
|
||||
# 1017.96,
|
||||
# 1043.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 = [
|
||||
@ -113,56 +116,60 @@ temperature_forecast = [
|
||||
]
|
||||
|
||||
# Electricity Price (in Euro per Wh)
|
||||
strompreis_euro_pro_wh = [
|
||||
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,
|
||||
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,
|
||||
]
|
||||
strompreis_euro_pro_wh = np.full(48, 0.001)
|
||||
strompreis_euro_pro_wh [0:10] = 0.00001
|
||||
strompreis_euro_pro_wh [11:15] = 0.00005
|
||||
strompreis_euro_pro_wh [20] = 0.00001
|
||||
# [
|
||||
# 0.0000384,
|
||||
# 0.0000318,
|
||||
# 0.0000284,
|
||||
# 0.0008283,
|
||||
# 0.0008289,
|
||||
# 0.0008334,
|
||||
# 0.0008290,
|
||||
# 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)
|
||||
gesamtlast = [
|
||||
@ -221,10 +228,10 @@ start_solution = None
|
||||
|
||||
# Define parameters for the optimization problem
|
||||
parameter = {
|
||||
# Cost of storing energy in battery (per Wh)
|
||||
"preis_euro_pro_wh_akku": 10e-05,
|
||||
# Value of energy in battery (per Wh)
|
||||
"preis_euro_pro_wh_akku": 0e-05,
|
||||
# Initial state of charge (SOC) of PV battery (%)
|
||||
"pv_soc": 80,
|
||||
"pv_soc": 15,
|
||||
# Battery capacity (in Wh)
|
||||
"pv_akku_cap": 26400,
|
||||
# Yearly energy consumption (in Wh)
|
||||
@ -242,7 +249,7 @@ parameter = {
|
||||
# Electricity price forecast (48 hours)
|
||||
"strompreis_euro_pro_wh": strompreis_euro_pro_wh,
|
||||
# Minimum SOC for electric car
|
||||
"eauto_min_soc": 20,
|
||||
"eauto_min_soc": 50,
|
||||
# Electric car battery capacity (Wh)
|
||||
"eauto_cap": 60000,
|
||||
# Charging efficiency of the electric car
|
||||
@ -250,7 +257,7 @@ parameter = {
|
||||
# Charging power of the electric car (W)
|
||||
"eauto_charge_power": 11040,
|
||||
# Current SOC of the electric car (%)
|
||||
"eauto_soc": 5,
|
||||
"eauto_soc": 15,
|
||||
# Current PV power generation (W)
|
||||
"pvpowernow": 211.137503624,
|
||||
# Initial solution for the optimization
|
||||
@ -258,7 +265,7 @@ parameter = {
|
||||
# Household appliance consumption (Wh)
|
||||
"haushaltsgeraet_wh": 5000,
|
||||
# Duration of appliance usage (hours)
|
||||
"haushaltsgeraet_dauer": 2,
|
||||
"haushaltsgeraet_dauer": 0,
|
||||
# Minimum Soc PV Battery
|
||||
"min_soc_prozent": 15,
|
||||
}
|
||||
@ -282,8 +289,25 @@ elapsed_time = end_time - start_time
|
||||
print(f"Elapsed time: {elapsed_time:.4f} seconds")
|
||||
|
||||
|
||||
# Print or visualize the result
|
||||
# pprint(ergebnis)
|
||||
|
||||
#json_data = json.dumps(ergebnis)
|
||||
#print(json_data)
|
||||
ac_charge, dc_charge, discharge = (ergebnis["ac_charge"],ergebnis["dc_charge"],ergebnis["discharge_allowed"])
|
||||
|
||||
visualisiere_ergebnisse(
|
||||
gesamtlast,
|
||||
pv_forecast,
|
||||
strompreis_euro_pro_wh,
|
||||
ergebnis["result"],
|
||||
ac_charge,
|
||||
dc_charge,
|
||||
discharge,
|
||||
temperature_forecast,
|
||||
start_hour,
|
||||
48,
|
||||
np.full(48, parameter["einspeiseverguetung_euro_pro_wh"]),
|
||||
filename="visualization_results.pdf",
|
||||
extra_data=None,
|
||||
)
|
||||
|
||||
|
||||
json_data = NumpyEncoder.dumps(ergebnis)
|
||||
print(json_data)
|
||||
|
@ -20,7 +20,7 @@ class PVAkku:
|
||||
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.discharge_array = np.full(self.hours, 1)
|
||||
self.charge_array = np.full(self.hours, 0)
|
||||
self.charge_array = np.full(self.hours, 1)
|
||||
# Charge and discharge efficiency
|
||||
self.lade_effizienz = lade_effizienz
|
||||
self.entlade_effizienz = entlade_effizienz
|
||||
@ -70,26 +70,19 @@ class PVAkku:
|
||||
self.soc_wh = min(max(self.soc_wh, self.min_soc_wh), self.max_soc_wh)
|
||||
|
||||
self.discharge_array = np.full(self.hours, 1)
|
||||
self.charge_array = np.full(self.hours, 0)
|
||||
self.charge_array = np.full(self.hours, 1)
|
||||
|
||||
def set_discharge_per_hour(self, discharge_array):
|
||||
assert len(discharge_array) == self.hours
|
||||
self.discharge_array = np.array(discharge_array)
|
||||
|
||||
# Ensure no simultaneous charging and discharging in the same hour using NumPy mask
|
||||
conflict_mask = (self.charge_array > 0) & (self.discharge_array > 0)
|
||||
# Prioritize discharge by setting charge to 0 where both are > 0
|
||||
self.charge_array[conflict_mask] = 0
|
||||
|
||||
def set_charge_per_hour(self, charge_array):
|
||||
assert len(charge_array) == self.hours
|
||||
self.charge_array = np.array(charge_array)
|
||||
|
||||
# Ensure no simultaneous charging and discharging in the same hour using NumPy mask
|
||||
conflict_mask = (self.charge_array > 0) & (self.discharge_array > 0)
|
||||
# Prioritize discharge by setting charge to 0 where both are > 0
|
||||
self.discharge_array[conflict_mask] = 0
|
||||
|
||||
def set_charge_allowed_for_hour(self, charge, hour):
|
||||
assert hour < self.hours
|
||||
self.charge_array[hour] = charge
|
||||
|
||||
def ladezustand_in_prozent(self):
|
||||
return (self.soc_wh / self.kapazitaet_wh) * 100
|
||||
@ -125,17 +118,14 @@ class PVAkku:
|
||||
# Return the actually discharged energy and the losses
|
||||
return tatsaechlich_abgegeben_wh, verluste_wh
|
||||
|
||||
def energie_laden(self, wh, hour):
|
||||
def energie_laden(self, wh, hour, relative_power=0.0):
|
||||
if hour is not None and self.charge_array[hour] == 0:
|
||||
return 0, 0 # Charging not allowed in this hour
|
||||
|
||||
if relative_power > 0.0:
|
||||
wh=self.max_ladeleistung_w*relative_power
|
||||
# If no value for wh is given, use the maximum charging power
|
||||
wh = wh if wh is not None else self.max_ladeleistung_w
|
||||
|
||||
# Relative to the maximum charging power (between 0 and 1)
|
||||
relative_ladeleistung = self.charge_array[hour]
|
||||
effektive_ladeleistung = relative_ladeleistung * self.max_ladeleistung_w
|
||||
|
||||
# Calculate the maximum energy that can be charged considering max_soc and efficiency
|
||||
if self.lade_effizienz > 0:
|
||||
max_possible_charge_wh = (self.max_soc_wh - self.soc_wh) / self.lade_effizienz
|
||||
@ -144,8 +134,8 @@ class PVAkku:
|
||||
max_possible_charge_wh = max(max_possible_charge_wh, 0.0) # Ensure non-negative
|
||||
|
||||
# The actually charged energy cannot exceed requested energy, charging power, or maximum possible charge
|
||||
effektive_lademenge = min(wh, effektive_ladeleistung, max_possible_charge_wh)
|
||||
|
||||
effektive_lademenge = min(wh, max_possible_charge_wh)
|
||||
|
||||
# Energy actually stored in the battery
|
||||
geladene_menge = effektive_lademenge * self.lade_effizienz
|
||||
|
||||
@ -153,10 +143,9 @@ class PVAkku:
|
||||
self.soc_wh += geladene_menge
|
||||
# Ensure soc_wh does not exceed max_soc_wh
|
||||
self.soc_wh = min(self.soc_wh, self.max_soc_wh)
|
||||
|
||||
|
||||
# Calculate losses
|
||||
verluste_wh = effektive_lademenge - geladene_menge
|
||||
|
||||
return geladene_menge, verluste_wh
|
||||
|
||||
def aktueller_energieinhalt(self):
|
||||
|
@ -1,6 +1,6 @@
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from akkudoktoreos.config import *
|
||||
import numpy as np
|
||||
|
||||
|
||||
@ -23,12 +23,17 @@ class EnergieManagementSystem:
|
||||
self.eauto = eauto
|
||||
self.haushaltsgeraet = haushaltsgeraet
|
||||
self.wechselrichter = wechselrichter
|
||||
self.ac_charge_hours = np.full(prediction_hours,0)
|
||||
self.dc_charge_hours = np.full(prediction_hours,1)
|
||||
|
||||
def set_akku_discharge_hours(self, ds: List[int]) -> None:
|
||||
self.akku.set_discharge_per_hour(ds)
|
||||
|
||||
def set_akku_charge_hours(self, ds: List[int]) -> None:
|
||||
self.akku.set_charge_per_hour(ds)
|
||||
def set_akku_ac_charge_hours(self, ds: np.ndarray) -> None:
|
||||
self.ac_charge_hours = ds
|
||||
|
||||
def set_akku_dc_charge_hours(self, ds: np.ndarray) -> None:
|
||||
self.dc_charge_hours = ds
|
||||
|
||||
def set_eauto_charge_hours(self, ds: List[int]) -> None:
|
||||
self.eauto.set_charge_per_hour(ds)
|
||||
@ -46,7 +51,12 @@ class EnergieManagementSystem:
|
||||
return self.simuliere(start_stunde)
|
||||
|
||||
def simuliere(self, start_stunde: int) -> dict:
|
||||
# Ensure arrays have the same length
|
||||
'''
|
||||
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 (
|
||||
len(lastkurve_wh) == len(self.pv_prognose_wh) == len(self.strompreis_euro_pro_wh)
|
||||
@ -91,16 +101,21 @@ class EnergieManagementSystem:
|
||||
eauto_soc_pro_stunde[stunde_since_now] = self.eauto.ladezustand_in_prozent()
|
||||
|
||||
# AC PV Battery Charge
|
||||
if self.akku.charge_array[stunde] > 0.0:
|
||||
geladene_menge, verluste_wh = self.akku.energie_laden(None,stunde)
|
||||
if self.ac_charge_hours[stunde] > 0.0:
|
||||
self.akku.set_charge_allowed_for_hour(self.ac_charge_hours[stunde],stunde)
|
||||
geladene_menge, verluste_wh = self.akku.energie_laden(None,stunde,relative_power=self.ac_charge_hours[stunde])
|
||||
verbrauch += geladene_menge
|
||||
verluste_wh_pro_stunde[stunde_since_now] += verluste_wh
|
||||
|
||||
verluste_wh_pro_stunde[stunde_since_now] += verluste_wh
|
||||
|
||||
# Process inverter logic
|
||||
erzeugung = self.pv_prognose_wh[stunde]
|
||||
self.akku.set_charge_allowed_for_hour(self.dc_charge_hours[stunde],stunde)
|
||||
netzeinspeisung, netzbezug, verluste, eigenverbrauch = (
|
||||
self.wechselrichter.energie_verarbeiten(erzeugung, verbrauch, stunde)
|
||||
)
|
||||
|
||||
|
||||
|
||||
netzeinspeisung_wh_pro_stunde[stunde_since_now] = netzeinspeisung
|
||||
netzbezug_wh_pro_stunde[stunde_since_now] = netzbezug
|
||||
verluste_wh_pro_stunde[stunde_since_now] += verluste
|
||||
@ -136,4 +151,5 @@ class EnergieManagementSystem:
|
||||
"Gesamt_Verluste": np.nansum(verluste_wh_pro_stunde),
|
||||
"Haushaltsgeraet_wh_pro_stunde": haushaltsgeraet_wh_pro_stunde,
|
||||
}
|
||||
|
||||
return out
|
||||
|
24
src/akkudoktoreos/class_numpy_encoder.py
Normal file
24
src/akkudoktoreos/class_numpy_encoder.py
Normal file
@ -0,0 +1,24 @@
|
||||
import json
|
||||
import numpy as np
|
||||
|
||||
class NumpyEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, np.ndarray):
|
||||
return obj.tolist() # Convert NumPy arrays to lists
|
||||
if isinstance(obj, np.generic):
|
||||
return obj.item() # Convert NumPy scalars to native Python types
|
||||
return super(NumpyEncoder, self).default(obj)
|
||||
|
||||
@staticmethod
|
||||
def dumps(data):
|
||||
"""
|
||||
Static method to serialize a Python object into a JSON string using NumpyEncoder.
|
||||
|
||||
Args:
|
||||
data: The Python object to serialize.
|
||||
|
||||
Returns:
|
||||
str: A JSON string representation of the object.
|
||||
"""
|
||||
return json.dumps(data, cls=NumpyEncoder)
|
||||
|
@ -12,6 +12,7 @@ from akkudoktoreos.config import possible_ev_charge_currents
|
||||
from akkudoktoreos.visualize import visualisiere_ergebnisse
|
||||
|
||||
|
||||
|
||||
class optimization_problem:
|
||||
def __init__(
|
||||
self,
|
||||
@ -35,53 +36,104 @@ class optimization_problem:
|
||||
if fixed_seed is not None:
|
||||
random.seed(fixed_seed)
|
||||
|
||||
def split_charge_discharge(self, discharge_hours_bin: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
||||
|
||||
def decode_charge_discharge(self, discharge_hours_bin: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
||||
"""
|
||||
Split the input array `discharge_hours_bin` into two separate arrays:
|
||||
- `charge`: Contains only the negative values from `discharge_hours_bin` (charging values).
|
||||
- `discharge`: Contains only the positive values from `discharge_hours_bin` (discharging values).
|
||||
|
||||
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).
|
||||
|
||||
Parameters:
|
||||
- discharge_hours_bin (np.ndarray): Input array with both positive and negative values.
|
||||
- discharge_hours_bin (np.ndarray): Input array with integer values representing the different states.
|
||||
The states are:
|
||||
0: No action ("idle")
|
||||
1: Discharge ("discharge")
|
||||
2-6: AC charging with different power levels ("ac_charge")
|
||||
7-8: DC charging Dissallowed/allowed ("dc_charge")
|
||||
|
||||
Returns:
|
||||
- charge (np.ndarray): Array with negative values from `discharge_hours_bin`, other values set to 0.
|
||||
- discharge (np.ndarray): Array with positive values from `discharge_hours_bin`, other values set to 0.
|
||||
- ac_charge (np.ndarray): Array with AC charging values as relative power (0-1), other values set to 0.
|
||||
- dc_charge (np.ndarray): Array with DC charging values as relative power (0-1), other values set to 0.
|
||||
- discharge (np.ndarray): Array with discharge values (1 for discharge, 0 otherwise).
|
||||
"""
|
||||
# Convert the input list to a NumPy array, if it's not already
|
||||
discharge_hours_bin = np.array(discharge_hours_bin)
|
||||
|
||||
# Create charge array: Keep only negative values, set the rest to 0
|
||||
charge = -np.where(discharge_hours_bin < 0, discharge_hours_bin, 0)
|
||||
charge = charge / np.max(charge)
|
||||
|
||||
# Create discharge array: Keep only positive values, set the rest to 0
|
||||
discharge = np.where(discharge_hours_bin > 0, discharge_hours_bin, 0)
|
||||
# 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 = 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)
|
||||
dc_charge = np.where(discharge_hours_bin == 8, 1, 0)
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
return charge, discharge
|
||||
|
||||
# Custom mutation function that applies type-specific mutations
|
||||
def mutate(self,individual):
|
||||
# Mutate the discharge state genes (-1, 0, 1)
|
||||
individual[:self.prediction_hours], = self.toolbox.mutate_discharge(
|
||||
individual[:self.prediction_hours]
|
||||
)
|
||||
def mutate(self, individual):
|
||||
"""
|
||||
Custom mutation function for the individual. This function mutates different parts of the individual:
|
||||
- Mutates the discharge and charge states (AC, DC, idle) using the split_charge_discharge method.
|
||||
- Mutates the EV charging schedule if EV optimization is enabled.
|
||||
- Mutates appliance start times if household appliances are part of the optimization.
|
||||
|
||||
Parameters:
|
||||
- individual (list): The individual being mutated, which includes different optimization parameters.
|
||||
|
||||
Returns:
|
||||
- (tuple): The mutated individual as a tuple (required by DEAP).
|
||||
"""
|
||||
|
||||
# Step 1: Mutate the charge/discharge states (idle, discharge, AC charge, DC charge)
|
||||
# Extract the relevant part of the individual for prediction hours, which represents the charge/discharge behavior.
|
||||
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)
|
||||
|
||||
# Ensure that no invalid states are introduced during mutation (valid values: 0-8)
|
||||
charge_discharge_mutated = np.clip(charge_discharge_mutated, 0, 8)
|
||||
|
||||
# Use split_charge_discharge to split the mutated array into AC charge, DC charge, and discharge components
|
||||
#ac_charge, dc_charge, discharge = self.split_charge_discharge(charge_discharge_mutated)
|
||||
|
||||
# Optionally: You can process the split arrays further if needed, for example,
|
||||
# applying additional constraints or penalties, or keeping track of charging limits.
|
||||
|
||||
# Reassign the mutated values back to the individual
|
||||
individual[:self.prediction_hours] = charge_discharge_mutated
|
||||
|
||||
# Step 2: Mutate EV charging schedule if enabled
|
||||
if self.optimize_ev:
|
||||
# Mutate the EV charging indices
|
||||
# Extract the relevant part for EV charging schedule
|
||||
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)
|
||||
|
||||
# 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
|
||||
|
||||
# Reassign the mutated EV charging part back to the individual
|
||||
individual[self.prediction_hours : self.prediction_hours * 2] = ev_charge_part_mutated
|
||||
|
||||
# Mutate the appliance start hour if present
|
||||
# Step 3: Mutate appliance start times if household appliances are part of the optimization
|
||||
if self.opti_param["haushaltsgeraete"] > 0:
|
||||
# Extract the appliance part (typically a single value for the start hour)
|
||||
appliance_part = [individual[-1]]
|
||||
|
||||
# Apply mutation on the appliance start hour
|
||||
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
|
||||
@ -106,8 +158,14 @@ class optimization_problem:
|
||||
2. Electric vehicle charge hours (possible_charge_values),
|
||||
3. Dishwasher start time (integer if applicable).
|
||||
"""
|
||||
|
||||
discharge_hours_bin = individual[: self.prediction_hours]
|
||||
eautocharge_hours_float = individual[self.prediction_hours : self.prediction_hours * 2]
|
||||
eautocharge_hours_float = (
|
||||
individual[self.prediction_hours : self.prediction_hours * 2]
|
||||
if self.optimize_ev
|
||||
else None
|
||||
)
|
||||
|
||||
spuelstart_int = (
|
||||
individual[-1]
|
||||
if self.opti_param and self.opti_param.get("haushaltsgeraete", 0) > 0
|
||||
@ -132,13 +190,12 @@ class optimization_problem:
|
||||
|
||||
# Initialize toolbox with attributes and operations
|
||||
self.toolbox = base.Toolbox()
|
||||
self.toolbox.register("attr_discharge_state", random.randint, -5, 1)
|
||||
self.toolbox.register("attr_discharge_state", random.randint, 0,11)
|
||||
if self.optimize_ev:
|
||||
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)
|
||||
|
||||
@ -148,7 +205,7 @@ class optimization_problem:
|
||||
#self.toolbox.register("mutate", tools.mutFlipBit, indpb=0.1)
|
||||
# Register separate mutation functions for each type of value:
|
||||
# - Discharge state mutation (-5, 0, 1)
|
||||
self.toolbox.register("mutate_discharge", tools.mutUniformInt, low=-5, up=1, indpb=0.1)
|
||||
self.toolbox.register("mutate_charge_discharge", tools.mutUniformInt, low=0, up=8, indpb=0.1)
|
||||
# - 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.1)
|
||||
# - Start hour mutation for household devices
|
||||
@ -173,11 +230,12 @@ class optimization_problem:
|
||||
if self.opti_param.get("haushaltsgeraete", 0) > 0:
|
||||
ems.set_haushaltsgeraet_start(spuelstart_int, global_start_hour=start_hour)
|
||||
|
||||
charge, discharge = self.split_charge_discharge(discharge_hours_bin)
|
||||
ac,dc,discharge = self.decode_charge_discharge(discharge_hours_bin)
|
||||
|
||||
|
||||
ems.set_akku_discharge_hours(discharge)
|
||||
ems.set_akku_charge_hours(charge)
|
||||
ems.set_akku_dc_charge_hours(dc)
|
||||
ems.set_akku_ac_charge_hours(ac)
|
||||
|
||||
if self.optimize_ev:
|
||||
eautocharge_hours_float = [
|
||||
@ -212,13 +270,6 @@ class optimization_problem:
|
||||
0.01 for i in range(self.prediction_hours) if discharge_hours_bin[i] == 0.0
|
||||
)
|
||||
|
||||
# Penalty for charging the electric vehicle during restricted hours
|
||||
# gesamtbilanz += sum(
|
||||
# self.strafe
|
||||
# for i in range(self.prediction_hours - self.fixed_eauto_hours, self.prediction_hours)
|
||||
# if eautocharge_hours_float[i] != 0.0
|
||||
# )
|
||||
|
||||
# Penalty for not meeting the minimum SOC (State of Charge) requirement
|
||||
if parameter["eauto_min_soc"] - ems.eauto.ladezustand_in_prozent() <= 0.0:
|
||||
gesamtbilanz += sum(
|
||||
@ -266,8 +317,8 @@ class optimization_problem:
|
||||
self.toolbox,
|
||||
mu=100,
|
||||
lambda_=150,
|
||||
cxpb=0.5,
|
||||
mutpb=0.5,
|
||||
cxpb=0.7,
|
||||
mutpb=0.3,
|
||||
ngen=ngen,
|
||||
stats=stats,
|
||||
halloffame=hof,
|
||||
@ -361,14 +412,18 @@ class optimization_problem:
|
||||
start_solution
|
||||
)
|
||||
|
||||
|
||||
|
||||
ac_charge, dc_charge, discharge = self.decode_charge_discharge(discharge_hours_bin)
|
||||
# Visualize the results
|
||||
visualisiere_ergebnisse(
|
||||
parameter["gesamtlast"],
|
||||
parameter["pv_forecast"],
|
||||
parameter["strompreis_euro_pro_wh"],
|
||||
o,
|
||||
discharge_hours_bin,
|
||||
eautocharge_hours_float,
|
||||
ac_charge,
|
||||
dc_charge,
|
||||
discharge,
|
||||
parameter["temperature_forecast"],
|
||||
start_hour,
|
||||
self.prediction_hours,
|
||||
@ -395,7 +450,7 @@ class optimization_problem:
|
||||
element_list = o[key].tolist()
|
||||
|
||||
# Change the first value to None
|
||||
element_list[0] = None
|
||||
#element_list[0] = None
|
||||
# Change the NaN to None (JSON)
|
||||
element_list = [
|
||||
None if isinstance(x, (int, float)) and np.isnan(x) else x for x in element_list
|
||||
@ -406,7 +461,9 @@ class optimization_problem:
|
||||
|
||||
# Return final results as a dictionary
|
||||
return {
|
||||
"discharge_hours_bin": discharge_hours_bin,
|
||||
"ac_charge": ac_charge.tolist(),
|
||||
"dc_charge":dc_charge.tolist(),
|
||||
"discharge_allowed":discharge.tolist(),
|
||||
"eautocharge_hours_float": eautocharge_hours_float,
|
||||
"result": o,
|
||||
"eauto_obj": ems.eauto.to_dict(),
|
||||
|
@ -18,8 +18,9 @@ def visualisiere_ergebnisse(
|
||||
pv_forecast,
|
||||
strompreise,
|
||||
ergebnisse,
|
||||
discharge_hours,
|
||||
laden_moeglich,
|
||||
ac, # AC charging allowed
|
||||
dc, # DC charging allowed
|
||||
discharge, # Discharge allowed
|
||||
temperature,
|
||||
start_hour,
|
||||
prediction_hours,
|
||||
@ -58,21 +59,7 @@ def visualisiere_ergebnisse(
|
||||
plt.grid(True)
|
||||
plt.legend()
|
||||
|
||||
# Electricity prices
|
||||
hours_p = np.arange(0, len(strompreise))
|
||||
plt.subplot(3, 2, 2)
|
||||
plt.plot(
|
||||
hours_p,
|
||||
strompreise,
|
||||
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.grid(True)
|
||||
|
||||
|
||||
# PV forecast
|
||||
plt.subplot(3, 2, 3)
|
||||
@ -122,30 +109,48 @@ def visualisiere_ergebnisse(
|
||||
|
||||
# Energy flow, grid feed-in, and grid consumption
|
||||
plt.subplot(3, 2, 1)
|
||||
plt.plot(hours, ergebnisse["Last_Wh_pro_Stunde"], label="Load (Wh)", marker="o")
|
||||
# Plot with transparency (alpha) and different linestyles
|
||||
plt.plot(
|
||||
hours,
|
||||
ergebnisse["Haushaltsgeraet_wh_pro_stunde"],
|
||||
label="Household Device (Wh)",
|
||||
marker="o",
|
||||
hours, ergebnisse["Last_Wh_pro_Stunde"], label="Load (Wh)", marker="o", linestyle="-", alpha=0.8
|
||||
)
|
||||
plt.plot(
|
||||
hours,
|
||||
ergebnisse["Netzeinspeisung_Wh_pro_Stunde"],
|
||||
label="Grid Feed-in (Wh)",
|
||||
marker="x",
|
||||
hours, ergebnisse["Haushaltsgeraet_wh_pro_stunde"], label="Household Device (Wh)", marker="o", linestyle="--", alpha=0.8
|
||||
)
|
||||
plt.plot(
|
||||
hours,
|
||||
ergebnisse["Netzbezug_Wh_pro_Stunde"],
|
||||
label="Grid Consumption (Wh)",
|
||||
marker="^",
|
||||
hours, ergebnisse["Netzeinspeisung_Wh_pro_Stunde"], label="Grid Feed-in (Wh)", marker="x", linestyle=":", alpha=0.8
|
||||
)
|
||||
plt.plot(hours, ergebnisse["Verluste_Pro_Stunde"], label="Losses (Wh)", marker="^")
|
||||
plt.plot(
|
||||
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
|
||||
)
|
||||
|
||||
# Title and labels
|
||||
plt.title("Energy Flow per Hour")
|
||||
plt.xlabel("Hour")
|
||||
plt.ylabel("Energy (Wh)")
|
||||
|
||||
# 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)
|
||||
plt.plot(
|
||||
hours_p,
|
||||
strompreise,
|
||||
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.grid(True)
|
||||
|
||||
# State of charge for batteries
|
||||
plt.subplot(3, 2, 2)
|
||||
@ -159,42 +164,30 @@ 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
|
||||
|
||||
ax1 = plt.subplot(3, 2, 3)
|
||||
# Plot charge and discharge values
|
||||
for hour, value in enumerate(discharge_hours):
|
||||
# Determine color and label based on the value
|
||||
if value > 0: # Positive values (discharge)
|
||||
color = "red"
|
||||
label = "Discharge" if hour == 0 else ""
|
||||
elif value < 0: # Negative values (charge)
|
||||
color = "blue"
|
||||
label = "Charge" if hour == 0 else ""
|
||||
# Plot for AC, DC charging, and Discharge status using bar charts
|
||||
ax1 = plt.subplot(3, 2, 5)
|
||||
|
||||
else:
|
||||
continue # Skip zero values
|
||||
# Plot AC charging as bars (relative values between 0 and 1)
|
||||
plt.bar(hours, ac, width=0.4, label="AC Charging (relative)", color="blue", alpha=0.6)
|
||||
|
||||
# Create colored areas with `axvspan`
|
||||
ax1.axvspan(
|
||||
hour, # Start of the hour
|
||||
hour + 1, # End of the hour
|
||||
ymin=0, # Lower bound
|
||||
ymax=abs(value) / 5 if value < 0 else value, # Adjust height based on the value
|
||||
color=color,
|
||||
alpha=0.3,
|
||||
label=label
|
||||
)
|
||||
# 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)
|
||||
|
||||
# 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))
|
||||
|
||||
# Configure the plot
|
||||
ax1.legend(loc="upper left")
|
||||
ax1.set_xlim(0, prediction_hours)
|
||||
ax1.set_xlabel("Hour")
|
||||
ax1.set_ylabel("Charge/Discharge Level")
|
||||
ax1.set_title("Charge and Discharge Hours Overview")
|
||||
ax1.set_ylabel("Relative Power (0-1) / Discharge (0 or 1)")
|
||||
ax1.set_title("AC/DC Charging and Discharge Overview")
|
||||
ax1.grid(True)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pdf.savefig() # Save the current figure state to the PDF
|
||||
plt.close() # Close the current figure to free up memory
|
||||
|
||||
@ -219,7 +212,6 @@ def visualisiere_ergebnisse(
|
||||
)
|
||||
# Annotate costs
|
||||
for hour, value in enumerate(costs):
|
||||
print(hour, " ", value)
|
||||
if value == 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')
|
||||
|
@ -5,6 +5,7 @@ from akkudoktoreos.class_akku import PVAkku
|
||||
from akkudoktoreos.class_ems import EnergieManagementSystem
|
||||
from akkudoktoreos.class_haushaltsgeraet import Haushaltsgeraet
|
||||
from akkudoktoreos.class_inverter import Wechselrichter # Example import
|
||||
from akkudoktoreos.visualize import *
|
||||
|
||||
prediction_hours = 48
|
||||
optimization_hours = 24
|
||||
@ -32,7 +33,7 @@ def create_ems_instance():
|
||||
|
||||
# Example initialization of electric car battery
|
||||
eauto = PVAkku(kapazitaet_wh=26400, start_soc_prozent=10, hours=48, min_soc_prozent=10)
|
||||
|
||||
eauto.set_charge_per_hour(np.full(48,1))
|
||||
# Parameters based on previous example data
|
||||
pv_prognose_wh = [
|
||||
0,
|
||||
@ -199,6 +200,9 @@ def create_ems_instance():
|
||||
haushaltsgeraet=home_appliance,
|
||||
wechselrichter=wechselrichter,
|
||||
)
|
||||
|
||||
|
||||
|
||||
return ems
|
||||
|
||||
|
||||
@ -212,6 +216,21 @@ def test_simulation(create_ems_instance):
|
||||
|
||||
result = ems.simuliere(start_stunde=start_hour)
|
||||
|
||||
visualisiere_ergebnisse(
|
||||
ems.gesamtlast,
|
||||
ems.pv_prognose_wh,
|
||||
ems.strompreis_euro_pro_wh,
|
||||
result,
|
||||
ems.akku.discharge_array+ems.akku.charge_array,
|
||||
None,
|
||||
ems.pv_prognose_wh,
|
||||
start_hour,
|
||||
48,
|
||||
np.full(48, 0.0),
|
||||
filename="visualization_results.pdf",
|
||||
extra_data=None,
|
||||
)
|
||||
|
||||
# Assertions to validate results
|
||||
assert result is not None, "Result should not be None"
|
||||
assert isinstance(result, dict), "Result should be a dictionary"
|
||||
|
224
tests/test_class_ems_2.py
Normal file
224
tests/test_class_ems_2.py
Normal file
@ -0,0 +1,224 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
from akkudoktoreos.visualize import *
|
||||
|
||||
from akkudoktoreos.class_akku import PVAkku
|
||||
from akkudoktoreos.class_ems import EnergieManagementSystem
|
||||
from akkudoktoreos.class_haushaltsgeraet import Haushaltsgeraet
|
||||
from akkudoktoreos.class_inverter import Wechselrichter # Example import
|
||||
|
||||
prediction_hours = 48
|
||||
optimization_hours = 24
|
||||
start_hour = 0
|
||||
|
||||
|
||||
# Example initialization of necessary components
|
||||
@pytest.fixture
|
||||
def create_ems_instance():
|
||||
"""
|
||||
Fixture to create an EnergieManagementSystem instance with given test parameters.
|
||||
"""
|
||||
# Initialize the battery and the inverter
|
||||
akku = PVAkku(kapazitaet_wh=5000, start_soc_prozent=80, hours=48, min_soc_prozent=10)
|
||||
akku.reset()
|
||||
wechselrichter = Wechselrichter(10000, akku)
|
||||
|
||||
# Household device (currently not used, set to None)
|
||||
home_appliance = Haushaltsgeraet(
|
||||
hours=prediction_hours,
|
||||
verbrauch_wh=2000,
|
||||
dauer_h=2,
|
||||
)
|
||||
home_appliance.set_startzeitpunkt(2)
|
||||
|
||||
# Example initialization of electric car battery
|
||||
eauto = PVAkku(kapazitaet_wh=26400, start_soc_prozent=100, hours=48, min_soc_prozent=100)
|
||||
|
||||
# Parameters based on previous example data
|
||||
pv_prognose_wh = np.full(prediction_hours, 0)
|
||||
pv_prognose_wh[10] = 5000.0
|
||||
pv_prognose_wh[11] = 5000.0
|
||||
|
||||
|
||||
strompreis_euro_pro_wh = np.full(48, 0.001)
|
||||
strompreis_euro_pro_wh [0:10] = 0.00001
|
||||
strompreis_euro_pro_wh [11:15] = 0.00005
|
||||
strompreis_euro_pro_wh [20] = 0.00001
|
||||
|
||||
einspeiseverguetung_euro_pro_wh = [0.00007] * len(strompreis_euro_pro_wh)
|
||||
|
||||
gesamtlast = [
|
||||
676.71,
|
||||
876.19,
|
||||
527.13,
|
||||
468.88,
|
||||
531.38,
|
||||
517.95,
|
||||
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,
|
||||
]
|
||||
|
||||
# Initialize the energy management system with the respective parameters
|
||||
ems = EnergieManagementSystem(
|
||||
pv_prognose_wh=pv_prognose_wh,
|
||||
strompreis_euro_pro_wh=strompreis_euro_pro_wh,
|
||||
einspeiseverguetung_euro_pro_wh=einspeiseverguetung_euro_pro_wh,
|
||||
eauto=eauto,
|
||||
gesamtlast=gesamtlast,
|
||||
haushaltsgeraet=home_appliance,
|
||||
wechselrichter=wechselrichter,
|
||||
)
|
||||
|
||||
|
||||
ac= np.full(prediction_hours,0)
|
||||
ac[20] = 1
|
||||
ems.set_akku_ac_charge_hours(ac)
|
||||
dc= np.full(prediction_hours,0)
|
||||
dc[11] = 1
|
||||
ems.set_akku_dc_charge_hours(dc)
|
||||
|
||||
return ems
|
||||
|
||||
|
||||
def test_simulation(create_ems_instance):
|
||||
"""
|
||||
Test the EnergieManagementSystem simulation method.
|
||||
"""
|
||||
ems = create_ems_instance
|
||||
|
||||
# Simulate starting from hour 0 (this value can be adjusted)
|
||||
result = ems.simuliere(start_stunde=start_hour)
|
||||
|
||||
# --- Pls do not remove! ---
|
||||
# visualisiere_ergebnisse(
|
||||
# ems.gesamtlast,
|
||||
# ems.pv_prognose_wh,
|
||||
# ems.strompreis_euro_pro_wh,
|
||||
# result,
|
||||
# ems.akku.discharge_array+ems.akku.charge_array,
|
||||
# None,
|
||||
# ems.pv_prognose_wh,
|
||||
# start_hour,
|
||||
# 48,
|
||||
# np.full(48, 0.0),
|
||||
# filename="visualization_results.pdf",
|
||||
# extra_data=None,
|
||||
# )
|
||||
|
||||
# Assertions to validate results
|
||||
assert result is not None, "Result should not be None"
|
||||
assert isinstance(result, dict), "Result should be a dictionary"
|
||||
assert "Last_Wh_pro_Stunde" in result, "Result should contain 'Last_Wh_pro_Stunde'"
|
||||
|
||||
"""
|
||||
Check the result of the simulation based on expected values.
|
||||
"""
|
||||
# Example result returned from the simulation (used for assertions)
|
||||
assert result is not None, "Result should not be None."
|
||||
|
||||
# Check that the result is a dictionary
|
||||
assert isinstance(result, dict), "Result should be a dictionary."
|
||||
|
||||
# Verify that the expected keys are present in the result
|
||||
expected_keys = [
|
||||
"Last_Wh_pro_Stunde",
|
||||
"Netzeinspeisung_Wh_pro_Stunde",
|
||||
"Netzbezug_Wh_pro_Stunde",
|
||||
"Kosten_Euro_pro_Stunde",
|
||||
"akku_soc_pro_stunde",
|
||||
"Einnahmen_Euro_pro_Stunde",
|
||||
"Gesamtbilanz_Euro",
|
||||
"E-Auto_SoC_pro_Stunde",
|
||||
"Gesamteinnahmen_Euro",
|
||||
"Gesamtkosten_Euro",
|
||||
"Verluste_Pro_Stunde",
|
||||
"Gesamt_Verluste",
|
||||
"Haushaltsgeraet_wh_pro_stunde",
|
||||
]
|
||||
|
||||
for key in expected_keys:
|
||||
assert key in result, f"The key '{key}' should be present in the result."
|
||||
|
||||
# Check the length of the main arrays
|
||||
assert (
|
||||
len(result["Last_Wh_pro_Stunde"]) == 48
|
||||
), "The length of 'Last_Wh_pro_Stunde' should be 48."
|
||||
assert (
|
||||
len(result["Netzeinspeisung_Wh_pro_Stunde"]) == 48
|
||||
), "The length of 'Netzeinspeisung_Wh_pro_Stunde' should be 48."
|
||||
assert (
|
||||
len(result["Netzbezug_Wh_pro_Stunde"]) == 48
|
||||
), "The length of 'Netzbezug_Wh_pro_Stunde' should be 48."
|
||||
assert (
|
||||
len(result["Kosten_Euro_pro_Stunde"]) == 48
|
||||
), "The length of 'Kosten_Euro_pro_Stunde' should be 48."
|
||||
assert (
|
||||
len(result["akku_soc_pro_stunde"]) == 48
|
||||
), "The length of 'akku_soc_pro_stunde' should be 48."
|
||||
|
||||
# Verfify DC and AC Charge Bins
|
||||
assert (
|
||||
abs(result["akku_soc_pro_stunde"][10] - 10.0) < 1e-5
|
||||
), "'akku_soc_pro_stunde[10]' should be 10."
|
||||
assert (
|
||||
abs(result["akku_soc_pro_stunde"][11] -79.275184) < 1e-5
|
||||
), "'akku_soc_pro_stunde[11]' should be 79.275184."
|
||||
|
||||
assert (
|
||||
abs(result["Netzeinspeisung_Wh_pro_Stunde"][10] - 3946.93) < 1e-3
|
||||
), "'Netzeinspeisung_Wh_pro_Stunde[11]' should be 4000."
|
||||
|
||||
assert (
|
||||
abs(result["Netzeinspeisung_Wh_pro_Stunde"][11] - 0.0) < 1e-3
|
||||
), "'Netzeinspeisung_Wh_pro_Stunde[11]' should be 0.0."
|
||||
|
||||
assert (
|
||||
abs(result["akku_soc_pro_stunde"][20] - 98
|
||||
) < 1e-5
|
||||
), "'akku_soc_pro_stunde[11]' should be 98."
|
||||
assert (
|
||||
abs(result["Last_Wh_pro_Stunde"][20] - 5450.98) < 1e-3
|
||||
), "'Netzeinspeisung_Wh_pro_Stunde[11]' should be 0.0."
|
||||
|
||||
|
||||
print("All tests passed successfully.")
|
Loading…
x
Reference in New Issue
Block a user