First self consumption predictor only PV > load

This commit is contained in:
Andreas 2024-12-20 15:59:49 +01:00 committed by Andreas
parent c56d29f63d
commit 4e8e9bd0c0
4 changed files with 49 additions and 19 deletions

View File

@ -1,6 +1,9 @@
from pydantic import BaseModel, Field
from akkudoktoreos.devices.battery import PVAkku
from akkudoktoreos.prediction.self_consumption_probability import (
self_consumption_probability_interpolator,
)
class WechselrichterParameters(BaseModel):
@ -8,11 +11,17 @@ class WechselrichterParameters(BaseModel):
class Wechselrichter:
def __init__(self, parameters: WechselrichterParameters, akku: PVAkku):
def __init__(
self,
parameters: WechselrichterParameters,
akku: PVAkku,
self_consumption_predictor: self_consumption_probability_interpolator,
):
self.max_leistung_wh = (
parameters.max_leistung_wh # Maximum power that the inverter can handle
)
self.akku = akku # Connection to a battery object
self.self_consumption_predictor = self_consumption_predictor
def energie_verarbeiten(
self, erzeugung: float, verbrauch: float, hour: int
@ -21,7 +30,7 @@ class Wechselrichter:
netzeinspeisung = 0.0 # Grid feed-in
netzbezug = 0.0 # Grid draw
eigenverbrauch = 0.0 # Self-consumption
aus_akku = 0.0
if erzeugung >= verbrauch:
if verbrauch > self.max_leistung_wh:
# If consumption exceeds maximum inverter power
@ -30,10 +39,14 @@ class Wechselrichter:
netzbezug = -restleistung_nach_verbrauch # Negative indicates feeding into the grid
eigenverbrauch = self.max_leistung_wh
else:
scr = self.self_consumption_predictor.calculate_self_consumption(
verbrauch, erzeugung
)
# Remaining power after consumption
restleistung_nach_verbrauch = (erzeugung - verbrauch) * 0.95 # EVQ
restleistung_nach_verbrauch = (erzeugung - verbrauch) * scr # EVQ
# Remaining load Self Consumption not perfect
restlast_evq = (erzeugung - verbrauch) * (1.0 - 0.95)
restlast_evq = (erzeugung - verbrauch) * (1.0 - scr)
if restlast_evq > 0:
# Akku muss den Restverbrauch decken

View File

@ -5,6 +5,7 @@ import numpy as np
from deap import algorithms, base, creator, tools
from pydantic import BaseModel, Field, field_validator, model_validator
from typing_extensions import Self
from pathlib import Path
from akkudoktoreos.config import AppConfig
from akkudoktoreos.devices.battery import (
@ -13,6 +14,9 @@ from akkudoktoreos.devices.battery import (
PVAkku,
PVAkkuParameters,
)
from akkudoktoreos.prediction.self_consumption_probability import (
self_consumption_probability_interpolator,
)
from akkudoktoreos.devices.generic import HomeAppliance, HomeApplianceParameters
from akkudoktoreos.devices.inverter import Wechselrichter, WechselrichterParameters
from akkudoktoreos.prediction.ems import (
@ -142,7 +146,7 @@ class optimization_problem:
# AC states
ac_mask = (discharge_hours_bin_np >= 2 * len_ac) & (discharge_hours_bin_np < 3 * len_ac)
ac_indices = discharge_hours_bin_np[ac_mask] - 2 * len_ac
ac_indices = (discharge_hours_bin_np[ac_mask] - 2 * len_ac).astype(int)
# DC states (if enabled)
if self.optimize_dc_charge:
@ -350,10 +354,10 @@ class optimization_problem:
worst_case: bool,
) -> Tuple[float]:
"""Evaluate the fitness of an individual solution based on the simulation results."""
try:
o = self.evaluate_inner(individual, ems, start_hour)
except Exception as e:
return (100000.0,) # Return a high penalty in case of an exception
# try:
o = self.evaluate_inner(individual, ems, start_hour)
# 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)
@ -443,13 +447,18 @@ class optimization_problem:
parameters: OptimizationParameters,
start_hour: int,
worst_case: bool = False,
ngen: int = 600,
ngen: int = 200,
) -> OptimizeResponse:
"""Perform EMS (Energy Management System) optimization and visualize results."""
einspeiseverguetung_euro_pro_wh = np.full(
self.prediction_hours, parameters.ems.einspeiseverguetung_euro_pro_wh
)
# 1h Load to Sub 1h Load Distribution -> SelfConsumptionRate
sc = self_consumption_probability_interpolator(
Path(__file__).parent.resolve() / ".." / "data" / "regular_grid_interpolator.pkl"
)
# Initialize PV and EV batteries
akku = PVAkku(
parameters.pv_akku,
@ -481,7 +490,11 @@ class optimization_problem:
)
# Initialize the inverter and energy management system
wr = Wechselrichter(parameters.wechselrichter, akku)
wr = Wechselrichter(
parameters.wechselrichter,
akku,
self_consumption_predictor=sc,
)
ems = EnergieManagementSystem(
self._config.eos,
parameters.ems,

View File

@ -10,9 +10,11 @@ class self_consumption_probability_interpolator:
def __init__(self, filepath: str | Path):
self.filepath = filepath
self.interpolator = None
print("OPEN")
# Load the RegularGridInterpolator
with open("regular_grid_interpolator.pkl", "rb") as file:
interpolator = pickle.load(self.filepath)
with open(self.filepath, "rb") as file:
print("OPENED")
self.interpolator = pickle.load(file)
def calculate_self_consumption(self, load_1h_power: float, pv_power: float) -> float:
"""Calculate the PV self-consumption rate using RegularGridInterpolator.
@ -29,11 +31,13 @@ class self_consumption_probability_interpolator:
# Get probabilities for all partial loads
points = np.array([np.full_like(partial_loads, load_1h_power), partial_loads]).T
probabilities = interpolator(points)
if self.interpolator == None:
return -1.0
probabilities = self.interpolator(points)
probabilities = probabilities / probabilities.sum()
for i, w in enumerate(partial_loads):
print(w, ": ", probabilities[i])
print(probabilities.sum())
# for i, w in enumerate(partial_loads):
# print(w, ": ", probabilities[i])
# print(probabilities.sum())
# Ensure probabilities are within [0, 1]
probabilities = np.clip(probabilities, 0, 1)

View File

@ -48,7 +48,7 @@ app = FastAPI(
working_dir = get_working_dir()
# copy config to working directory. Make this a CLI option later
config = load_config(working_dir, True)
opt_class = optimization_problem(config)
opt_class = optimization_problem(config, verbose=False)
server_dir = Path(__file__).parent.resolve()
@ -196,7 +196,7 @@ def fastapi_optimize(
] = None,
) -> OptimizeResponse:
if start_hour is None:
start_hour = datetime.now().hour
start_hour = 10 # datetime.now().hour
# Perform optimization simulation
result = opt_class.optimierung_ems(parameters=parameters, start_hour=start_hour)