2026-08-07 13:13:17 +02:00
|
|
|
from enum import StrEnum
|
2026-07-29 12:56:08 +02:00
|
|
|
from typing import Optional
|
2024-12-15 14:40:03 +01:00
|
|
|
|
2026-04-15 08:48:56 +02:00
|
|
|
from pydantic import Field, computed_field
|
2024-12-15 14:40:03 +01:00
|
|
|
|
|
|
|
|
from akkudoktoreos.config.configabc import SettingsBaseModel
|
2026-02-22 14:12:42 +01:00
|
|
|
from akkudoktoreos.core.coreabc import get_ems
|
2025-10-30 13:26:17 +01:00
|
|
|
from akkudoktoreos.core.pydantic import (
|
|
|
|
|
PydanticBaseModel,
|
|
|
|
|
PydanticDateTimeDataFrame,
|
|
|
|
|
)
|
2026-07-29 12:56:08 +02:00
|
|
|
from akkudoktoreos.optimization.genetic0.genetic0settings import Genetic0CommonSettings
|
|
|
|
|
from akkudoktoreos.optimization.genetic.geneticsettings import GeneticCommonSettings
|
2025-10-28 02:50:31 +01:00
|
|
|
from akkudoktoreos.utils.datetimeutil import DateTime
|
|
|
|
|
|
|
|
|
|
|
2026-08-07 13:13:17 +02:00
|
|
|
class OptimizationAlgorithm(StrEnum):
|
|
|
|
|
"""Optimization Algorithm."""
|
|
|
|
|
|
|
|
|
|
GENETIC = "GENETIC"
|
|
|
|
|
GENETIC0 = "GENETIC0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def optimization_default_algorithm() -> OptimizationAlgorithm:
|
|
|
|
|
"""Provide default optimization algorithm."""
|
|
|
|
|
return OptimizationAlgorithm.GENETIC
|
2024-12-15 14:40:03 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class OptimizationCommonSettings(SettingsBaseModel):
|
2025-10-28 02:50:31 +01:00
|
|
|
"""General Optimization Configuration."""
|
2024-12-15 14:40:03 +01:00
|
|
|
|
2026-08-07 13:13:17 +02:00
|
|
|
algorithm: OptimizationAlgorithm = Field(
|
|
|
|
|
default_factory=optimization_default_algorithm,
|
2026-04-15 08:48:56 +02:00
|
|
|
json_schema_extra={
|
2026-08-07 13:13:17 +02:00
|
|
|
"description": (
|
|
|
|
|
f"Optimization algorithm "
|
|
|
|
|
f"[{' | '.join(mode.value for mode in OptimizationAlgorithm)}]. "
|
|
|
|
|
f"Defaults to {optimization_default_algorithm()}."
|
|
|
|
|
),
|
2026-07-29 12:56:08 +02:00
|
|
|
"examples": ["GENETIC", "GENETIC0"],
|
2026-04-15 08:48:56 +02:00
|
|
|
},
|
2025-10-30 13:26:17 +01:00
|
|
|
)
|
|
|
|
|
|
2026-04-15 08:48:56 +02:00
|
|
|
genetic: GeneticCommonSettings = Field(
|
|
|
|
|
default_factory=GeneticCommonSettings,
|
2025-11-10 16:57:44 +01:00
|
|
|
json_schema_extra={
|
2026-07-29 12:56:08 +02:00
|
|
|
"description": "GENETIC optimization algorithm configuration.",
|
|
|
|
|
"examples": [{"individuals": 400, "seed": None, "penalties": {"ev_soc_miss": 10}}],
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
genetic0: Genetic0CommonSettings = Field(
|
|
|
|
|
default_factory=Genetic0CommonSettings,
|
|
|
|
|
json_schema_extra={
|
|
|
|
|
"description": "GENETIC0 optimization algorithm configuration.",
|
2025-11-10 16:57:44 +01:00
|
|
|
"examples": [{"individuals": 400, "seed": None, "penalties": {"ev_soc_miss": 10}}],
|
|
|
|
|
},
|
2025-10-28 02:50:31 +01:00
|
|
|
)
|
|
|
|
|
|
2025-12-30 22:08:21 +01:00
|
|
|
# Computed fields
|
2026-07-29 12:56:08 +02:00
|
|
|
@computed_field # type: ignore[prop-decorator]
|
|
|
|
|
@property
|
|
|
|
|
def algorithms(self) -> list[str]:
|
|
|
|
|
"""Available optimization algorithms."""
|
2026-08-07 13:13:17 +02:00
|
|
|
return [algo.value for algo in OptimizationAlgorithm]
|
2026-07-29 12:56:08 +02:00
|
|
|
|
2025-12-30 22:08:21 +01:00
|
|
|
@computed_field # type: ignore[prop-decorator]
|
|
|
|
|
@property
|
|
|
|
|
def keys(self) -> list[str]:
|
|
|
|
|
"""The keys of the solution."""
|
2026-02-22 14:12:42 +01:00
|
|
|
try:
|
|
|
|
|
ems_eos = get_ems()
|
2026-07-20 01:11:33 +02:00
|
|
|
except Exception:
|
2026-02-22 14:12:42 +01:00
|
|
|
# ems might not be initialized
|
|
|
|
|
return []
|
2025-12-30 22:08:21 +01:00
|
|
|
|
|
|
|
|
key_list = []
|
2026-02-22 14:12:42 +01:00
|
|
|
optimization_solution = ems_eos.optimization_solution()
|
2025-12-30 22:08:21 +01:00
|
|
|
if optimization_solution:
|
|
|
|
|
# Prepare mapping
|
|
|
|
|
df = optimization_solution.solution.to_dataframe()
|
|
|
|
|
key_list = df.columns.tolist()
|
|
|
|
|
return sorted(set(key_list))
|
|
|
|
|
|
2025-10-28 02:50:31 +01:00
|
|
|
|
|
|
|
|
class OptimizationSolution(PydanticBaseModel):
|
|
|
|
|
"""General Optimization Solution."""
|
|
|
|
|
|
2025-11-10 16:57:44 +01:00
|
|
|
id: str = Field(
|
|
|
|
|
..., json_schema_extra={"description": "Unique ID for the optimization solution."}
|
|
|
|
|
)
|
2025-10-28 02:50:31 +01:00
|
|
|
|
2025-11-10 16:57:44 +01:00
|
|
|
generated_at: DateTime = Field(
|
|
|
|
|
..., json_schema_extra={"description": "Timestamp when the solution was generated."}
|
|
|
|
|
)
|
2025-10-28 02:50:31 +01:00
|
|
|
|
|
|
|
|
comment: Optional[str] = Field(
|
2025-11-10 16:57:44 +01:00
|
|
|
default=None,
|
|
|
|
|
json_schema_extra={"description": "Optional comment or annotation for the solution."},
|
2025-10-28 02:50:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
valid_from: Optional[DateTime] = Field(
|
2025-11-10 16:57:44 +01:00
|
|
|
default=None, json_schema_extra={"description": "Start time of the optimization solution."}
|
2025-10-28 02:50:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
valid_until: Optional[DateTime] = Field(
|
2025-11-10 16:57:44 +01:00
|
|
|
default=None, json_schema_extra={"description": "End time of the optimization solution."}
|
2025-10-28 02:50:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
total_losses_energy_wh: float = Field(
|
2025-11-10 16:57:44 +01:00
|
|
|
json_schema_extra={"description": "The total losses in watt-hours over the entire period."}
|
2025-10-28 02:50:31 +01:00
|
|
|
)
|
|
|
|
|
|
2025-11-10 16:57:44 +01:00
|
|
|
total_revenues_amt: float = Field(
|
|
|
|
|
json_schema_extra={"description": "The total revenues [money amount]."}
|
|
|
|
|
)
|
2025-10-28 02:50:31 +01:00
|
|
|
|
2025-11-10 16:57:44 +01:00
|
|
|
total_costs_amt: float = Field(
|
|
|
|
|
json_schema_extra={"description": "The total costs [money amount]."}
|
|
|
|
|
)
|
2025-10-28 02:50:31 +01:00
|
|
|
|
2025-11-10 16:57:44 +01:00
|
|
|
fitness_score: set[float] = Field(
|
|
|
|
|
json_schema_extra={"description": "The fitness score as a set of fitness values."}
|
|
|
|
|
)
|
2025-11-08 15:42:18 +01:00
|
|
|
|
2025-11-01 00:49:11 +01:00
|
|
|
prediction: PydanticDateTimeDataFrame = Field(
|
2025-11-10 16:57:44 +01:00
|
|
|
json_schema_extra={
|
|
|
|
|
"description": (
|
|
|
|
|
"Datetime data frame with time series prediction data per optimization interval:"
|
|
|
|
|
"- pv_energy_wh: PV energy prediction (positive) in wh"
|
|
|
|
|
"- elec_price_amt_kwh: Electricity price prediction in money per kwh"
|
|
|
|
|
"- feed_in_tariff_amt_kwh: Feed in tariff prediction in money per kwh"
|
|
|
|
|
"- weather_temp_air_celcius: Temperature in °C"
|
|
|
|
|
"- loadforecast_energy_wh: Load mean energy prediction in wh"
|
|
|
|
|
"- loadakkudoktor_std_energy_wh: Load energy standard deviation prediction in wh"
|
|
|
|
|
"- loadakkudoktor_mean_energy_wh: Load mean energy prediction in wh"
|
|
|
|
|
)
|
|
|
|
|
}
|
2025-11-01 00:49:11 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
solution: PydanticDateTimeDataFrame = Field(
|
2025-11-10 16:57:44 +01:00
|
|
|
json_schema_extra={
|
|
|
|
|
"description": (
|
|
|
|
|
"Datetime data frame with time series solution data per optimization interval:"
|
|
|
|
|
"- load_energy_wh: Load of all energy consumers in wh"
|
|
|
|
|
"- grid_energy_wh: Grid energy feed in (negative) or consumption (positive) in wh"
|
|
|
|
|
"- costs_amt: Costs in money amount"
|
|
|
|
|
"- revenue_amt: Revenue in money amount"
|
|
|
|
|
"- losses_energy_wh: Energy losses in wh"
|
|
|
|
|
"- <device-id>_operation_mode_id: Operation mode id of the device."
|
|
|
|
|
"- <device-id>_operation_mode_factor: Operation mode factor of the device."
|
|
|
|
|
"- <device-id>_soc_factor: State of charge of a battery/ electric vehicle device as factor of total capacity."
|
|
|
|
|
"- <device-id>_energy_wh: Energy consumption (positive) of a device in wh."
|
|
|
|
|
)
|
|
|
|
|
}
|
2024-12-15 14:40:03 +01:00
|
|
|
)
|