Zusammenfassung
Die solar_cap-Regel setzt bei knapper freier Kapazitaet bewusst cap == floor, damit die Batterie ausschliesslich Clip-Energie aufnimmt und exportierbarer Ueberschuss ins Netz geht statt Kapazitaet zu belegen. Am Anfang des Clip-Fensters ist der Floor aber noch klein (zweistellige bis niedrige dreistellige Watt), und enforce_min_pv_charge_rate() hebt jeden Wert zwischen 1 und 499 W auf 500 W an. Die Batterie laedt dadurch ein Vielfaches der beabsichtigten Menge und verbraucht genau die Reserve, die die Regel kurz zuvor geschuetzt hat -- mit der Folge, dass am Peak abgeregelt wird.
Netto regelt solar_cap allein in unserem Beispieltag mehr ab als die reine Zeit-Regel, obwohl es die Anti-Abregelungs-Regel ist.
Reproduktion
python scripts/generate_peak_shaving_csv.py
Ergebnis fuer den Beispieltag (10 kWh Batterie, PV-Peak 5000 W, Grundlast 400 W, feed_in_limit_w: 4000, 15-min-Aufloesung):
| Konfiguration |
Batterie voll |
abgeregelt |
| ohne Peak Shaving |
11:45 |
1,03 kWh |
| nur time based |
14:00 |
0,01 kWh |
| nur solar_cap |
13:30 |
0,20 kWh |
| time + solar_cap |
15:15 |
0,00 kWh |
Sichtbar auch im Chart auf der Doku-Seite features/peak-shaving-scenarios.md (Szenario "Solar cap only").
Ursache
Auszug aus docs/assets/data/peak_shaving/solar.csv:
time,surplus_w,limit_w,charge_w,curtailed_w,soc_pct,rule_floor_w
11:45,4054.3,500,500.0,0.0,90.85,54
12:00,4243.5,500,500.0,0.0,92.10,243
12:15,4396.2,500,500.0,0.0,93.35,396
...
13:30,4508.4,508,399.4,109.0,100.00,508
13:45,4396.2,500,0.0,396.2,100.00,396
14:00,4243.5,500,0.0,243.5,100.00,243
14:15,4054.3,500,0.0,54.3,100.00,54
Rechnung fuer die drei ersten Clip-Slots:
Clip-Energie (54 + 243 + 396) W * 0,25 h = 173,2 Wh <- beabsichtigt
tatsaechlich geladen 500 W * 0,25 h * 3 = 375,0 Wh
Ueberschuss = 201,8 Wh
Die spaeter abgeregelte Energie betraegt (109,0 + 396,2 + 243,5 + 54,3) W * 0,25 h = 200,8 Wh -- also praktisch exakt der Betrag, den die Mindestladerate vorne zu viel eingeladen hat.
Codepfad
src/batcontrol/logic/solar_limit.py (Case B, knappe Kapazitaet) setzt den Cap gleich dem Floor:
extra_wh = max(0.0, free_capacity_wh - remaining_clip_wh) # 0
cap_w = int(floor_w + extra_wh / remaining_prod_h) # == floor_w
src/batcontrol/logic/next.py in _apply_solar_limit() hebt das Ergebnis anschliessend an:
final_w = solar_limit.merge_limits(floor_w, [settings.limit_battery_charge_rate, cap_w])
if final_w > 0:
final_w = self.common.enforce_min_pv_charge_rate(final_w) # 54 W -> 500 W
Zielkonflikt
Beide Seiten haben einen legitimen Grund:
MIN_CHARGE_RATE = 500 existiert, um ineffizientes Troepfchenladen zu vermeiden -- 54 W in die Batterie zu schieben ist tatsaechlich verlustbehaftet.
- Die dokumentierte Absicht der Regel (
docs/features/peak-shaving.md: "If free capacity is scarce, the cap equals the floor (absorb only clip power, grid feed-in at the limit)") wird dadurch unterlaufen.
Es ist also kein eindeutiger Bug, aber die Kosten sind messbar und die Doku beschreibt derzeit ein Verhalten, das so nicht eintritt.
Moegliche Richtungen
- Nichts aendern, nur dokumentieren. Der Effekt tritt nur bei knapper Kapazitaet auf und kostet im Beispiel 0,2 kWh. (Die Doku-Beschreibung wird unabhaengig von diesem Issue ergaenzt.)
- Floor von der Mindestladerate ausnehmen. Wenn
cap == floor gilt, den Wert nicht auf 500 W anheben, sondern entweder exakt den Floor fahren oder auf 0 setzen. Fuehrt kurzzeitig zu sehr kleinen Laderaten.
- Schwellwert statt Anhebung. Liegt der Floor unter
MIN_CHARGE_RATE, gar nicht laden (0) und die Kapazitaet vollstaendig fuer den Peak aufsparen. Vermeidet Troepfchenladen und den Reserveverbrauch, verschenkt aber die kleinen Clip-Mengen am Rand des Fensters.
- Reserve nachfuehren. Den Mehrverbrauch durch die Mindestladerate in der Reservierung einkalkulieren.
Aus den Zahlen des Beispieltags waere Variante 3 am wirksamsten, weil die Randslots ohnehin nur wenige Wh Clip enthalten, die spaeteren Peak-Slots aber viel.
Kontext
Gefunden beim Erstellen der Peak-Shaving-Szenarien fuer die Doku. Verwandt mit #39.
Zusammenfassung
Die
solar_cap-Regel setzt bei knapper freier Kapazitaet bewusstcap == floor, damit die Batterie ausschliesslich Clip-Energie aufnimmt und exportierbarer Ueberschuss ins Netz geht statt Kapazitaet zu belegen. Am Anfang des Clip-Fensters ist der Floor aber noch klein (zweistellige bis niedrige dreistellige Watt), undenforce_min_pv_charge_rate()hebt jeden Wert zwischen 1 und 499 W auf 500 W an. Die Batterie laedt dadurch ein Vielfaches der beabsichtigten Menge und verbraucht genau die Reserve, die die Regel kurz zuvor geschuetzt hat -- mit der Folge, dass am Peak abgeregelt wird.Netto regelt
solar_capallein in unserem Beispieltag mehr ab als die reine Zeit-Regel, obwohl es die Anti-Abregelungs-Regel ist.Reproduktion
Ergebnis fuer den Beispieltag (10 kWh Batterie, PV-Peak 5000 W, Grundlast 400 W,
feed_in_limit_w: 4000, 15-min-Aufloesung):Sichtbar auch im Chart auf der Doku-Seite
features/peak-shaving-scenarios.md(Szenario "Solar cap only").Ursache
Auszug aus
docs/assets/data/peak_shaving/solar.csv:Rechnung fuer die drei ersten Clip-Slots:
Die spaeter abgeregelte Energie betraegt
(109,0 + 396,2 + 243,5 + 54,3) W * 0,25 h = 200,8 Wh-- also praktisch exakt der Betrag, den die Mindestladerate vorne zu viel eingeladen hat.Codepfad
src/batcontrol/logic/solar_limit.py(Case B, knappe Kapazitaet) setzt den Cap gleich dem Floor:src/batcontrol/logic/next.pyin_apply_solar_limit()hebt das Ergebnis anschliessend an:Zielkonflikt
Beide Seiten haben einen legitimen Grund:
MIN_CHARGE_RATE = 500existiert, um ineffizientes Troepfchenladen zu vermeiden -- 54 W in die Batterie zu schieben ist tatsaechlich verlustbehaftet.docs/features/peak-shaving.md: "If free capacity is scarce, the cap equals the floor (absorb only clip power, grid feed-in at the limit)") wird dadurch unterlaufen.Es ist also kein eindeutiger Bug, aber die Kosten sind messbar und die Doku beschreibt derzeit ein Verhalten, das so nicht eintritt.
Moegliche Richtungen
cap == floorgilt, den Wert nicht auf 500 W anheben, sondern entweder exakt den Floor fahren oder auf 0 setzen. Fuehrt kurzzeitig zu sehr kleinen Laderaten.MIN_CHARGE_RATE, gar nicht laden (0) und die Kapazitaet vollstaendig fuer den Peak aufsparen. Vermeidet Troepfchenladen und den Reserveverbrauch, verschenkt aber die kleinen Clip-Mengen am Rand des Fensters.Aus den Zahlen des Beispieltags waere Variante 3 am wirksamsten, weil die Randslots ohnehin nur wenige Wh Clip enthalten, die spaeteren Peak-Slots aber viel.
Kontext
Gefunden beim Erstellen der Peak-Shaving-Szenarien fuer die Doku. Verwandt mit #39.