Skip to content

Commit 5f4446a

Browse files
authored
consider max ac out for all inverters (#2429)
1 parent be6a483 commit 5f4446a

4 files changed

Lines changed: 50 additions & 94 deletions

File tree

packages/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from control.counter import Set as CounterSet
1515
from control.counter_all import CounterAll
1616
from control.pv import Pv, PvData
17+
from control.pv import Config as PvConfig
1718
from control.pv import Get as PvGet
1819
from helpermodules import hardware_configuration, pub, timecheck
1920
from modules.chargepoints.mqtt.chargepoint_module import ChargepointModule
@@ -155,7 +156,7 @@ def data_() -> None:
155156
set=Mock(spec=BatSet, power_limit=None)))})
156157
data.data.pv_data.update({"pv1": Mock(spec=Pv, data=Mock(
157158
spec=PvData, get=Mock(spec=PvGet, power=-10000, daily_exported=6000, exported=27000, currents=None,
158-
fault_state=0)))})
159+
fault_state=0), config=Mock(spec=PvConfig, max_ac_out=10000)))})
159160
data.data.counter_data.update({
160161
"counter0": Mock(spec=Counter, data=Mock(spec=CounterData, get=Mock(
161162
spec=CounterGet, currents=[40]*3, power=6200, daily_imported=45000, daily_exported=3000, fault_state=0))),

packages/control/bat_all.py

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@
2525
from control import data
2626
from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT
2727
from control.algorithm.filter_chargepoints import get_chargepoints_by_chargemodes
28-
from control.bat import Bat
28+
from control.pv import Pv
2929
from helpermodules.constants import NO_ERROR
3030
from modules.common.abstract_device import AbstractDevice
31-
from modules.common.fault_state import FaultStateLevel
3231

3332
log = logging.getLogger(__name__)
3433

@@ -144,48 +143,32 @@ def calc_power_for_all_components(self):
144143
except Exception:
145144
log.exception("Fehler im Bat-Modul")
146145

147-
def _max_bat_power_hybrid_system(self, battery: Bat) -> float:
146+
def _inverter_limited_power(self, inverter: Pv) -> float:
148147
"""gibt die maximale Entladeleistung des Speichers zurück, bis die maximale Ausgangsleistung des WR erreicht
149148
ist."""
150149
# tested
151-
parent = data.data.counter_all_data.get_entry_of_parent(battery.num)
152-
if parent.get("type") == "inverter":
153-
parent_data = data.data.pv_data[f"pv{parent['id']}"].data
154-
# Wenn vom PV-Ertrag der Speicher geladen wird, kann diese Leistung bis zur max Ausgangsleistung des WR
155-
# genutzt werden.
156-
if parent_data.config.max_ac_out > 0:
157-
max_bat_discharge_power = parent_data.config.max_ac_out + \
158-
parent_data.get.power + battery.data.get.power
159-
return max_bat_discharge_power, True
160-
else:
161-
battery.data.get.fault_state = FaultStateLevel.ERROR.value
162-
battery.data.get.fault_str = self.ERROR_CONFIG_MAX_AC_OUT
163-
raise ValueError(self.ERROR_CONFIG_MAX_AC_OUT)
150+
# Wenn vom PV-Ertrag der Speicher geladen wird, kann diese Leistung bis zur max Ausgangsleistung des WR
151+
# genutzt werden.
152+
if inverter.data.config.max_ac_out > 0:
153+
return max(inverter.data.get.power * -1 - inverter.data.config.max_ac_out, 0)
164154
else:
165-
# Kein Hybrid-WR
166-
# Maximal die Speicher-Leistung als Entladeleistung nutzen, um nicht unnötig Bezug zu erzeugen.
167-
return abs(battery.data.get.power) + 50, False
155+
return 0
168156

169157
def _limit_bat_power_discharge(self, required_power):
170158
"""begrenzt die für den Algorithmus benötigte Entladeleistung des Speichers, wenn die maximale Ausgangsleistung
171159
des WR erreicht ist."""
172-
available_power = 0
173-
hybrid = False
160+
inverter_limited_power = 0
174161
if required_power > 0:
175162
# Nur wenn der Speicher entladen werden soll, fließt Leistung durch den WR.
176-
for battery in data.data.bat_data.values():
163+
for inverter in data.data.pv_data.values():
177164
try:
178-
available_power_bat, hybrid_bat = self._max_bat_power_hybrid_system(battery)
179-
if hybrid_bat:
180-
hybrid = True
181-
available_power += available_power_bat
165+
inverter_limited_power += self._inverter_limited_power(inverter)
182166
except Exception:
183-
log.exception(f"Fehler im Bat-Modul {battery.num}")
184-
if hybrid:
185-
if required_power > available_power:
186-
log.debug(f"Verbleibende Speicher-Leistung durch maximale Ausgangsleistung auf {available_power}W"
187-
" begrenzt.")
188-
return min(required_power, available_power)
167+
log.exception(f"Fehler im Bat-Modul {inverter.num}")
168+
if inverter_limited_power > 0:
169+
required_power = max(required_power-inverter_limited_power, 0)
170+
log.debug(f"Verbleibende Speicher-Leistung durch maximale Ausgangsleistung auf {required_power}W"
171+
" begrenzt.")
189172
return required_power
190173

191174
def setup_bat(self):
@@ -332,10 +315,7 @@ def get_power_limit(self):
332315
if self.data.set.power_limit is None:
333316
power_limit = None
334317
else:
335-
power_limit = min(self._max_bat_power_hybrid_system(
336-
data.data.bat_data[f"bat{bat_component.component_config.id}"])[0], remaining_power_limit)
337-
remaining_power_limit -= power_limit
338-
remaining_power_limit = max(remaining_power_limit, 0)
318+
power_limit = self._limit_bat_power_discharge(remaining_power_limit)
339319

340320
data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set.power_limit = power_limit
341321

packages/control/bat_all_test.py

Lines changed: 30 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass, field
22
from typing import List, Optional
3-
from unittest.mock import Mock
3+
from unittest.mock import MagicMock, Mock
44
import pytest
55
from packages.conftest import hierarchy_standard
66
from control import bat_all
@@ -27,66 +27,40 @@ def data_fixture() -> None:
2727

2828

2929
@pytest.mark.parametrize(
30-
"parent, bat_power, pv_power, expected_power_hybrid",
30+
"max_ac_out, power, expected_result",
3131
[
32-
pytest.param({"id": 6, "type": "counter", "children": [
33-
{"id": 2, "type": "bat", "children": []}]}, 100, -6400, (150, False),
34-
id="kein Hybrid-System, Speicher wird geladen"),
35-
pytest.param({"id": 6, "type": "counter", "children": [
36-
{"id": 2, "type": "bat", "children": []}]}, -100, -6400, (150, False),
37-
id="kein Hybrid-System, Speicher wird entladen"),
38-
pytest.param({"id": 1, "type": "inverter", "children": []}, 1200, -6400, (2000, True),
39-
id="Speicher lädt mit 1200W, max 2000W zusätzliche Entladeleistung bis WR max"),
40-
pytest.param({"id": 1, "type": "inverter", "children": []}, 600, -6400, (1400, True),
41-
id="Speicher lädt mit 600W, max 1400W zusätzliche Entladeleistung bis WR max"),
42-
pytest.param({"id": 1, "type": "inverter", "children": []}, 0, -6400, (800, True),
43-
id="Speicher neutral, max 800W Entladeleistung bis WR max"),
44-
pytest.param({"id": 1, "type": "inverter", "children": []}, -600, -6400, (200, True),
45-
id="Speicher entlädt mit 600W, max 200W Entladeleistung bis WR max"),
46-
pytest.param({"id": 1, "type": "inverter", "children": []}, -800, -6400, (0, True),
47-
id="Speicher entlädt mit 800W, maximale Entladeleistung des WR erreicht"),
48-
pytest.param({"id": 1, "type": "inverter", "children": []}, 1200, -7200, (1200, True),
49-
id="Speicher lädt mit 1200W, max 1200W zusätzliche Entladeleistung bis WR max"),
50-
pytest.param({"id": 1, "type": "inverter", "children": []}, 600, -7200, (600, True),
51-
id="Speicher lädt mit 600W, max 600W zusätzliche Entladeleistung bis WR max"),
52-
pytest.param({"id": 1, "type": "inverter", "children": []}, 0, -7200, (0, True),
53-
id="Speicher neutral, maximale Entladeleistung des WR erreicht"),
54-
pytest.param({"id": 1, "type": "inverter", "children": []}, 1200, -7800, (600, True),
55-
id="Speicher lädt mit 1200W, max 600W zusätzliche Entladeleistung bis WR max"),
56-
pytest.param({"id": 1, "type": "inverter", "children": []}, 600, -7800, (0, True),
57-
id="Speicher lädt mit 600W, maximale Entladeleistung des WR erreicht"),
58-
])
59-
def test_max_bat_power_hybrid_system(parent, bat_power, pv_power, expected_power_hybrid, data_fixture, monkeypatch):
60-
# setup
61-
# pv1-Data: max_ac_out 7200
62-
mock_get_entry_of_parent = Mock(return_value=parent)
63-
monkeypatch.setattr(data.data.counter_all_data, "get_entry_of_parent", mock_get_entry_of_parent)
64-
data.data.pv_data["pv1"].data.get.power = pv_power
65-
66-
b = BatAll()
67-
bat2 = Bat(2)
68-
bat2.data.get.power = bat_power
69-
70-
# execution
71-
power = b._max_bat_power_hybrid_system(bat2)
72-
73-
# evaluation
74-
assert power == expected_power_hybrid
32+
pytest.param(5000, -6000, 1000, id="Leistung überschreitet max_ac_out"),
33+
pytest.param(5000, -4000, 0, id="Leistung liegt unter max_ac_out"),
34+
pytest.param(5000, 0, 0, id="Keine Leistung (power = 0)"),
35+
pytest.param(0, -6000, 0, id="max_ac_out ist 0"),
36+
],
37+
)
38+
def test_inverter_limited_power(max_ac_out, power, expected_result):
39+
# Mock für die Pv-Klasse
40+
inverter = Pv(1)
41+
inverter.data.config.max_ac_out = max_ac_out
42+
inverter.data.get.power = power
43+
bat_all = BatAll()
44+
45+
# Aufruf der zu testenden Funktion
46+
result = bat_all._inverter_limited_power(inverter)
47+
48+
# Überprüfung des Ergebnisses
49+
assert result == expected_result
7550

7651

7752
@pytest.mark.parametrize(
78-
"required_power, return_max_bat_power_hybrid_system, expected_power",
53+
"required_power, return_inverter_limited_power, expected_power",
7954
[
80-
pytest.param(1000, (1100, True), 1000, id="maximale Entladeleistung nicht erreicht"),
81-
pytest.param(1000, (900, True), 900, id="maximale Entladeleistung erreicht"),
82-
pytest.param(-1000, (10, True), -1000, id="Speicher soll nicht mehr entladen werden"),
83-
pytest.param(1000, (900, False), 1000, id="kein Hybrid-System"),
55+
pytest.param(1000, 0, 1000, id="maximale Entladeleistung nicht erreicht"),
56+
pytest.param(1000, 100, 900, id="maximale Entladeleistung erreicht"),
57+
pytest.param(-1000, 10, -1000, id="Speicher soll nicht mehr entladen werden"),
8458
])
85-
def test_limit_bat_power_discharge(required_power, return_max_bat_power_hybrid_system, expected_power, monkeypatch):
59+
def test_limit_bat_power_discharge(required_power, return_inverter_limited_power, expected_power, monkeypatch):
8660
# setup
87-
data.data.bat_data = {"bat2": Bat(2)}
88-
mock_max_bat_power_hybrid_system = Mock(return_value=return_max_bat_power_hybrid_system)
89-
monkeypatch.setattr(BatAll, "_max_bat_power_hybrid_system", mock_max_bat_power_hybrid_system)
61+
data.data.pv_data = {"pv2": Pv(2)}
62+
mock_inverter_limited_power = Mock(return_value=return_inverter_limited_power)
63+
monkeypatch.setattr(BatAll, "_inverter_limited_power", mock_inverter_limited_power)
9064

9165
b = BatAll()
9266

@@ -168,8 +142,8 @@ def test_get_charging_power_left(params: Params, caplog, data_fixture, monkeypat
168142
b.data.get.power = params.power
169143
data.data.bat_data["bat0"] = b
170144
data.data.general_data.data.chargemode_config.pv_charging = params.config
171-
mock__max_bat_power_hybrid_system = Mock(return_value=(params.power, None))
172-
monkeypatch.setattr(BatAll, "_max_bat_power_hybrid_system", mock__max_bat_power_hybrid_system)
145+
mock_limit_bat_power_discharge = MagicMock(side_effect=lambda x: x)
146+
monkeypatch.setattr(BatAll, "_limit_bat_power_discharge", mock_limit_bat_power_discharge)
173147

174148
# execution
175149
b_all._get_charging_power_left()

packages/helpermodules/measurement_logging/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from threading import Event
22
import pytest
33

4+
from control.bat import Bat
45
from control.chargepoint import chargepoint
56
from control.chargepoint.chargepoint_all import AllChargepoints
67
from control import bat_all, counter, pv_all, pv
@@ -10,7 +11,7 @@
1011
@pytest.fixture(autouse=True)
1112
def data_module() -> None:
1213
data.data_init(Event())
13-
data.data.bat_data.update({"all": bat_all.BatAll(), "bat2": bat_all.Bat(2)})
14+
data.data.bat_data.update({"all": bat_all.BatAll(), "bat2": Bat(2)})
1415
data.data.counter_data.update({"counter0": counter.Counter(0)})
1516
data.data.cp_all_data = AllChargepoints()
1617
data.data.cp_data.update({"cp4": chargepoint.Chargepoint(

0 commit comments

Comments
 (0)