From 004e1f3dc743b5918cf1b25b3f075b686c502148 Mon Sep 17 00:00:00 2001 From: Andreas Date: Wed, 9 Oct 2024 16:52:51 +0200 Subject: [PATCH] [BUG]: class_ems nd_array not JSON serializable Big Bugfix - not sure if everything works --- single_test_optimization.py | 113 ++------------------- src/akkudoktoreos/class_ems.py | 53 +++------- src/akkudoktoreos/class_haushaltsgeraet.py | 1 - src/akkudoktoreos/class_optimize.py | 33 +++++- src/akkudoktoreosserver/flask_server.py | 23 +++-- tests/test_class_ems.py | 16 ++- 6 files changed, 79 insertions(+), 160 deletions(-) diff --git a/single_test_optimization.py b/single_test_optimization.py index f442d1d..679d0a9 100644 --- a/single_test_optimization.py +++ b/single_test_optimization.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 -from pprint import pprint +import json # Import necessary modules from the project -from modules.class_optimize import optimization_problem +from akkudoktoreos.class_optimize import optimization_problem start_hour = 10 @@ -216,107 +216,7 @@ gesamtlast = [ ] # Start Solution (binary) -start_solution = [ - 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, -] +start_solution = None # Define parameters for the optimization problem parameter = { @@ -341,7 +241,7 @@ parameter = { # Electricity price forecast (48 hours) "strompreis_euro_pro_wh": strompreis_euro_pro_wh, # Minimum SOC for electric car - "eauto_min_soc": 1000, + "eauto_min_soc": 80, # Electric car battery capacity (Wh) "eauto_cap": 60000, # Charging efficiency of the electric car @@ -371,4 +271,7 @@ opt_class = optimization_problem( ergebnis = opt_class.optimierung_ems(parameter=parameter, start_hour=start_hour) # Print or visualize the result -pprint(ergebnis) +# pprint(ergebnis) + +json_data = json.dumps(ergebnis) +print(json_data) diff --git a/src/akkudoktoreos/class_ems.py b/src/akkudoktoreos/class_ems.py index 36d21bb..7b4074f 100644 --- a/src/akkudoktoreos/class_ems.py +++ b/src/akkudoktoreos/class_ems.py @@ -58,15 +58,15 @@ class EnergieManagementSystem: total_hours = ende - start_stunde # Pre-allocate arrays for the results, optimized for speed - last_wh_pro_stunde = np.zeros(total_hours) - netzeinspeisung_wh_pro_stunde = np.zeros(total_hours) - netzbezug_wh_pro_stunde = np.zeros(total_hours) - kosten_euro_pro_stunde = np.zeros(total_hours) - einnahmen_euro_pro_stunde = np.zeros(total_hours) - akku_soc_pro_stunde = np.zeros(total_hours) - eauto_soc_pro_stunde = np.zeros(total_hours) - verluste_wh_pro_stunde = np.zeros(total_hours) - haushaltsgeraet_wh_pro_stunde = np.zeros(total_hours) + last_wh_pro_stunde = np.full((total_hours), np.nan) + netzeinspeisung_wh_pro_stunde = np.full((total_hours), np.nan) + netzbezug_wh_pro_stunde = np.full((total_hours), np.nan) + kosten_euro_pro_stunde = np.full((total_hours), np.nan) + einnahmen_euro_pro_stunde = np.full((total_hours), np.nan) + akku_soc_pro_stunde = np.full((total_hours), np.nan) + eauto_soc_pro_stunde = np.full((total_hours), np.nan) + verluste_wh_pro_stunde = np.full((total_hours), np.nan) + haushaltsgeraet_wh_pro_stunde = np.full((total_hours), np.nan) # Set initial state akku_soc_pro_stunde[0] = self.akku.ladezustand_in_prozent() @@ -78,7 +78,7 @@ class EnergieManagementSystem: # Accumulate loads and PV generation verbrauch = self.gesamtlast[stunde] - + verluste_wh_pro_stunde[stunde_since_now] = 0.0 if self.haushaltsgeraet: ha_load = self.haushaltsgeraet.get_last_fuer_stunde(stunde) verbrauch += ha_load @@ -117,7 +117,7 @@ class EnergieManagementSystem: akku_soc_pro_stunde[stunde_since_now] = self.akku.ladezustand_in_prozent() # Total cost and return - gesamtkosten_euro = np.sum(kosten_euro_pro_stunde) - np.sum( + gesamtkosten_euro = np.nansum(kosten_euro_pro_stunde) - np.nansum( einnahmen_euro_pro_stunde ) @@ -131,35 +131,10 @@ class EnergieManagementSystem: "Einnahmen_Euro_pro_Stunde": einnahmen_euro_pro_stunde, "Gesamtbilanz_Euro": gesamtkosten_euro, "E-Auto_SoC_pro_Stunde": eauto_soc_pro_stunde, - "Gesamteinnahmen_Euro": np.sum(einnahmen_euro_pro_stunde), - "Gesamtkosten_Euro": np.sum(kosten_euro_pro_stunde), + "Gesamteinnahmen_Euro": np.nansum(einnahmen_euro_pro_stunde), + "Gesamtkosten_Euro": np.nansum(kosten_euro_pro_stunde), "Verluste_Pro_Stunde": verluste_wh_pro_stunde, - "Gesamt_Verluste": np.sum(verluste_wh_pro_stunde), + "Gesamt_Verluste": np.nansum(verluste_wh_pro_stunde), "Haushaltsgeraet_wh_pro_stunde": haushaltsgeraet_wh_pro_stunde, } - - # List output keys where the first element needs to be changed to None - keys_to_modify = [ - "Last_Wh_pro_Stunde", - "Netzeinspeisung_Wh_pro_Stunde", - "akku_soc_pro_stunde", - "Netzbezug_Wh_pro_Stunde", - "Kosten_Euro_pro_Stunde", - "Einnahmen_Euro_pro_Stunde", - "E-Auto_SoC_pro_Stunde", - "Verluste_Pro_Stunde", - "Haushaltsgeraet_wh_pro_stunde" - ] - - # Loop through each key in the list - for key in keys_to_modify: - # Convert the NumPy array to a list - element_list = out[key].tolist() - - # Change the first value to None - element_list[0] = None - - # Assign the modified list back to the dictionary - out[key] = element_list - return out diff --git a/src/akkudoktoreos/class_haushaltsgeraet.py b/src/akkudoktoreos/class_haushaltsgeraet.py index a3f4536..7b9f37a 100644 --- a/src/akkudoktoreos/class_haushaltsgeraet.py +++ b/src/akkudoktoreos/class_haushaltsgeraet.py @@ -16,7 +16,6 @@ class Haushaltsgeraet: :param start_hour: The hour at which the device should start. """ self.reset() - # Check if the duration of use is within the available time frame if start_hour + self.dauer_h > self.hours: raise ValueError("The duration of use exceeds the available time frame.") diff --git a/src/akkudoktoreos/class_optimize.py b/src/akkudoktoreos/class_optimize.py index 6c46d98..918418f 100644 --- a/src/akkudoktoreos/class_optimize.py +++ b/src/akkudoktoreos/class_optimize.py @@ -15,7 +15,7 @@ from akkudoktoreos.visualize import visualisiere_ergebnisse class optimization_problem: def __init__( self, - prediction_hours: int = 24, + prediction_hours: int = 48, strafe: float = 10, optimization_hours: int = 24, verbose: bool = False, @@ -138,7 +138,7 @@ class optimization_problem: """ try: o = self.evaluate_inner(individual, ems, start_hour) - except Exception: + except Exception as e: return (100000.0,) # Return a high penalty in case of an exception gesamtbilanz = o["Gesamtbilanz_Euro"] * (-1.0 if worst_case else 1.0) @@ -326,6 +326,35 @@ class optimization_problem: extra_data=extra_data, ) + # List output keys where the first element needs to be changed to None + keys_to_modify = [ + "Last_Wh_pro_Stunde", + "Netzeinspeisung_Wh_pro_Stunde", + "akku_soc_pro_stunde", + "Netzbezug_Wh_pro_Stunde", + "Kosten_Euro_pro_Stunde", + "Einnahmen_Euro_pro_Stunde", + "E-Auto_SoC_pro_Stunde", + "Verluste_Pro_Stunde", + "Haushaltsgeraet_wh_pro_stunde", + ] + + # Loop through each key in the list + for key in keys_to_modify: + # Convert the NumPy array to a list + element_list = o[key].tolist() + + # Change the first value to 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 + ] + + # Assign the modified list back to the dictionary + o[key] = element_list + # Return final results as a dictionary return { "discharge_hours_bin": discharge_hours_bin, diff --git a/src/akkudoktoreosserver/flask_server.py b/src/akkudoktoreosserver/flask_server.py index 80b0cb9..743da59 100755 --- a/src/akkudoktoreosserver/flask_server.py +++ b/src/akkudoktoreosserver/flask_server.py @@ -3,14 +3,14 @@ import os from datetime import datetime from typing import Any, TypeGuard -import json + import matplotlib # Sets the Matplotlib backend to 'Agg' for rendering plots in environments without a display matplotlib.use("Agg") import pandas as pd -from flask import Flask, jsonify, redirect, request, send_from_directory, url_for, Response +from flask import Flask, jsonify, redirect, request, send_from_directory, url_for from akkudoktoreos.class_load import LoadForecast from akkudoktoreos.class_load_container import Gesamtlast @@ -211,6 +211,12 @@ def flask_pvprognose(): @app.route("/optimize", methods=["POST"]) def flask_optimize(): + with open( + "C:\\Users\\drbac\\OneDrive\\Dokumente\\PythonPojects\\EOS\\debug_output.txt", + "a", + ) as f: + f.write("Test\n") + if request.method == "POST": from datetime import datetime @@ -243,18 +249,17 @@ def flask_optimize(): {"error": f"Missing parameter: {', '.join(missing_params)}"} ), 400 # Return error for missing parameters - # Perform optimization simulation - result = opt_class.optimierung_ems( - parameter=parameter, start_hour=datetime.now().hour - ) - # Optional min SoC PV Battery if "min_soc_prozent" not in parameter: parameter["min_soc_prozent"] = None + # Perform optimization simulation + result = opt_class.optimierung_ems( + parameter=parameter, start_hour=datetime.now().hour + ) + print(result) # convert to JSON (None accepted by dumps) - json_data = json.dumps(result) - return Response(json_data, mimetype='application/json') + return jsonify(result) @app.route("/visualisierungsergebnisse.pdf") diff --git a/tests/test_class_ems.py b/tests/test_class_ems.py index ed24f09..d76c2bf 100644 --- a/tests/test_class_ems.py +++ b/tests/test_class_ems.py @@ -1,3 +1,4 @@ +import numpy as np import pytest from akkudoktoreos.class_akku import PVAkku @@ -279,15 +280,15 @@ def test_simulation(create_ems_instance): # Verify that the value at index 0 is 'None' assert ( - result["Last_Wh_pro_Stunde"][0] is None + np.isnan(result["Last_Wh_pro_Stunde"][0]) ), "The value at index 0 of 'Last_Wh_pro_Stunde' should be None." # Check that 'Netzeinspeisung_Wh_pro_Stunde' and 'Netzbezug_Wh_pro_Stunde' are consistent assert ( - result["Netzeinspeisung_Wh_pro_Stunde"][0] is None + np.isnan(result["Netzeinspeisung_Wh_pro_Stunde"][0]) ), "The value at index 0 of 'Netzeinspeisung_Wh_pro_Stunde' should be None." assert ( - result["Netzbezug_Wh_pro_Stunde"][0] is None + np.isnan(result["Netzbezug_Wh_pro_Stunde"][0]) ), "The value at index 0 of 'Netzbezug_Wh_pro_Stunde' should be None." assert ( result["Netzbezug_Wh_pro_Stunde"][1] == 21679.13 @@ -325,7 +326,14 @@ def test_simulation(create_ems_instance): ), "The sum of 'ems.haushaltsgeraet.get_lastkurve()' should be 2000." assert ( - sum(result["Haushaltsgeraet_wh_pro_stunde"]) == 2000 + np.nansum( + np.where( + np.equal(result["Haushaltsgeraet_wh_pro_stunde"], None), + np.nan, + np.array(result["Haushaltsgeraet_wh_pro_stunde"]), + ) + ) + == 2000 ), "The sum of 'Haushaltsgeraet_wh_pro_stunde' should be 2000." print("All tests passed successfully.")