mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 08:55:15 +00:00
First self consumption predictor only PV > load
This commit is contained in:
parent
41747a02f8
commit
0e7350b1e9
@ -1,6 +1,9 @@
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from akkudoktoreos.devices.battery import PVAkku
|
from akkudoktoreos.devices.battery import PVAkku
|
||||||
|
from akkudoktoreos.prediction.self_consumption_probability import (
|
||||||
|
self_consumption_probability_interpolator,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WechselrichterParameters(BaseModel):
|
class WechselrichterParameters(BaseModel):
|
||||||
@ -8,11 +11,17 @@ class WechselrichterParameters(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class Wechselrichter:
|
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 = (
|
self.max_leistung_wh = (
|
||||||
parameters.max_leistung_wh # Maximum power that the inverter can handle
|
parameters.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
|
||||||
|
self.self_consumption_predictor = self_consumption_predictor
|
||||||
|
|
||||||
def energie_verarbeiten(
|
def energie_verarbeiten(
|
||||||
self, erzeugung: float, verbrauch: float, hour: int
|
self, erzeugung: float, verbrauch: float, hour: int
|
||||||
@ -21,7 +30,7 @@ class Wechselrichter:
|
|||||||
netzeinspeisung = 0.0 # Grid feed-in
|
netzeinspeisung = 0.0 # Grid feed-in
|
||||||
netzbezug = 0.0 # Grid draw
|
netzbezug = 0.0 # Grid draw
|
||||||
eigenverbrauch = 0.0 # Self-consumption
|
eigenverbrauch = 0.0 # Self-consumption
|
||||||
|
aus_akku = 0.0
|
||||||
if erzeugung >= verbrauch:
|
if erzeugung >= verbrauch:
|
||||||
if verbrauch > self.max_leistung_wh:
|
if verbrauch > self.max_leistung_wh:
|
||||||
# If consumption exceeds maximum inverter power
|
# If consumption exceeds maximum inverter power
|
||||||
@ -30,10 +39,14 @@ class Wechselrichter:
|
|||||||
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:
|
||||||
|
scr = self.self_consumption_predictor.calculate_self_consumption(
|
||||||
|
verbrauch, erzeugung
|
||||||
|
)
|
||||||
|
|
||||||
# Remaining power after consumption
|
# 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
|
# 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:
|
if restlast_evq > 0:
|
||||||
# Akku muss den Restverbrauch decken
|
# Akku muss den Restverbrauch decken
|
||||||
|
@ -5,6 +5,7 @@ import numpy as np
|
|||||||
from deap import algorithms, base, creator, tools
|
from deap import algorithms, base, creator, tools
|
||||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from akkudoktoreos.config import AppConfig
|
from akkudoktoreos.config import AppConfig
|
||||||
from akkudoktoreos.devices.battery import (
|
from akkudoktoreos.devices.battery import (
|
||||||
@ -13,6 +14,9 @@ from akkudoktoreos.devices.battery import (
|
|||||||
PVAkku,
|
PVAkku,
|
||||||
PVAkkuParameters,
|
PVAkkuParameters,
|
||||||
)
|
)
|
||||||
|
from akkudoktoreos.prediction.self_consumption_probability import (
|
||||||
|
self_consumption_probability_interpolator,
|
||||||
|
)
|
||||||
from akkudoktoreos.devices.generic import HomeAppliance, HomeApplianceParameters
|
from akkudoktoreos.devices.generic import HomeAppliance, HomeApplianceParameters
|
||||||
from akkudoktoreos.devices.inverter import Wechselrichter, WechselrichterParameters
|
from akkudoktoreos.devices.inverter import Wechselrichter, WechselrichterParameters
|
||||||
from akkudoktoreos.prediction.ems import (
|
from akkudoktoreos.prediction.ems import (
|
||||||
@ -142,7 +146,7 @@ class optimization_problem:
|
|||||||
|
|
||||||
# AC states
|
# AC states
|
||||||
ac_mask = (discharge_hours_bin_np >= 2 * len_ac) & (discharge_hours_bin_np < 3 * len_ac)
|
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)
|
# DC states (if enabled)
|
||||||
if self.optimize_dc_charge:
|
if self.optimize_dc_charge:
|
||||||
@ -350,10 +354,10 @@ class optimization_problem:
|
|||||||
worst_case: bool,
|
worst_case: bool,
|
||||||
) -> Tuple[float]:
|
) -> Tuple[float]:
|
||||||
"""Evaluate the fitness of an individual solution based on the simulation results."""
|
"""Evaluate the fitness of an individual solution based on the simulation results."""
|
||||||
try:
|
# try:
|
||||||
o = self.evaluate_inner(individual, ems, start_hour)
|
o = self.evaluate_inner(individual, ems, start_hour)
|
||||||
except Exception as e:
|
# except Exception as e:
|
||||||
return (100000.0,) # Return a high penalty in case of an exception
|
# return (100000.0,) # Return a high penalty in case of an exception
|
||||||
|
|
||||||
gesamtbilanz = o["Gesamtbilanz_Euro"] * (-1.0 if worst_case else 1.0)
|
gesamtbilanz = o["Gesamtbilanz_Euro"] * (-1.0 if worst_case else 1.0)
|
||||||
|
|
||||||
@ -443,13 +447,18 @@ class optimization_problem:
|
|||||||
parameters: OptimizationParameters,
|
parameters: OptimizationParameters,
|
||||||
start_hour: int,
|
start_hour: int,
|
||||||
worst_case: bool = False,
|
worst_case: bool = False,
|
||||||
ngen: int = 600,
|
ngen: int = 200,
|
||||||
) -> OptimizeResponse:
|
) -> OptimizeResponse:
|
||||||
"""Perform EMS (Energy Management System) optimization and visualize results."""
|
"""Perform EMS (Energy Management System) optimization and visualize results."""
|
||||||
einspeiseverguetung_euro_pro_wh = np.full(
|
einspeiseverguetung_euro_pro_wh = np.full(
|
||||||
self.prediction_hours, parameters.ems.einspeiseverguetung_euro_pro_wh
|
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
|
# Initialize PV and EV batteries
|
||||||
akku = PVAkku(
|
akku = PVAkku(
|
||||||
parameters.pv_akku,
|
parameters.pv_akku,
|
||||||
@ -481,7 +490,11 @@ class optimization_problem:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Initialize the inverter and energy management system
|
# Initialize the inverter and energy management system
|
||||||
wr = Wechselrichter(parameters.wechselrichter, akku)
|
wr = Wechselrichter(
|
||||||
|
parameters.wechselrichter,
|
||||||
|
akku,
|
||||||
|
self_consumption_predictor=sc,
|
||||||
|
)
|
||||||
ems = EnergieManagementSystem(
|
ems = EnergieManagementSystem(
|
||||||
self._config.eos,
|
self._config.eos,
|
||||||
parameters.ems,
|
parameters.ems,
|
||||||
|
@ -10,9 +10,11 @@ class self_consumption_probability_interpolator:
|
|||||||
def __init__(self, filepath: str | Path):
|
def __init__(self, filepath: str | Path):
|
||||||
self.filepath = filepath
|
self.filepath = filepath
|
||||||
self.interpolator = None
|
self.interpolator = None
|
||||||
|
print("OPEN")
|
||||||
# Load the RegularGridInterpolator
|
# Load the RegularGridInterpolator
|
||||||
with open("regular_grid_interpolator.pkl", "rb") as file:
|
with open(self.filepath, "rb") as file:
|
||||||
interpolator = pickle.load(self.filepath)
|
print("OPENED")
|
||||||
|
self.interpolator = pickle.load(file)
|
||||||
|
|
||||||
def calculate_self_consumption(self, load_1h_power: float, pv_power: float) -> float:
|
def calculate_self_consumption(self, load_1h_power: float, pv_power: float) -> float:
|
||||||
"""Calculate the PV self-consumption rate using RegularGridInterpolator.
|
"""Calculate the PV self-consumption rate using RegularGridInterpolator.
|
||||||
@ -29,11 +31,13 @@ class self_consumption_probability_interpolator:
|
|||||||
|
|
||||||
# Get probabilities for all partial loads
|
# Get probabilities for all partial loads
|
||||||
points = np.array([np.full_like(partial_loads, load_1h_power), partial_loads]).T
|
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()
|
probabilities = probabilities / probabilities.sum()
|
||||||
for i, w in enumerate(partial_loads):
|
# for i, w in enumerate(partial_loads):
|
||||||
print(w, ": ", probabilities[i])
|
# print(w, ": ", probabilities[i])
|
||||||
print(probabilities.sum())
|
# print(probabilities.sum())
|
||||||
# Ensure probabilities are within [0, 1]
|
# Ensure probabilities are within [0, 1]
|
||||||
probabilities = np.clip(probabilities, 0, 1)
|
probabilities = np.clip(probabilities, 0, 1)
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ app = FastAPI(
|
|||||||
working_dir = get_working_dir()
|
working_dir = get_working_dir()
|
||||||
# copy config to working directory. Make this a CLI option later
|
# copy config to working directory. Make this a CLI option later
|
||||||
config = load_config(working_dir, True)
|
config = load_config(working_dir, True)
|
||||||
opt_class = optimization_problem(config)
|
opt_class = optimization_problem(config, verbose=False)
|
||||||
server_dir = Path(__file__).parent.resolve()
|
server_dir = Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ def fastapi_optimize(
|
|||||||
] = None,
|
] = None,
|
||||||
) -> OptimizeResponse:
|
) -> OptimizeResponse:
|
||||||
if start_hour is None:
|
if start_hour is None:
|
||||||
start_hour = datetime.now().hour
|
start_hour = 10 # datetime.now().hour
|
||||||
|
|
||||||
# Perform optimization simulation
|
# Perform optimization simulation
|
||||||
result = opt_class.optimierung_ems(parameters=parameters, start_hour=start_hour)
|
result = opt_class.optimierung_ems(parameters=parameters, start_hour=start_hour)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user