mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 00:45:22 +00:00
Tests for class_load_container v2 (#247)
* Tests for class_load_container v2 - file as same name as class - useless numpy conversations removed - switched to built-in type tests - human readable tests included * ruff * removed dupicate empty list * load_aggregator: collections.abc.Sequence --------- Co-authored-by: Dominique Lasserre <lasserre.d@gmail.com>
This commit is contained in:
parent
31bd2de18b
commit
0e122a9a49
37
src/akkudoktoreos/prediction/load_aggregator.py
Normal file
37
src/akkudoktoreos/prediction/load_aggregator.py
Normal file
@ -0,0 +1,37 @@
|
||||
from collections import defaultdict
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
class LoadAggregator:
|
||||
def __init__(self, prediction_hours: int = 24) -> None:
|
||||
"""Initializes the LoadAggregator object with the number of prediction hours.
|
||||
|
||||
:param prediction_hours: Number of hours to predict (default: 24)
|
||||
"""
|
||||
self.loads: defaultdict[str, list[float]] = defaultdict(
|
||||
list
|
||||
) # Dictionary to hold load arrays for different sources
|
||||
self.prediction_hours: int = prediction_hours
|
||||
|
||||
def add_load(self, name: str, last_array: Sequence[float]) -> None:
|
||||
"""Adds a load array for a specific source. Accepts a Sequence of floats.
|
||||
|
||||
:param name: Name of the load source (e.g., "Household", "Heat Pump").
|
||||
:param last_array: Sequence of loads, where each entry corresponds to an hour.
|
||||
:raises ValueError: If the length of last_array doesn't match the prediction hours.
|
||||
"""
|
||||
# Check length of the array without converting
|
||||
if len(last_array) != self.prediction_hours:
|
||||
raise ValueError(f"Total load inconsistent lengths in arrays: {name} {len(last_array)}")
|
||||
self.loads[name] = list(last_array)
|
||||
|
||||
def calculate_total_load(self) -> list[float]:
|
||||
"""Calculates the total load for each hour by summing up the loads from all sources.
|
||||
|
||||
:return: A list representing the total load for each hour.
|
||||
Returns an empty list if no loads have been added.
|
||||
"""
|
||||
# Optimize the summation using a single loop with zip
|
||||
total_load = [sum(hourly_loads) for hourly_loads in zip(*self.loads.values())]
|
||||
|
||||
return total_load
|
@ -1,39 +0,0 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
class Gesamtlast:
|
||||
def __init__(self, prediction_hours: int = 24):
|
||||
self.lasten: dict[
|
||||
str, np.ndarray
|
||||
] = {} # Contains names and load arrays for different sources
|
||||
self.prediction_hours = prediction_hours
|
||||
|
||||
def hinzufuegen(self, name: str, last_array: np.ndarray) -> None:
|
||||
"""Adds an array of loads for a specific source.
|
||||
|
||||
:param name: Name of the load source (e.g., "Household", "Heat Pump")
|
||||
:param last_array: Array of loads, where each entry corresponds to an hour
|
||||
"""
|
||||
if len(last_array) != self.prediction_hours:
|
||||
raise ValueError(f"Total load inconsistent lengths in arrays: {name} {len(last_array)}")
|
||||
self.lasten[name] = last_array
|
||||
|
||||
def gesamtlast_berechnen(self) -> np.ndarray:
|
||||
"""Calculates the total load for each hour and returns an array of total loads.
|
||||
|
||||
:return: Array of total loads, where each entry corresponds to an hour
|
||||
"""
|
||||
if not self.lasten:
|
||||
return np.ndarray(0)
|
||||
|
||||
# Assumption: All load arrays have the same length
|
||||
stunden = len(next(iter(self.lasten.values())))
|
||||
gesamtlast_array = [0] * stunden
|
||||
|
||||
for last_array in self.lasten.values():
|
||||
gesamtlast_array = [
|
||||
gesamtlast + stundenlast
|
||||
for gesamtlast, stundenlast in zip(gesamtlast_array, last_array)
|
||||
]
|
||||
|
||||
return np.array(gesamtlast_array)
|
@ -23,7 +23,7 @@ from akkudoktoreos.optimization.genetic import (
|
||||
)
|
||||
|
||||
# Still to be adapted
|
||||
from akkudoktoreos.prediction.load_container import Gesamtlast
|
||||
from akkudoktoreos.prediction.load_aggregator import LoadAggregator
|
||||
from akkudoktoreos.prediction.load_corrector import LoadPredictionAdjuster
|
||||
from akkudoktoreos.prediction.load_forecast import LoadForecast
|
||||
from akkudoktoreos.prediction.prediction import get_prediction
|
||||
@ -160,14 +160,13 @@ def fastapi_gesamtlast(request: GesamtlastRequest) -> list[float]:
|
||||
future_predictions = adjuster.predict_next_hours(hours)
|
||||
|
||||
leistung_haushalt = future_predictions["Adjusted Pred"].to_numpy()
|
||||
gesamtlast = Gesamtlast(prediction_hours=hours)
|
||||
gesamtlast.hinzufuegen(
|
||||
gesamtlast = LoadAggregator(prediction_hours=hours)
|
||||
gesamtlast.add_load(
|
||||
"Haushalt",
|
||||
leistung_haushalt,
|
||||
tuple(leistung_haushalt),
|
||||
)
|
||||
|
||||
last = gesamtlast.gesamtlast_berechnen()
|
||||
return last.tolist()
|
||||
return gesamtlast.calculate_total_load()
|
||||
|
||||
|
||||
@app.get("/gesamtlast_simple")
|
||||
@ -183,8 +182,10 @@ def fastapi_gesamtlast_simple(year_energy: float) -> list[float]:
|
||||
)[0] # Get expected household load for the date range
|
||||
|
||||
prediction_hours = config_eos.prediction_hours if config_eos.prediction_hours else 48
|
||||
gesamtlast = Gesamtlast(prediction_hours=prediction_hours) # Create Gesamtlast instance
|
||||
gesamtlast.hinzufuegen("Haushalt", leistung_haushalt) # Add household to total load calculation
|
||||
gesamtlast = LoadAggregator(prediction_hours=prediction_hours) # Create Gesamtlast instance
|
||||
gesamtlast.add_load(
|
||||
"Haushalt", tuple(leistung_haushalt)
|
||||
) # Add household to total load calculation
|
||||
|
||||
# ###############
|
||||
# # WP (Heat Pump)
|
||||
@ -192,8 +193,7 @@ def fastapi_gesamtlast_simple(year_energy: float) -> list[float]:
|
||||
# leistung_wp = wp.simulate_24h(temperature_forecast) # Simulate heat pump load for 24 hours
|
||||
# gesamtlast.hinzufuegen("Heatpump", leistung_wp) # Add heat pump load to total load calculation
|
||||
|
||||
last = gesamtlast.gesamtlast_berechnen() # Calculate total load
|
||||
return last.tolist() # Return total load as JSON
|
||||
return gesamtlast.calculate_total_load()
|
||||
|
||||
|
||||
class ForecastResponse(PydanticBaseModel):
|
||||
|
39
tests/test_load_aggregator.py
Normal file
39
tests/test_load_aggregator.py
Normal file
@ -0,0 +1,39 @@
|
||||
import pytest
|
||||
|
||||
from akkudoktoreos.prediction.load_aggregator import LoadAggregator
|
||||
|
||||
|
||||
def test_initialization():
|
||||
aggregator = LoadAggregator()
|
||||
assert aggregator.prediction_hours == 24
|
||||
assert aggregator.loads == {}
|
||||
|
||||
|
||||
def test_add_load_valid():
|
||||
aggregator = LoadAggregator(prediction_hours=3)
|
||||
aggregator.add_load("Source1", [10.0, 20.0, 30.0])
|
||||
assert aggregator.loads["Source1"] == [10.0, 20.0, 30.0]
|
||||
|
||||
|
||||
def test_add_load_invalid_length():
|
||||
aggregator = LoadAggregator(prediction_hours=3)
|
||||
with pytest.raises(ValueError, match="Total load inconsistent lengths in arrays: Source1 2"):
|
||||
aggregator.add_load("Source1", [10.0, 20.0])
|
||||
|
||||
|
||||
def test_calculate_total_load_empty():
|
||||
aggregator = LoadAggregator()
|
||||
assert aggregator.calculate_total_load() == []
|
||||
|
||||
|
||||
def test_calculate_total_load():
|
||||
aggregator = LoadAggregator(prediction_hours=3)
|
||||
aggregator.add_load("Source1", [10.0, 20.0, 30.0])
|
||||
aggregator.add_load("Source2", [5.0, 15.0, 25.0])
|
||||
assert aggregator.calculate_total_load() == [15.0, 35.0, 55.0]
|
||||
|
||||
|
||||
def test_calculate_total_load_single_source():
|
||||
aggregator = LoadAggregator(prediction_hours=3)
|
||||
aggregator.add_load("Source1", [10.0, 20.0, 30.0])
|
||||
assert aggregator.calculate_total_load() == [10.0, 20.0, 30.0]
|
Loading…
x
Reference in New Issue
Block a user