mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-01-08 19:56:18 +00:00
fix: load prediction adjustment with measurement in kwh (#826)
Use load energy meter reading in kWh for load prediction adjustment. Before the reading was falsely regarded to be in Wh. Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
This commit is contained in:
@@ -136,7 +136,7 @@
|
||||
}
|
||||
},
|
||||
"general": {
|
||||
"version": "0.2.0.dev70048701",
|
||||
"version": "0.2.0.dev81043823",
|
||||
"data_folder_path": null,
|
||||
"data_output_subpath": "output",
|
||||
"latitude": 52.52,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
| latitude | `EOS_GENERAL__LATITUDE` | `Optional[float]` | `rw` | `52.52` | Latitude in decimal degrees between -90 and 90. North is positive (ISO 19115) (°) |
|
||||
| longitude | `EOS_GENERAL__LONGITUDE` | `Optional[float]` | `rw` | `13.405` | Longitude in decimal degrees within -180 to 180 (°) |
|
||||
| timezone | | `Optional[str]` | `ro` | `N/A` | Computed timezone based on latitude and longitude. |
|
||||
| version | `EOS_GENERAL__VERSION` | `str` | `rw` | `0.2.0.dev70048701` | Configuration file version. Used to check compatibility. |
|
||||
| version | `EOS_GENERAL__VERSION` | `str` | `rw` | `0.2.0.dev81043823` | Configuration file version. Used to check compatibility. |
|
||||
:::
|
||||
<!-- pyml enable line-length -->
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"version": "0.2.0.dev70048701",
|
||||
"version": "0.2.0.dev81043823",
|
||||
"data_folder_path": null,
|
||||
"data_output_subpath": "output",
|
||||
"latitude": 52.52,
|
||||
@@ -46,7 +46,7 @@
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"version": "0.2.0.dev70048701",
|
||||
"version": "0.2.0.dev81043823",
|
||||
"data_folder_path": null,
|
||||
"data_output_subpath": "output",
|
||||
"latitude": 52.52,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Akkudoktor-EOS
|
||||
|
||||
**Version**: `v0.2.0.dev70048701`
|
||||
**Version**: `v0.2.0.dev81043823`
|
||||
|
||||
<!-- pyml disable line-length -->
|
||||
**Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"info": {
|
||||
"title": "Akkudoktor-EOS",
|
||||
"description": "This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period.",
|
||||
"version": "v0.2.0.dev70048701"
|
||||
"version": "v0.2.0.dev81043823"
|
||||
},
|
||||
"paths": {
|
||||
"/v1/admin/cache/clear": {
|
||||
@@ -2525,7 +2525,7 @@
|
||||
"general": {
|
||||
"$ref": "#/components/schemas/GeneralSettings-Output",
|
||||
"default": {
|
||||
"version": "0.2.0.dev70048701",
|
||||
"version": "0.2.0.dev81043823",
|
||||
"data_output_subpath": "output",
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
@@ -4272,7 +4272,7 @@
|
||||
"type": "string",
|
||||
"title": "Version",
|
||||
"description": "Configuration file version. Used to check compatibility.",
|
||||
"default": "0.2.0.dev70048701"
|
||||
"default": "0.2.0.dev81043823"
|
||||
},
|
||||
"data_folder_path": {
|
||||
"anyOf": [
|
||||
@@ -4346,7 +4346,7 @@
|
||||
"type": "string",
|
||||
"title": "Version",
|
||||
"description": "Configuration file version. Used to check compatibility.",
|
||||
"default": "0.2.0.dev70048701"
|
||||
"default": "0.2.0.dev81043823"
|
||||
},
|
||||
"data_folder_path": {
|
||||
"anyOf": [
|
||||
|
||||
@@ -176,7 +176,7 @@ class Measurement(SingletonMixin, DataImportMixin, DataSequence):
|
||||
logger.debug(debug_msg)
|
||||
return energy_array
|
||||
|
||||
def load_total(
|
||||
def load_total_kwh(
|
||||
self,
|
||||
start_datetime: Optional[DateTime] = None,
|
||||
end_datetime: Optional[DateTime] = None,
|
||||
@@ -207,7 +207,7 @@ class Measurement(SingletonMixin, DataImportMixin, DataSequence):
|
||||
if end_datetime is None:
|
||||
end_datetime = self[-1].date_time
|
||||
size = self._interval_count(start_datetime, end_datetime, interval)
|
||||
load_total_array = np.zeros(size)
|
||||
load_total_kwh_array = np.zeros(size)
|
||||
# Loop through all loads
|
||||
if isinstance(self.config.measurement.load_emr_keys, list):
|
||||
for key in self.config.measurement.load_emr_keys:
|
||||
@@ -219,11 +219,11 @@ class Measurement(SingletonMixin, DataImportMixin, DataSequence):
|
||||
interval=interval,
|
||||
)
|
||||
# Add calculated load to total load
|
||||
load_total_array += load_array
|
||||
debug_msg = f"Total load '{key}' calculation: {load_total_array}"
|
||||
load_total_kwh_array += load_array
|
||||
debug_msg = f"Total load '{key}' calculation: {load_total_kwh_array}"
|
||||
logger.debug(debug_msg)
|
||||
|
||||
return load_total_array
|
||||
return load_total_kwh_array
|
||||
|
||||
|
||||
def get_measurement() -> Measurement:
|
||||
|
||||
@@ -124,23 +124,23 @@ class LoadAkkudoktorAdjusted(LoadAkkudoktor):
|
||||
compare_end = self.measurement.max_datetime
|
||||
compare_interval = to_duration("1 hour")
|
||||
|
||||
load_total_array = self.measurement.load_total(
|
||||
load_total_kwh_array = self.measurement.load_total_kwh(
|
||||
start_datetime=compare_start,
|
||||
end_datetime=compare_end,
|
||||
interval=compare_interval,
|
||||
)
|
||||
compare_dt = compare_start
|
||||
for i in range(len(load_total_array)):
|
||||
load_total = load_total_array[i]
|
||||
for i in range(len(load_total_kwh_array)):
|
||||
load_total_wh = load_total_kwh_array[i] * 1000
|
||||
# Extract mean (index 0) and standard deviation (index 1) for the given day and hour
|
||||
# Day indexing starts at 0, -1 because of that
|
||||
hourly_stats = data_year_energy[compare_dt.day_of_year - 1, :, compare_dt.hour]
|
||||
weight = 1 / ((compare_end - compare_dt).days + 1)
|
||||
if compare_dt.day_of_week < 5:
|
||||
weekday_adjust[compare_dt.hour] += (load_total - hourly_stats[0]) * weight
|
||||
weekday_adjust[compare_dt.hour] += (load_total_wh - hourly_stats[0]) * weight
|
||||
weekday_adjust_weight[compare_dt.hour] += weight
|
||||
else:
|
||||
weekend_adjust[compare_dt.hour] += (load_total - hourly_stats[0]) * weight
|
||||
weekend_adjust[compare_dt.hour] += (load_total_wh - hourly_stats[0]) * weight
|
||||
weekend_adjust_weight[compare_dt.hour] += weight
|
||||
compare_dt += compare_interval
|
||||
# Calculate mean
|
||||
|
||||
@@ -56,9 +56,10 @@ def loadakkudoktoradjusted(config_eos):
|
||||
@pytest.fixture
|
||||
def measurement_eos():
|
||||
"""Fixture to initialise the Measurement instance."""
|
||||
# Load meter readings are in kWh
|
||||
measurement = get_measurement()
|
||||
load0_mr = 500
|
||||
load1_mr = 500
|
||||
load0_mr = 500.0
|
||||
load1_mr = 500.0
|
||||
dt = to_datetime("2024-01-01T00:00:00")
|
||||
interval = to_duration("1 hour")
|
||||
for i in range(25):
|
||||
@@ -70,8 +71,9 @@ def measurement_eos():
|
||||
)
|
||||
)
|
||||
dt += interval
|
||||
load0_mr += 50
|
||||
load1_mr += 50
|
||||
# 0.05 kWh = 50 Wh
|
||||
load0_mr += 0.05
|
||||
load1_mr += 0.05
|
||||
assert compare_datetimes(measurement.min_datetime, to_datetime("2024-01-01T00:00:00")).equal
|
||||
assert compare_datetimes(measurement.max_datetime, to_datetime("2024-01-02T00:00:00")).equal
|
||||
return measurement
|
||||
@@ -187,7 +189,7 @@ def test_calculate_adjustment(loadakkudoktoradjusted, measurement_eos):
|
||||
100.0,
|
||||
]
|
||||
)
|
||||
np.testing.assert_array_equal(weekday_adjust, expected)
|
||||
np.testing.assert_allclose(weekday_adjust, expected)
|
||||
|
||||
assert weekend_adjust.shape == (24,)
|
||||
expected = np.array(
|
||||
|
||||
@@ -217,6 +217,7 @@ class TestMeasurement:
|
||||
@pytest.fixture
|
||||
def measurement_eos(self, config_eos):
|
||||
"""Fixture to create a Measurement instance."""
|
||||
# Load meter readings are in kWh
|
||||
config_eos.measurement.load_emr_keys = ["load0_mr", "load1_mr", "load2_mr", "load3_mr"]
|
||||
measurement = get_measurement()
|
||||
record0 = MeasurementDataRecord(
|
||||
@@ -365,35 +366,35 @@ class TestMeasurement:
|
||||
with pytest.raises(ValueError, match="interval must be positive"):
|
||||
measurement_eos._energy_from_meter_readings(key, start_datetime, end_datetime, interval)
|
||||
|
||||
def test_load_total(self, measurement_eos):
|
||||
def test_load_total_kwh(self, measurement_eos):
|
||||
"""Test total load calculation."""
|
||||
start = datetime(2023, 1, 1, 0)
|
||||
end = datetime(2023, 1, 1, 2)
|
||||
interval = duration(hours=1)
|
||||
|
||||
result = measurement_eos.load_total(start_datetime=start, end_datetime=end, interval=interval)
|
||||
result = measurement_eos.load_total_kwh(start_datetime=start, end_datetime=end, interval=interval)
|
||||
|
||||
# Expected total load per interval
|
||||
expected = np.array([100, 100]) # Differences between consecutive meter readings
|
||||
np.testing.assert_array_equal(result, expected)
|
||||
|
||||
def test_load_total_no_data(self, measurement_eos):
|
||||
def test_load_total_kwh_no_data(self, measurement_eos):
|
||||
"""Test total load calculation with no data."""
|
||||
measurement_eos.records = []
|
||||
start = datetime(2023, 1, 1, 0)
|
||||
end = datetime(2023, 1, 1, 3)
|
||||
interval = duration(hours=1)
|
||||
|
||||
result = measurement_eos.load_total(start_datetime=start, end_datetime=end, interval=interval)
|
||||
result = measurement_eos.load_total_kwh(start_datetime=start, end_datetime=end, interval=interval)
|
||||
expected = np.zeros(3) # No data, so all intervals are zero
|
||||
np.testing.assert_array_equal(result, expected)
|
||||
|
||||
def test_load_total_partial_intervals(self, measurement_eos):
|
||||
def test_load_total_kwh_partial_intervals(self, measurement_eos):
|
||||
"""Test total load calculation with partial intervals."""
|
||||
start = datetime(2023, 1, 1, 0, 30) # Start in the middle of an interval
|
||||
end = datetime(2023, 1, 1, 1, 30) # End in the middle of another interval
|
||||
interval = duration(hours=1)
|
||||
|
||||
result = measurement_eos.load_total(start_datetime=start, end_datetime=end, interval=interval)
|
||||
result = measurement_eos.load_total_kwh(start_datetime=start, end_datetime=end, interval=interval)
|
||||
expected = np.array([100]) # Only one complete interval covered
|
||||
np.testing.assert_array_equal(result, expected)
|
||||
|
||||
Reference in New Issue
Block a user