Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 29 additions & 26 deletions packages/control/bat_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,46 +187,49 @@ def calc_power_for_all_components(self):
except Exception:
log.exception("Fehler im Bat-Modul")

def _get_pv_power_beyond_max_ac_out(self, inverter: Pv) -> float:
"""gibt die PV-Leistung zurück, die über der maximalen Ausgangsleistung des Wechselrichters liegt und somit
nicht für die Entladung des Speichers genutzt werden kann."""
# tested
# Wenn vom PV-Ertrag der Speicher geladen wird, kann diese Leistung bis zur max Ausgangsleistung des WR
# genutzt werden.
if inverter.data.config.max_ac_out > 0:
return max(inverter.data.get.power * -1 - inverter.data.config.max_ac_out, 0)
else:
return 0
def get_bat_power_of_hybrid_system(self, inverter: Pv) -> float:
""" ermittelt die Leistung des Speichers, die über den Wechselrichter fließt, um die Leistung des Speichers
bei einem Hybrid-System zu berücksichtigen, wenn die maximale Ausgangsleistung des Wechselrichters erreicht ist.
"""
bat_power = 0
try:
children = data.data.counter_all_data.get_entry_of_element(inverter.num)["children"]
if len(children):
hybrid: List[str] = []
for c in children:
if c.get("type") == "bat":
hybrid.append(f'bat{c["id"]}')
break
if len(hybrid):
for bat in hybrid:
# nur wenn der Speicher entlädt, fließt Leistung über den WR
if data.data.bat_data[bat].data.get.power < 0:
bat_power += data.data.bat_data[bat].data.get.power
Comment on lines +195 to +207
except Exception:
log.exception(f"Fehler im Bat-Modul {inverter.num}")
return bat_power

def _limit_bat_power_discharge(self, required_power):
def _limit_bat_power_discharge(self, required_power: float) -> float:
"""begrenzt die für den Algorithmus benötigte Entladeleistung des Speichers, wenn die maximale Ausgangsleistung
des WR erreicht ist."""
pv_power_beyond_max_ac_out = 0
if required_power > 0:
# Nur wenn der Speicher entladen werden soll, fließt Leistung durch den WR.
remaining_inverter_ac_out_power = 0
for inverter in data.data.pv_data.values():
try:
pv_power_beyond_max_ac_out += self._get_pv_power_beyond_max_ac_out(inverter)
bat_power = self.get_bat_power_of_hybrid_system(inverter)
inverter_power = max((inverter.data.get.power + bat_power) * -1, 0)
remaining_inverter_ac_out_power += inverter.data.config.max_ac_out - inverter_power + bat_power * -1
except Exception:
log.exception(f"Fehler im Bat-Modul {inverter.num}")
# Wenn max_inverter_power_for_bat nur deshalb negativ ist, weil der Speicher aktuell bereits entlädt
# (self.data.get.power < 0) und keine PV-Leistung über der maximalen WR-Ausgangsleistung anliegt
# (pv_power_beyond_max_ac_out == 0), dann würde eine Begrenzung auf 0W die noch verfügbare
# Entladeleistung fälschlicherweise unterdrücken. In diesem Fall wenden wir keine zusätzliche
# Begrenzung durch die WR-Ausgangsleistung an.
if pv_power_beyond_max_ac_out > 0:
max_inverter_power_for_bat = self.data.get.power - pv_power_beyond_max_ac_out
# Negative Werte bedeuten, dass bereits mehr Leistung über den WR fließt, als für den Speicher
# zusätzlich verfügbar ist; in diesem Fall ist keine weitere Entladung möglich.
max_inverter_power_for_bat = max(max_inverter_power_for_bat, 0)
required_power = min(required_power, max_inverter_power_for_bat)
if remaining_inverter_ac_out_power > 0:
required_power = min(required_power, remaining_inverter_ac_out_power)
log.debug(
f"Verbleibende Speicher-Leistung durch maximale Ausgangsleistung auf {required_power}W begrenzt."
)
else:
log.debug(
"Speicher-Entladeleistung nicht durch maximale WR-Ausgangsleistung begrenzt, da keine PV-Leistung "
"über der maximalen WR-Ausgangsleistung anliegt.")
"Speicher-Entladeleistung nicht durch maximale WR-Ausgangsleistung begrenzt.")
return required_power

def _set_bat_power_active_control(self, power):
Expand Down
51 changes: 13 additions & 38 deletions packages/control/bat_all_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,56 +27,31 @@ def data_fixture() -> None:


@pytest.mark.parametrize(
"max_ac_out, power, expected_result",
"bat_power, required_power, pv_power, expected_power",
[
pytest.param(5000, -6000, 1000, id="Leistung überschreitet max_ac_out"),
pytest.param(5000, -4000, 0, id="Leistung liegt unter max_ac_out"),
pytest.param(5000, 0, 0, id="Keine Leistung (power = 0)"),
pytest.param(0, -6000, 0, id="max_ac_out ist 0"),
],
)
def test_pv_power_beyond_max_ac_out(max_ac_out: int, power: int, expected_result: int):
# Mock für die Pv-Klasse
inverter = Pv(1)
inverter.data.config.max_ac_out = max_ac_out
inverter.data.get.power = power
bat_all = BatAll()

# Aufruf der zu testenden Funktion
result = bat_all._get_pv_power_beyond_max_ac_out(inverter)

# Überprüfung des Ergebnisses
assert result == expected_result


@pytest.mark.parametrize(
"bat_power, required_power, return_pv_power_beyond_max_ac_out, expected_power",
[
pytest.param(1000, 1000, 0, 1000, id="exakt max Leistung des WR"),
pytest.param(1000, 1000, 100, 900, id="max Leistung des WR um 100W überschritten"),
pytest.param(1000, 1000, 1100, 0, id="maximale Entladeleistung erreicht"),
pytest.param(3000, 3000, 2000, 1000, id="max Leistung des WR um 2000W überschritten"),
pytest.param(3000, 5000, 2000, 1000, id="max Leistung des WR um 2000W überschritten, " +
"erlaubte Entladeleistung höher als aktuelle Leistung"),
pytest.param(-1000, 1100, 0, 1100, id="Speicher entlädt, soll entladen"),
pytest.param(-1000, -600, 0, -600, id="Speicher entlädt, soll weniger entladen"),
pytest.param(0, 600, 0, 600, id="Speicher ruht, soll entladen"),
pytest.param(-1000, 1000, 0, 1000, id="Leistung verfügbar"),
pytest.param(-4900, 5100, -100, 4900, id="max Leistung des WR um 100W überschritten, Speicher entlädt"),
pytest.param(1000, 1600, -4500, 500, id="Speicher lädt, soll entladen"),
])
def test_limit_bat_power_discharge(bat_power: int,
required_power: int,
return_pv_power_beyond_max_ac_out: int,
pv_power: int,
expected_power: int,
monkeypatch):
monkeypatch: pytest.MonkeyPatch):
# setup
data.data.pv_data = {"pv2": Pv(2)}
mock_pv_power_beyond_max_ac_out = Mock(return_value=return_pv_power_beyond_max_ac_out)
monkeypatch.setattr(BatAll, "_get_pv_power_beyond_max_ac_out", mock_pv_power_beyond_max_ac_out)
data.data.pv_data["pv2"].data.get.power = pv_power
data.data.pv_data["pv2"].data.config.max_ac_out = 5000
data.data.bat_data["bat1"] = Bat(1)
data.data.bat_data["bat1"].data.get.power = bat_power
mock_entry_children = Mock(return_value={"children": [{"id": 1, "type": "bat"}]})
monkeypatch.setattr(data.data.counter_all_data, "get_entry_of_element", mock_entry_children)

b = BatAll()
b.data.get.power = bat_power

# execution
power = b._limit_bat_power_discharge(required_power)
power = b._limit_bat_power_discharge(required_power) # pyright: ignore[reportPrivateUsage]

# evaluation
assert power == expected_power
Expand Down
2 changes: 1 addition & 1 deletion packages/control/counter_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def get_counters_to_check(self, num: int) -> List[str]:
self.__get_all_counter_in_branch(self.data.get.hierarchy[0], num)
return self.connected_counters

def get_entry_of_element(self, id_to_find: int) -> Dict:
def get_entry_of_element(self, id_to_find: int) -> Dict[str, List[Dict[str, Union[int, str]]]]:
item = self.__is_id_in_top_level(id_to_find)
if item:
return item
Expand Down
2 changes: 1 addition & 1 deletion packages/control/pv.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ class PvData:


class Pv:
def __init__(self, index):
def __init__(self, index: int):
self.data = PvData()
self.num = index
Loading