Cleanup: parameters: extra=forbid, optimize: battery, inverter optional (#361)

* Cleanup: parameters: extra=forbid, optimize: battery, inverter optional

 * Don't allow extra fields for parameters/REST-API (at least for now while
   changing API).
 * Allow both battery and inverter to be set optionally (atm optional
   battery not implemented, no API constraints).
 * inverter: Remove default max_power_wh
 * single_test_optimization: Add more cli-parameters

* Workflow docker-build: Don't try to authenticate for PRs

 * Secrets are not available anyway for forks.
This commit is contained in:
Dominique Lasserre
2025-01-13 21:44:17 +01:00
committed by GitHub
parent 745086c2eb
commit 9ad61f66b2
12 changed files with 119 additions and 109 deletions

View File

@@ -1,64 +0,0 @@
import numpy as np
from pydantic import BaseModel, Field
class HomeApplianceParameters(BaseModel):
consumption_wh: int = Field(
gt=0,
description="An integer representing the energy consumption of a household device in watt-hours.",
)
duration_h: int = Field(
gt=0,
description="An integer representing the usage duration of a household device in hours.",
)
class HomeAppliance:
def __init__(self, parameters: HomeApplianceParameters, hours: int):
self.hours = hours # Total duration for which the planning is done
self.consumption_wh = (
parameters.consumption_wh
) # Total energy consumption of the device in kWh
self.duration_h = parameters.duration_h # Duration of use in hours
self.load_curve = np.zeros(self.hours) # Initialize the load curve with zeros
def set_starting_time(self, start_hour: int, global_start_hour: int = 0) -> None:
"""Sets the start time of the device and generates the corresponding load curve.
: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.duration_h > self.hours:
raise ValueError("The duration of use exceeds the available time frame.")
if start_hour < global_start_hour:
raise ValueError("The start time is earlier than the available time frame.")
# Calculate power per hour based on total consumption and duration
power_per_hour = self.consumption_wh / self.duration_h # Convert to watt-hours
# Set the power for the duration of use in the load curve array
self.load_curve[start_hour : start_hour + self.duration_h] = power_per_hour
def reset(self) -> None:
"""Resets the load curve."""
self.load_curve = np.zeros(self.hours)
def get_load_curve(self) -> np.ndarray:
"""Returns the current load curve."""
return self.load_curve
def get_load_for_hour(self, hour: int) -> float:
"""Returns the load for a specific hour.
:param hour: The hour for which the load is queried.
:return: The load in watts for the specified hour.
"""
if hour < 0 or hour >= self.hours:
raise ValueError("The specified hour is outside the available time frame.")
return self.load_curve[hour]
def get_latest_starting_point(self) -> int:
"""Returns the latest possible start time at which the device can still run completely."""
return self.hours - self.duration_h

View File

@@ -8,7 +8,7 @@ from typing_extensions import Self
from akkudoktoreos.core.coreabc import ConfigMixin, PredictionMixin, SingletonMixin
from akkudoktoreos.core.logging import get_logger
from akkudoktoreos.core.pydantic import PydanticBaseModel
from akkudoktoreos.core.pydantic import ParametersBaseModel, PydanticBaseModel
from akkudoktoreos.devices.battery import Battery
from akkudoktoreos.devices.generic import HomeAppliance
from akkudoktoreos.devices.inverter import Inverter
@@ -18,7 +18,7 @@ from akkudoktoreos.utils.utils import NumpyEncoder
logger = get_logger(__name__)
class EnergieManagementSystemParameters(PydanticBaseModel):
class EnergieManagementSystemParameters(ParametersBaseModel):
pv_prognose_wh: list[float] = Field(
description="An array of floats representing the forecasted photovoltaic output in watts for different time intervals."
)
@@ -50,7 +50,7 @@ class EnergieManagementSystemParameters(PydanticBaseModel):
return self
class SimulationResult(PydanticBaseModel):
class SimulationResult(ParametersBaseModel):
"""This object contains the results of the simulation and provides insights into various parameters over the entire forecast period."""
Last_Wh_pro_Stunde: list[Optional[float]] = Field(description="TBD")

View File

@@ -478,3 +478,7 @@ class PydanticDateTimeSeries(PydanticBaseModel):
dtype=str(series.dtype),
tz=tz,
)
class ParametersBaseModel(PydanticBaseModel):
model_config = ConfigDict(extra="forbid")

View File

@@ -4,6 +4,7 @@ import numpy as np
from pydantic import BaseModel, Field, field_validator
from akkudoktoreos.core.logging import get_logger
from akkudoktoreos.core.pydantic import ParametersBaseModel
from akkudoktoreos.devices.devicesabc import DeviceBase
from akkudoktoreos.utils.utils import NumpyEncoder
@@ -24,7 +25,7 @@ def initial_soc_percentage_field(description: str) -> int:
return Field(default=0, ge=0, le=100, description=description)
class BaseBatteryParameters(BaseModel):
class BaseBatteryParameters(ParametersBaseModel):
"""Base class for battery parameters with fields for capacity, efficiency, and state of charge."""
capacity_wh: int = Field(

View File

@@ -1,15 +1,16 @@
from typing import Optional
import numpy as np
from pydantic import BaseModel, Field
from pydantic import Field
from akkudoktoreos.core.logging import get_logger
from akkudoktoreos.core.pydantic import ParametersBaseModel
from akkudoktoreos.devices.devicesabc import DeviceBase
logger = get_logger(__name__)
class HomeApplianceParameters(BaseModel):
class HomeApplianceParameters(ParametersBaseModel):
consumption_wh: int = Field(
gt=0,
description="An integer representing the energy consumption of a household device in watt-hours.",

View File

@@ -1,17 +1,18 @@
from typing import Optional
from pydantic import BaseModel, Field
from pydantic import Field
from scipy.interpolate import RegularGridInterpolator
from akkudoktoreos.core.logging import get_logger
from akkudoktoreos.core.pydantic import ParametersBaseModel
from akkudoktoreos.devices.battery import Battery
from akkudoktoreos.devices.devicesabc import DeviceBase
logger = get_logger(__name__)
class InverterParameters(BaseModel):
max_power_wh: float = Field(default=10000, gt=0)
class InverterParameters(ParametersBaseModel):
max_power_wh: float = Field(gt=0)
class Inverter(DeviceBase):

View File

@@ -6,7 +6,7 @@ from typing import Any, Optional
import numpy as np
from deap import algorithms, base, creator, tools
from pydantic import BaseModel, Field, field_validator, model_validator
from pydantic import Field, field_validator, model_validator
from typing_extensions import Self
from akkudoktoreos.core.coreabc import (
@@ -16,6 +16,7 @@ from akkudoktoreos.core.coreabc import (
)
from akkudoktoreos.core.ems import EnergieManagementSystemParameters, SimulationResult
from akkudoktoreos.core.logging import get_logger
from akkudoktoreos.core.pydantic import ParametersBaseModel
from akkudoktoreos.devices.battery import (
Battery,
ElectricVehicleParameters,
@@ -30,10 +31,10 @@ from akkudoktoreos.utils.utils import NumpyEncoder
logger = get_logger(__name__)
class OptimizationParameters(BaseModel):
class OptimizationParameters(ParametersBaseModel):
ems: EnergieManagementSystemParameters
pv_akku: SolarPanelBatteryParameters
inverter: InverterParameters = InverterParameters()
pv_akku: Optional[SolarPanelBatteryParameters]
inverter: Optional[InverterParameters]
eauto: Optional[ElectricVehicleParameters]
dishwasher: Optional[HomeApplianceParameters] = None
temperature_forecast: Optional[list[Optional[float]]] = Field(
@@ -60,7 +61,7 @@ class OptimizationParameters(BaseModel):
return start_solution
class OptimizeResponse(BaseModel):
class OptimizeResponse(ParametersBaseModel):
"""**Note**: The first value of "Last_Wh_per_hour", "Netzeinspeisung_Wh_per_hour", and "Netzbezug_Wh_per_hour", will be set to null in the JSON output and represented as NaN or None in the corresponding classes' data returns. This approach is adopted to ensure that the current hour's processing remains unchanged."""
ac_charge: list[float] = Field(
@@ -565,11 +566,13 @@ class optimization_problem(ConfigMixin, DevicesMixin, EnergyManagementSystemMixi
)
# Initialize PV and EV batteries
akku = Battery(
parameters.pv_akku,
hours=self.config.prediction_hours,
)
akku.set_charge_per_hour(np.full(self.config.prediction_hours, 1))
akku: Optional[Battery] = None
if parameters.pv_akku:
akku = Battery(
parameters.pv_akku,
hours=self.config.prediction_hours,
)
akku.set_charge_per_hour(np.full(self.config.prediction_hours, 1))
eauto: Optional[Battery] = None
if parameters.eauto:
@@ -595,11 +598,13 @@ class optimization_problem(ConfigMixin, DevicesMixin, EnergyManagementSystemMixi
)
# Initialize the inverter and energy management system
inverter = Inverter(
sc,
parameters.inverter,
akku,
)
inverter: Optional[Inverter] = None
if parameters.inverter:
inverter = Inverter(
sc,
parameters.inverter,
akku,
)
self.ems.set_parameters(
parameters.ems,
inverter=inverter,