From 6aa72602fea5fb53fde79cc1c8683335b2faac9d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 07:55:14 +0000 Subject: [PATCH 01/13] feat: evaluate solar feed-in limit (Solarspitzengesetz) clip absorption Add a day simulation for a proposed peak-shaving rule that absorbs PV energy above the 60% feed-in limit for uncontrolled plants into the battery instead of losing it to inverter curtailment: - scripts/simulate_solar_limit_day.py: candidate algorithm (reservation cap before the clipping window, charge floor inside it, merge rule final = max(floor, min(caps))) plus six scenarios comparing baseline, legacy time-based shaving and the new rule - docs/development/solar-limit-evaluation.md: results (100% clip recovery with correct forecast, theoretical maximum with a scarce battery), per-rule switch config design (time_active, price_active, solar_cap_active with feed_in_limit_w: 0 as neutral), documented rule priorities and integration roadmap - register the evaluation page in mkdocs.yml, extend scripts/README.md Key finding: the existing time/price caps can themselves cause curtailment losses on clipping days (1.8 kWh on the reference day); the proposed floor eliminates that. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- docs/development/solar-limit-evaluation.md | 235 ++++++++++ mkdocs.yml | 2 + scripts/README.md | 23 + scripts/simulate_solar_limit_day.py | 518 +++++++++++++++++++++ 4 files changed, 778 insertions(+) create mode 100644 docs/development/solar-limit-evaluation.md create mode 100644 scripts/simulate_solar_limit_day.py diff --git a/docs/development/solar-limit-evaluation.md b/docs/development/solar-limit-evaluation.md new file mode 100644 index 00000000..32ed1c7d --- /dev/null +++ b/docs/development/solar-limit-evaluation.md @@ -0,0 +1,235 @@ +# Evaluation: Solar-Einspeisegrenze (Solarspitzengesetz) im Peak-Shaving + +Status: **Evaluations- und Simulationsphase** — noch nicht in `logic/next.py` integriert. +Simulationsskript: [`scripts/simulate_solar_limit_day.py`](https://github.com/MaStr/batcontrol/blob/main/scripts/simulate_solar_limit_day.py) + +## Hintergrund: die 60-Prozent-Regel + +Das Solarspitzengesetz (in Kraft seit 25.02.2025) begrenzt für ungesteuerte PV-Anlagen +(ohne iMSys + Steuerbox) die Wirkleistungseinspeisung am Netzanschlusspunkt auf **60 % der +installierten Leistung** (§ 9 Abs. 2 Nr. 3 EEG). Der Wechselrichter setzt die Grenze hart +durch: Erzeugung oberhalb der Grenze wird **abgeregelt und ist verloren**, außer sie wird +selbst verbraucht oder in den Akku geladen. + +Für eine 10-kWp-Anlage heißt das: maximal 6 000 W Einspeisung. An einem klaren Sommertag +mit ~8,9 kW Spitzenleistung liegen mehrere Stunden über der Grenze; ohne Gegenmaßnahme +gehen an so einem Tag ca. **7,5 kWh verloren** (siehe Referenzszenario unten). + +Wichtig: Es handelt sich um eine **Leistungsgrenze, keine Mengengrenze**. Die Verluste +konzentrieren sich auf die Mittagsspitze — genau das Zeitfenster, in dem der Akku +bei naivem Verhalten längst voll ist. + +## Kernbefund: das heutige Peak-Shaving kann Abregelung verursachen + +Das bestehende Peak-Shaving (Modi `time`/`price`) setzt einen **Cap** auf die +PV-Ladeleistung (`limit_battery_charge_rate`). Liegt der PV-Überschuss über der +Einspeisegrenze, blockiert dieser Cap genau die Energie, die sonst in den Akku müsste — +die Differenz wird abgeregelt. Im Referenzszenario regelt das reine Zeit-Shaving +1,8 kWh ab, die mit der neuen Regel vollständig gerettet werden. + +Umgekehrt hilft das Zeit-Shaving bereits teilweise (76 % Rückgewinnung vs. 0 % Baseline), +weil es Kapazität in den Nachmittag verschiebt — aber unkoordiniert und ohne Garantie. + +## Vorgeschlagener Algorithmus: Regel "solar_cap" + +Die neue Regel arbeitet mit den vorhandenen Forecast-Arrays (Wh pro Intervall, +Index 0 = jetzt) und kennt zwei Fälle. Pro Slot `k` (bis zum Ende des Produktionsfensters): + +``` +surplus_wh[k] = max(0, production[k] - consumption[k]) +feed_allow_wh[k] = feed_in_limit_w * slot_h[k] +clip_wh[k] = min(surplus_wh[k], max(0, surplus_wh[k] - feed_allow_wh[k]) * headroom) +``` + +**Fall A — vor dem Kappungsfenster: Reservierungs-Cap.** +Freie Kapazität minus prognostizierte Kappungsenergie wird gleichmäßig über die Slots +bis Fensterbeginn verteilt. Ist die Reserve größer als die freie Kapazität, wird das +PV-Laden komplett geblockt (Cap 0). Damit verdrängt einspeisbare Energie nicht 1:1 die +Kappungsenergie im Akku. + +**Fall B — im Kappungsfenster: Floor + kapazitätsschonender Cap.** + +``` +floor_w = clip_raw_wh[0] / slot_h[0] # Pflicht-Laderate, ohne headroom +cap_w = -1 wenn Gesamt-Surplus <= freie Kapazität + = floor_w + extra_wh / restliche_h sonst (extra = freie Kap. - restliche Kappung) +``` + +Bei Knappheit (`extra = 0`) gilt `cap == floor`: Der Akku nimmt **nur** Kappungsenergie +auf, alles unterhalb der Grenze wird eingespeist. Eine Priorisierung innerhalb des +Fensters ist unnötig — jede absorbierte Kappungs-Wh ist gleichwertig; schädlich ist +allein das Füllen der Kapazität mit einspeisbarer Energie. + +Der Floor wird aus der **Roh-Kappung ohne headroom** berechnet: Es wird nie Energie +zwangsgeladen, die legal eingespeist werden könnte. + +## Konfigurationsdesign: Schalter pro Regel + +Mit drei Regelsorten (Zielzeit, Preis, Solar) wird der bisherige `mode`-String +(`time`/`price`/`combined`) unübersichtlich. Beschlossenes Design: **ein expliziter +Schalter pro Regel**, `mode` wird deprecated und beim Einlesen auf die Schalter gemappt +(`time` → `time_active`, `price` → `price_active`, `combined` → beide): + +```yaml +peak_shaving: + enabled: false # Master-Schalter (wie bisher, inkl. evcc-Override) + time_active: true # Zielzeit-Regel (counter-linearer Ramp) + price_active: false # Preis-Regel (Reserve fuer Billigfenster) + solar_cap_active: false # NEU: Kappungs-Absorption (Einspeisegrenze) + allow_full_battery_after: 14 # Parameter der Zielzeit-Regel + price_limit: 0.05 # Parameter der Preis-Regel + feed_in_limit_w: 0 # Parameter der Solar-Regel: Einspeisegrenze in W. + # 0 = Neutralstellung (Regel wirkungslos, auch wenn + # solar_cap_active true ist). Formel: 0.6 * kWp * 1000 + feed_in_limit_headroom: 1.0 # Sicherheitsfaktor >= 1.0 auf die prognostizierte + # Kappungsenergie (nur Reservierung, nie Floor) +``` + +`feed_in_limit_w` ist bewusst ein **absoluter Wattwert**: Die installierte Leistung (kWp) +steht heute nur bei fcsolar-`pvinstallations` in der Config (bei Solcast gar nicht), und +die Grenze gilt am Netzanschlusspunkt der Gesamtanlage. `0` ist die Neutralstellung — +zusätzlich zum Schalter, damit eine unkonfigurierte Grenze nie versehentlich als +"0 W Einspeisung erlaubt" interpretiert wird. + +### Prioritäten zwischen den Regelsorten + +Dokumentierte, feste Rangfolge (keine Konfiguration nötig): + +1. **`enabled` (Master)** aus → keine Regel wirkt (inkl. evcc-Laufzeit-Override). +2. **Force-Charge aus dem Netz (MODE -1)** überstimmt jedes Peak-Shaving (wie heute). +3. **Alle aktiven Cap-Regeln** (Zielzeit-Ramp, Preis-Reserve, Solar-Reservierung) + liefern je ein Limit; das **strengste gewinnt** (`min`, wie heute bei `combined`). +4. **Der Solar-Floor überstimmt jeden Cap**: `final = max(floor, min(caps))`. + Begründung: Caps optimieren Ökonomie (Ladung verschieben), der Floor verhindert + **physischen Verlust** (Abregelung). Ein Cap unterhalb des Floors würde Energie + vernichten. Deshalb gilt der Floor auch **nach** `allow_full_battery_after` und + auch bei hohem SoC (`always_allow_discharge`-Region) — das Kappungsfenster dauert + physikalisch länger als die Zielstunde. Konsequenz: Die Solar-Reservierung kann den + Akku erst nach der Zielstunde voll werden lassen; verlorene Energie wiegt schwerer + als ein später voller Akku. +5. **Statische Inverter-Klemmen** zuletzt (`max_pv_charge_rate` als Obergrenze, + 500-W-Minimum via `enforce_min_pv_charge_rate`). Achtung: ein konfiguriertes + `max_pv_charge_rate` unterhalb des Floors macht Abregelung physisch unvermeidbar + → Startup-Warnung vorgesehen. + +Sentinel-Semantik bleibt: `-1` = kein Limit, `0` = Laden blocken. `-1` erfüllt jeden +Floor automatisch, weil der Inverter Überschuss dann ohnehin greedy in den Akku lädt — +es ist **kein neuer Inverter-Modus** nötig, der Floor ist die Garantie +`angewandter Cap >= floor`. + +## Simulationsergebnisse + +Alle Zahlen aus `scripts/simulate_solar_limit_day.py` (Referenz: 10 kWp Süd, klarer +Sommertag, Peak 8,9 kW, Grenze 6 000 W, 10 kWh Akku, 400 W Grundlast, Start-SoC 15 %, +Stundenraster). "Rückgewinnung" = Anteil der ohne Akku abgeregelten Energie, der +gerettet wird. + +### Szenario 1 — Referenztag + +| Trace | Eingespeist | Abgeregelt | Rückgewinnung | +|----------------------------------|------------:|-----------:|--------------:| +| Baseline (alle Regeln aus) | 40,50 kWh | 7,50 kWh | 0 % | +| Nur `time_active` (heute) | 46,20 kWh | 1,80 kWh | 76,0 % | +| Nur `solar_cap_active` | 48,00 kWh | 0,00 kWh | **100 %** | +| `time_active + solar_cap_active` | 48,00 kWh | 0,00 kWh | **100 %** | + +End-SoC ist in allen Traces identisch (83,3 %) — die Regel verschenkt nichts, sie +verschiebt nur, **womit** der Akku gefüllt wird. Im Slot-Detail sichtbar: Vor dem +Fenster begrenzt der Reservierungs-Cap auf 625 W; ab 11:00 hebt der Floor die Laderate +exakt auf die Kappungsleistung (1 200 → 2 500 → 2 400 → 1 400 W), die Einspeisung +steht dabei konstant auf 6 000 W. In der Kombination überstimmt der Floor den +Zeit-Ramp-Cap genau dann, wenn dieser Abregelung verursachen würde. + +### Szenario 2 — Ost/West-Profil (Peak 5,6 kW < Grenze) + +Keine Kappung erwartet; die Regel bleibt vollständig inert — Trace bitidentisch zur +Baseline (Regressionsprüfung bestanden, keine False Positives). + +### Szenario 3 — Kleiner Akku (5 kWh, Knappheit) + +Freie Kapazität bei Fensterbeginn 5,00 kWh, Kappungspotenzial 7,50 kWh: + +| Trace | Abgeregelt | Rückgewinnung | +|------------------------|-----------:|--------------:| +| Baseline | 7,50 kWh | 0 % | +| Nur `solar_cap_active` | 2,50 kWh | 66,7 % | + +Zurückgewonnen: **5,00 kWh = exakt die freie Kapazität bei Fensterbeginn** — das +theoretische Maximum. Die Reservierung blockt morgens das PV-Laden komplett (Cap 0, +Einspeisung läuft unterhalb der Grenze weiter), im Fenster gilt `cap == floor`. + +### Szenario 4 — Prognosefehler (Forecast = 85 % der Realität) + +| Trace | Abgeregelt | Rückgewinnung | +|-----------------------------|-----------:|--------------:| +| Baseline | 7,50 kWh | 0 % | +| solar, headroom 1.0 | 4,96 kWh | 33,8 % | +| solar, headroom 1.2 | 4,46 kWh | 40,6 % | +| solar, headroom 1.5 | 4,23 kWh | 43,5 % | +| solar, perfekter Forecast | 0,00 kWh | 100 % | + +Erkenntnisse: (a) Der Algorithmus ist deutlich forecast-sensitiv — eine +15-%-Unterschätzung der Produktion unterschätzt die Kappung überproportional (Kappung +ist die "Spitze" der Kurve). (b) `headroom` verbessert die Reservierung nur moderat +(+7 Punkte bei 1.2), weil im Fenster auch der **Floor** aus dem zu niedrigen Forecast +berechnet wird. Empfehlung: Default `1.0`, dokumentiert `1.2` für konservative Nutzer; +als **Verbesserungsoption für die Integration**: den Floor in Slot 0 aus der +**Live-Messung** des Wechselrichters statt aus dem Forecast speisen (batcontrol +evaluiert alle ~3 Minuten — die aktuelle Produktion ist bekannt). Das würde die +Floor-Hälfte des Fehlers eliminieren; die Simulation modelliert das bewusst noch nicht. + +### Szenario 5 — Mittags-Verbrauchsspitze (2,4 kW, 12–14 Uhr) + +Eigenverbrauch senkt das Kappungspotenzial auf 3,50 kWh; Kombination +`time + solar_cap` gewinnt 100 % zurück (Baseline 0 %, nur-Zeit 60 %). + +### Szenario 6 — 15-Minuten-Raster + +Konsistenzprüfung am interpolierten Referenztag: 99,1 % Rückgewinnung (Restverlust +0,07 kWh durch Interpolationskanten an Slot-Grenzen). Das 15-Minuten-Raster reduziert +zusätzlich den systematischen Fehler "Stundenmittel unterschätzt Momentankappung". + +## Bewertung + +Der Algorithmus erfüllt die Anforderungen: + +1. **Er rettet die "40 %"**: 100 % Rückgewinnung bei korrektem Forecast, exakt das + physikalische Maximum bei knappem Akku. +2. **Er repariert einen Defekt**: Ohne den Floor verursacht das bestehende Peak-Shaving + an Kappungstagen selbst Verluste (1,8 kWh am Referenztag). +3. **Er ist minimal-invasiv**: kein neuer Inverter-Modus, keine neue Datenquelle, + gleiche Sentinel-Semantik, additiv als Post-Processing-Schritt. +4. **Er ist neutral, wenn er nichts zu tun hat** (Ost/West-Szenario) und per + `feed_in_limit_w: 0` bzw. `solar_cap_active: false` vollständig abschaltbar. + +Bekannte Grenzen: Forecast-Sensitivität (siehe Szenario 4; Mitigation: Live-Messung +für den Floor, 15-Minuten-Raster, headroom) und Stundenmittel vs. Momentanleistung +(ein Slot mit Mittel knapp unter der Grenze kann real kurzzeitig kappen — nicht +modelliert, durch headroom teilweise abgedeckt). + +## Integrations-Roadmap (Folgeschritt) + +1. `logic/logic_interface.py`: `PeakShavingConfig` um `time_active`, `price_active`, + `solar_cap_active`, `feed_in_limit_w` (Default 0 = neutral), `feed_in_limit_headroom` + (Default 1.0) erweitern; `mode` deprecaten und in `from_config()` auf die Schalter + mappen (Warnung loggen); Validierung analog `price_limit`. +2. Neues `logic/solar_limit.py`: `compute_solar_limit()` und `merge_limits()` aus dem + Simulationsskript unverändert übernehmen (pure Funktionen, Muster + `grid_charge_target.py`). +3. `logic/next.py`: eigener Post-Processing-Schritt `_apply_solar_limit()` **nach** + `_apply_peak_shaving()` mit eigener (kleinerer) Skip-Liste: läuft auch bei hohem SoC + und nach `allow_full_battery_after`; skippt bei Force-Charge und (v1) bei + `allow_discharge == False` (dort lädt der Inverter Überschuss ohnehin ungebremst). + Merge nach der Prioritätsregel oben; `enforce_min_pv_charge_rate` einmalig auf den + final gemergten Wert. Helper `_remaining_interval_hours()` extrahieren + (anteiliger Slot 0, vgl. Grid-Recharge-Block). +4. `core.py`: Startup-Warnung wenn `feed_in_limit_w > 0` und `max_pv_charge_rate > 0`. +5. Tests: `tests/batcontrol/logic/test_solar_limit.py` (pure Funktionen) + Integrationsfälle + in `test_peak_shaving.py` (Floor überstimmt Cap inkl. Cap 0, Reservierung, Knappheit + `cap == floor`, Neutralstellung = bitidentisches Verhalten, Sentinels, Slot-0-Anteiligkeit, + 15-min, mode-Deprecation-Mapping). +6. `config/batcontrol_config_dummy.yaml` + `docs/features/peak-shaving.md` + + HA-Add-on-Spiegelung (`MaStr/batcontrol_ha_addon`). +7. Offen für die Integration: Live-Messung als Floor-Quelle für Slot 0 (siehe Szenario 4); + aktives Entladen vor dem Fenster (v1: nein, nur passive Reservierung); MQTT-Topic + `predicted_clip_wh` (read-only, optional). diff --git a/mkdocs.yml b/mkdocs.yml index 1bab3703..2e507461 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -79,6 +79,7 @@ plugins: - integrations/forecast-metrics.md: Forecast data exposed via MQTT Optional: - development/15-min-transform.md: Internal 15-minute interval resolution + - development/solar-limit-evaluation.md: Solar feed-in limit (Solarspitzengesetz) evaluation nav: - Home: index.md @@ -102,6 +103,7 @@ nav: - evcc Connection: integrations/evcc-connection.md - Development: - 15-Minute Interval Transformation: development/15-min-transform.md + - Solar Feed-in Limit Evaluation: development/solar-limit-evaluation.md validation: links: diff --git a/scripts/README.md b/scripts/README.md index 604ea81f..036fd573 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -13,6 +13,29 @@ The `scripts` folder is separate from the `tests` folder to avoid interference w ## Available Scripts +### simulate_solar_limit_day.py + +Day simulation for the proposed solar feed-in limit rule (Solarspitzengesetz, +60% feed-in cap for uncontrolled PV plants). Evaluates the "solar_cap" peak +shaving rule: reserve battery capacity before the predicted clipping window +and enforce a charge floor during it so the battery absorbs energy the +inverter would otherwise curtail. + +**Usage:** +```bash +python scripts/simulate_solar_limit_day.py +``` + +**Features:** +- Six scenarios: reference summer day, east-west profile, small battery, + forecast error with headroom sweep, midday consumption spike, 15-min interval +- Compares baseline, legacy time-based peak shaving, and the new rule +- Prints curtailed/feed-in energy, end SoC and clip-recovery percentage +- Contains the candidate algorithm (`compute_solar_limit`, `merge_limits`) + intended to move to `src/batcontrol/logic/solar_limit.py` + +See `docs/development/solar-limit-evaluation.md` for results and design. + ### test_evcc.py Standalone test script for the evcc dynamic tariff module. diff --git a/scripts/simulate_solar_limit_day.py b/scripts/simulate_solar_limit_day.py new file mode 100644 index 00000000..893fbb54 --- /dev/null +++ b/scripts/simulate_solar_limit_day.py @@ -0,0 +1,518 @@ +#!/usr/bin/env python3 +"""Day simulation for solar feed-in limit clip absorption (Solarspitzengesetz). + +German law (in force since 2025-02-25) limits uncontrolled PV plants (no +iMSys + Steuerbox) to feeding in at most 60% of their installed power at the +grid connection point. The inverter curtails everything above the limit -- +that energy is LOST unless it is self-consumed or charged into the battery. + +This script evaluates a proposed peak-shaving extension ("solar cap rule"): + - BEFORE the predicted clipping window: cap PV->battery charging so battery + capacity is reserved for energy that would otherwise be curtailed + (exportable energy must not displace clip energy 1:1). + - DURING clipping slots: enforce a charge FLOOR so the battery absorbs at + least the power above the feed-in limit. Important: the existing + time/price peak-shaving caps can otherwise CAUSE curtailment losses. + +Rule switches simulated (proposed config design): + time_active - existing counter-linear ramp until allow_full_battery_after + solar_cap_active - new rule evaluated here (reservation cap + floor) + (price_active exists in the code base but is orthogonal; not simulated) + +Priority between rules: + final_limit = max(solar_floor, min(all active caps)) + -1 = no cap. The floor overrides every cap because a cap below the floor + burns energy (curtailment); caps only optimize economics. + +The candidate algorithm lives in the "proposed algorithm" section below and +is cut so it can move to src/batcontrol/logic/solar_limit.py unchanged. + +Usage: + python scripts/simulate_solar_limit_day.py +""" +import sys +import os +import datetime + +import numpy as np + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from batcontrol.logic.next import NextLogic +from batcontrol.logic.logic_interface import ( + CalculationInput, + CalculationParameters, + PeakShavingConfig, +) +from batcontrol.logic.common import CommonLogic + +# --------------------------------------------------------------------------- +# Global simulation parameters +# --------------------------------------------------------------------------- +FEED_IN_LIMIT_W = 6_000 # W = 60% of a 10 kWp plant +CONSUMPTION_W = 400 # W constant house load (unless scenario overrides) +ALLOW_FULL_AFTER = 14 # target hour for the legacy time rule +INITIAL_SOC_PCT = 0.15 + +TZ = datetime.timezone.utc +BASE_DATE = datetime.datetime(2026, 6, 21, 0, 0, 0, tzinfo=TZ) + +# 10 kWp south-facing, clear summer day (W per hour). Peak ~8.9 kW. +PROFILE_SOUTH_W = np.array([ + 0, 0, 0, 0, 0, 50, # 00-05 + 300, 1200, 2800, 4700, 6300, 7600, # 06-11 + 8900, 8800, 7800, 6300, 4600, 2700, # 12-17 + 1100, 300, 30, 0, 0, 0, # 18-23 +], dtype=float) + +# 10 kWp east-west, flatter curve, peak below the feed-in limit. +PROFILE_EAST_WEST_W = np.array([ + 0, 0, 0, 0, 0, 100, # 00-05 + 700, 1800, 3000, 4100, 4900, 5400, # 06-11 + 5600, 5600, 5400, 4900, 4100, 3000, # 12-17 + 1800, 700, 100, 0, 0, 0, # 18-23 +], dtype=float) + + +# --------------------------------------------------------------------------- +# Proposed algorithm (candidate for src/batcontrol/logic/solar_limit.py) +# --------------------------------------------------------------------------- +def compute_solar_limit(production_wh, consumption_wh, feed_in_limit_w, + interval_h, free_capacity_wh, max_capacity_wh, + headroom=1.0, slot0_hours=None): + """Compute the solar-cap rule output for the current slot. + + Args: + production_wh: forecast PV energy per slot (Wh), index 0 = now. + consumption_wh: forecast consumption per slot (Wh). + feed_in_limit_w: grid feed-in power limit in W. <= 0 = rule inactive + (neutral value, e.g. 0). + interval_h: slot length in hours (0.25 or 1.0). + free_capacity_wh: battery free capacity (Wh). + max_capacity_wh: battery max capacity (Wh). + headroom: safety factor >= 1.0 on predicted clip energy for + RESERVATION sizing only (hourly averages understate + instantaneous clipping). Never applied to the floor. + slot0_hours: remaining hours in the current slot (partial slot). + Defaults to interval_h. + + Returns: + (floor_w, cap_w): + floor_w: minimum charge rate (W) the battery must sustain NOW to + absorb power above the feed-in limit. 0 = no floor. + cap_w: charge rate cap (W) to reserve capacity for the clip + window. -1 = no cap, 0 = block charging. + """ + if feed_in_limit_w is None or feed_in_limit_w <= 0: + return 0, -1 + if slot0_hours is None: + slot0_hours = interval_h + + n = min(len(production_wh), len(consumption_wh)) + # Production window ends at the first slot with zero production + # (same convention as the price-based peak shaving rule). + prod_end = n + for i in range(n): + if float(production_wh[i]) == 0: + prod_end = i + break + if prod_end == 0: + return 0, -1 + + slot_h = np.full(prod_end, interval_h, dtype=float) + slot_h[0] = slot0_hours + + surplus_wh = np.clip( + np.asarray(production_wh[:prod_end], dtype=float) + - np.asarray(consumption_wh[:prod_end], dtype=float), + 0, None) + feed_allow_wh = feed_in_limit_w * slot_h + clip_raw_wh = np.clip(surplus_wh - feed_allow_wh, 0, None) + # Headroom only inflates the reservation; a slot can never clip more + # than its surplus. + clip_wh = np.minimum(surplus_wh, clip_raw_wh * headroom) + + clip_slots = np.nonzero(clip_raw_wh > 0)[0] + if len(clip_slots) == 0: + return 0, -1 + + first_clip = int(clip_slots[0]) + + # -- Case A: before the clip window -> reservation cap ---------------- # + if first_clip > 0: + total_clip_wh = min(float(np.sum(clip_wh)), max_capacity_wh) + allowed_wh = free_capacity_wh - total_clip_wh + if allowed_wh <= 0: + return 0, 0 # block PV charging, keep all capacity for the clip + hours_before = slot0_hours + (first_clip - 1) * interval_h + return 0, int(allowed_wh / hours_before) + + # -- Case B: inside a clip slot -> floor + capacity-preserving cap ---- # + # Floor from the RAW clip (no headroom): never force absorbing energy + # that could legally be exported. + floor_w = clip_raw_wh[0] / slot0_hours + total_surplus_wh = float(np.sum(surplus_wh)) + if total_surplus_wh <= free_capacity_wh: + return int(floor_w), -1 # everything fits, no cap needed + + remaining_clip_wh = float(np.sum(clip_wh)) + extra_wh = max(0.0, free_capacity_wh - remaining_clip_wh) + remaining_prod_h = float(np.sum(slot_h)) + # When clip energy alone exceeds free capacity (extra == 0) the cap + # equals the floor: the battery absorbs ONLY otherwise-curtailed energy, + # exportable surplus goes to the grid instead of displacing clip energy. + cap_w = int(floor_w + extra_wh / remaining_prod_h) + return int(floor_w), cap_w + + +def merge_limits(floor_w, caps): + """Merge rule outputs: final = max(floor, min(active caps)). + + caps entries: -1 = rule emits no cap. Returns -1 (no limit), 0 (block) + or a positive W value. An unlimited cap always satisfies the floor + because the inverter charges PV surplus greedily. + """ + active = [c for c in caps if c is not None and c >= 0] + if not active: + return -1 + return max(int(floor_w), min(active)) + + +# --------------------------------------------------------------------------- +# Battery / feed-in model +# --------------------------------------------------------------------------- +def apply_slot(prod_w, cons_w, limit_w, stored_wh, capacity_wh, + feed_in_limit_w, interval_h): + """Advance the battery by one slot under a feed-in power limit. + + The inverter charges PV surplus greedily up to limit_w (-1 = unlimited), + exports the rest up to feed_in_limit_w and curtails everything above. + + Returns (charge_w, feed_in_w, curtailed_w, new_stored_wh). + """ + surplus_w = prod_w - cons_w + if surplus_w <= 0: + discharge_w = min(-surplus_w, stored_wh / interval_h) + return 0.0, 0.0, 0.0, max(stored_wh - discharge_w * interval_h, 0.0) + + if limit_w == 0: + want_w = 0.0 + elif limit_w > 0: + want_w = min(surplus_w, float(limit_w)) + else: + want_w = surplus_w + + charge_wh = min(want_w * interval_h, capacity_wh - stored_wh) + charge_w = charge_wh / interval_h + rest_w = surplus_w - charge_w + feed_in_w = min(rest_w, feed_in_limit_w) + curtailed_w = rest_w - feed_in_w + return charge_w, feed_in_w, curtailed_w, stored_wh + charge_wh + + +# --------------------------------------------------------------------------- +# Scenario runner +# --------------------------------------------------------------------------- +def run_day(prod_actual_w, cons_actual_w, capacity_wh, + time_active=False, solar_cap_active=False, + forecast_prod_w=None, forecast_cons_w=None, + feed_in_limit_w=FEED_IN_LIMIT_W, headroom=1.0, + interval_min=60, allow_full_after=ALLOW_FULL_AFTER, + initial_soc_wh=None, collect_rows=False): + """Simulate one day and return metrics (and per-slot rows on request).""" + interval_h = interval_min / 60.0 + n_slots = len(prod_actual_w) + if forecast_prod_w is None: + forecast_prod_w = prod_actual_w + if forecast_cons_w is None: + forecast_cons_w = cons_actual_w + if initial_soc_wh is None: + initial_soc_wh = INITIAL_SOC_PCT * capacity_wh + + # CommonLogic is a singleton keyed to battery capacity -> reset per run. + CommonLogic._instance = None + common = CommonLogic.get_instance( + charge_rate_multiplier=1.1, + always_allow_discharge_limit=0.90, + max_capacity=capacity_wh, + ) + logic = NextLogic(timezone=TZ, interval_minutes=interval_min) + logic.set_calculation_parameters(CalculationParameters( + max_charging_from_grid_limit=0.79, + min_price_difference=0.05, + min_price_difference_rel=0.2, + max_capacity=capacity_wh, + peak_shaving=PeakShavingConfig( + enabled=True, mode='time', + allow_full_battery_after=allow_full_after, + ), + )) + + stored_wh = float(initial_soc_wh) + totals = {'charged_wh': 0.0, 'feed_in_wh': 0.0, 'curtailed_wh': 0.0} + rows = [] + + for s in range(n_slots): + minutes = s * interval_min + ts = BASE_DATE + datetime.timedelta(minutes=minutes) + prod_w = float(prod_actual_w[s]) + cons_w = float(cons_actual_w[s]) + + fc_prod_wh = np.asarray(forecast_prod_w[s:], dtype=float) * interval_h + fc_cons_wh = np.asarray(forecast_cons_w[s:], dtype=float) * interval_h + free_cap = capacity_wh - stored_wh + + floor_w, solar_cap_w = 0, -1 + if solar_cap_active: + floor_w, solar_cap_w = compute_solar_limit( + fc_prod_wh, fc_cons_wh, feed_in_limit_w, interval_h, + free_cap, capacity_wh, headroom=headroom) + + time_cap_w = -1 + if time_active and fc_prod_wh[0] > 0: + # Mirror the relevant _apply_peak_shaving skip: unlimited in the + # always_allow_discharge region (high SoC). + if not common.is_discharge_always_allowed_capacity(stored_wh): + calc_input = CalculationInput( + production=fc_prod_wh, + consumption=fc_cons_wh, + prices={}, + stored_energy=stored_wh, + stored_usable_energy=max(stored_wh - 0.05 * capacity_wh, 0), + free_capacity=free_cap, + ) + time_cap_w = logic._calculate_peak_shaving_charge_limit( + calc_input, ts) + + final_w = merge_limits(floor_w, [time_cap_w, solar_cap_w]) + if final_w > 0: + final_w = common.enforce_min_pv_charge_rate(final_w) + + charge_w, feed_w, curt_w, stored_wh_new = apply_slot( + prod_w, cons_w, final_w, stored_wh, capacity_wh, + feed_in_limit_w, interval_h) + + totals['charged_wh'] += charge_w * interval_h + totals['feed_in_wh'] += feed_w * interval_h + totals['curtailed_wh'] += curt_w * interval_h + + if collect_rows: + rows.append({ + 'ts': ts, 'prod_w': prod_w, 'cons_w': cons_w, + 'floor_w': int(floor_w), 'time_cap_w': time_cap_w, + 'solar_cap_w': solar_cap_w, 'final_w': final_w, + 'charge_w': charge_w, 'feed_w': feed_w, 'curt_w': curt_w, + 'soc_pct': stored_wh / capacity_wh * 100, + }) + stored_wh = stored_wh_new + + totals['end_soc_pct'] = stored_wh / capacity_wh * 100 + totals['rows'] = rows + return totals + + +def clip_potential_wh(prod_w, cons_w, feed_in_limit_w, interval_h): + """Energy above the feed-in limit if no battery absorbed anything (Wh).""" + surplus = np.clip(np.asarray(prod_w) - np.asarray(cons_w), 0, None) + return float(np.sum(np.clip(surplus - feed_in_limit_w, 0, None))) * interval_h + + +def fmt_cap(v): + return ' -' if v < 0 else f'{v:>4d}' + + +def print_rows(rows): + print(f" {'Time':>5} {'PV W':>5} {'Floor':>5} {'TimeCap':>7} " + f"{'SolarCap':>8} {'Final':>6} {'Chg W':>5} {'Feed W':>6} " + f"{'Curt W':>6} {'SoC%':>5}") + print(' ' + '-' * 78) + for r in rows: + if r['prod_w'] <= 0 and r['ts'].hour not in (4, 21): + continue # keep output compact: skip most night slots + final = 'unlim' if r['final_w'] < 0 else str(r['final_w']) + print(f" {r['ts'].strftime('%H:%M')} {r['prod_w']:>5.0f} " + f"{r['floor_w']:>5d} {fmt_cap(r['time_cap_w']):>7} " + f"{fmt_cap(r['solar_cap_w']):>8} {final:>6} " + f"{r['charge_w']:>5.0f} {r['feed_w']:>6.0f} " + f"{r['curt_w']:>6.0f} {r['soc_pct']:>5.1f}") + print(' ' + '-' * 78) + + +def print_summary(title, results, potential_wh): + print(f" {title}") + print(f" {'Trace':<34} {'Charged':>9} {'Feed-in':>9} {'Curtailed':>10} " + f"{'EndSoC':>7} {'ClipRecov':>10}") + print(' ' + '-' * 84) + for name, res in results: + recov = '' + if potential_wh > 0: + recov_pct = (potential_wh - res['curtailed_wh']) / potential_wh * 100 + recov = f'{recov_pct:>9.1f}%' + print(f" {name:<34} {res['charged_wh']/1000:>7.2f}kWh " + f"{res['feed_in_wh']/1000:>7.2f}kWh {res['curtailed_wh']/1000:>8.2f}kWh " + f"{res['end_soc_pct']:>6.1f}% {recov:>10}") + print(' ' + '-' * 84) + print(f" Clip potential (no battery absorption): {potential_wh/1000:.2f} kWh") + print() + + +# --------------------------------------------------------------------------- +# Scenarios +# --------------------------------------------------------------------------- +def scenario_reference(): + cons = np.full(24, CONSUMPTION_W, dtype=float) + cap = 10_000 + potential = clip_potential_wh(PROFILE_SOUTH_W, cons, FEED_IN_LIMIT_W, 1.0) + + base = run_day(PROFILE_SOUTH_W, cons, cap) + legacy = run_day(PROFILE_SOUTH_W, cons, cap, time_active=True) + solar = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + collect_rows=True) + both = run_day(PROFILE_SOUTH_W, cons, cap, time_active=True, + solar_cap_active=True, collect_rows=True) + + print('=' * 88) + print(' SCENARIO 1 -- Reference: 10 kWp south, clear day, limit 6000 W, ' + '10 kWh battery, 400 W load') + print('=' * 88) + print_summary('Full-day comparison:', [ + ('baseline (all rules off)', base), + ('time_active only (legacy)', legacy), + ('solar_cap_active only', solar), + ('time_active + solar_cap_active', both), + ], potential) + print(' Slot detail, solar_cap_active only:') + print_rows(solar['rows']) + print() + print(' Slot detail, time_active + solar_cap_active ' + '(floor overrides time cap in clip slots):') + print_rows(both['rows']) + print() + return {'baseline': base, 'legacy': legacy, 'solar': solar, 'both': both, + 'potential': potential} + + +def scenario_east_west(): + cons = np.full(24, CONSUMPTION_W, dtype=float) + cap = 10_000 + potential = clip_potential_wh(PROFILE_EAST_WEST_W, cons, FEED_IN_LIMIT_W, 1.0) + base = run_day(PROFILE_EAST_WEST_W, cons, cap) + solar = run_day(PROFILE_EAST_WEST_W, cons, cap, solar_cap_active=True) + print('=' * 88) + print(' SCENARIO 2 -- East-west 10 kWp (peak 5.6 kW < limit): rule must ' + 'stay inert') + print('=' * 88) + print_summary('No clipping expected; solar rule must not change anything:', [ + ('baseline', base), + ('solar_cap_active only', solar), + ], potential) + identical = abs(base['curtailed_wh'] - solar['curtailed_wh']) < 1e-6 and \ + abs(base['end_soc_pct'] - solar['end_soc_pct']) < 1e-6 + print(f" Check: solar trace identical to baseline: {identical}") + print() + + +def scenario_small_battery(): + cons = np.full(24, CONSUMPTION_W, dtype=float) + cap = 5_000 + potential = clip_potential_wh(PROFILE_SOUTH_W, cons, FEED_IN_LIMIT_W, 1.0) + base = run_day(PROFILE_SOUTH_W, cons, cap) + solar = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + collect_rows=True) + print('=' * 88) + print(' SCENARIO 3 -- Small battery 5 kWh: clip energy exceeds free ' + 'capacity (scarcity)') + print('=' * 88) + # Theoretical max recovery = free capacity when the clip window starts + # (overnight house load drains the battery below the day-start SoC). + first_clip_row = next(r for r in solar['rows'] if r['floor_w'] > 0) + free_at_window = cap * (1 - first_clip_row['soc_pct'] / 100) + print_summary( + f'Free capacity at window start: {free_at_window/1000:.2f} kWh ' + f'< clip potential {potential/1000:.2f} kWh:', [ + ('baseline', base), + ('solar_cap_active only', solar), + ], potential) + recovered = potential - solar['curtailed_wh'] + print(f" Recovered clip energy: {recovered/1000:.2f} kWh " + f"(theoretical max = free capacity at window start = " + f"{free_at_window/1000:.2f} kWh)") + print(' Slot detail (cap == floor inside window once capacity is scarce):') + print_rows(solar['rows']) + print() + + +def scenario_forecast_error(): + cons = np.full(24, CONSUMPTION_W, dtype=float) + cap = 10_000 + forecast = PROFILE_SOUTH_W * 0.85 # forecast 15% below actual + potential = clip_potential_wh(PROFILE_SOUTH_W, cons, FEED_IN_LIMIT_W, 1.0) + base = run_day(PROFILE_SOUTH_W, cons, cap) + h10 = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + forecast_prod_w=forecast, headroom=1.0) + h12 = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + forecast_prod_w=forecast, headroom=1.2) + h15 = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + forecast_prod_w=forecast, headroom=1.5) + perfect = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True) + print('=' * 88) + print(' SCENARIO 4 -- Forecast error: forecast = 85% of actual ' + '(underestimates clipping)') + print('=' * 88) + print_summary('Effect of feed_in_limit_headroom on the reservation:', [ + ('baseline', base), + ('solar, headroom 1.0', h10), + ('solar, headroom 1.2', h12), + ('solar, headroom 1.5', h15), + ('solar, perfect forecast (ref)', perfect), + ], potential) + + +def scenario_consumption_spike(): + cons = np.full(24, CONSUMPTION_W, dtype=float) + cons[12:14] = 2_400 # cooking 12:00-14:00 + cap = 10_000 + potential = clip_potential_wh(PROFILE_SOUTH_W, cons, FEED_IN_LIMIT_W, 1.0) + base = run_day(PROFILE_SOUTH_W, cons, cap) + legacy = run_day(PROFILE_SOUTH_W, cons, cap, time_active=True) + both = run_day(PROFILE_SOUTH_W, cons, cap, time_active=True, + solar_cap_active=True) + print('=' * 88) + print(' SCENARIO 5 -- Midday consumption spike (2.4 kW, 12-14h) reduces ' + 'clipping') + print('=' * 88) + print_summary('Self-consumption already absorbs part of the peak:', [ + ('baseline', base), + ('time_active only (legacy)', legacy), + ('time_active + solar_cap_active', both), + ], potential) + + +def scenario_15min(): + # Linear power interpolation of the hourly profile to 15-min slots. + hours = np.arange(24) + slots = np.arange(0, 24, 0.25) + prod15 = np.interp(slots, hours, PROFILE_SOUTH_W) + cons15 = np.full(len(slots), CONSUMPTION_W, dtype=float) + cap = 10_000 + potential = clip_potential_wh(prod15, cons15, FEED_IN_LIMIT_W, 0.25) + base = run_day(prod15, cons15, cap, interval_min=15) + solar = run_day(prod15, cons15, cap, solar_cap_active=True, + interval_min=15) + print('=' * 88) + print(' SCENARIO 6 -- 15-minute interval resolution ' + '(same reference day, interpolated)') + print('=' * 88) + print_summary('Consistency check vs. hourly resolution:', [ + ('baseline (15 min)', base), + ('solar_cap_active (15 min)', solar), + ], potential) + + +if __name__ == '__main__': + scenario_reference() + scenario_east_west() + scenario_small_battery() + scenario_forecast_error() + scenario_consumption_spike() + scenario_15min() From e7809985ac62d19a451592dba135e53a3643db9a Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 09:11:28 +0000 Subject: [PATCH 02/13] feat: add 125% forecast-error scenario with mitigation plan Add scenario 4b (actual production = 125% of forecast) to the solar feed-in limit simulation. The forecast then sees only 1.36 of 7.50 kWh clip potential and misses entire clip slots, so multiplying predicted clip energy cannot recover it. Two mitigations are implemented and compared: - headroom_on='surplus': apply the headroom factor to the forecast surplus before the clip computation (fixes the reservation, finds clip slots the raw forecast misses) - live_floor: derive the slot-0 charge floor additionally from the actual current production (fixes absorption inside the window) Result: each mitigation alone recovers only 32-39%, the combination 94.7% (perfect forecast: 100%). The evaluation doc now mandates the live floor for v1 integration and fixes the feed_in_limit_headroom semantics to apply to the forecast surplus. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- docs/development/solar-limit-evaluation.md | 71 +++++++++++++++---- scripts/simulate_solar_limit_day.py | 81 +++++++++++++++++++--- 2 files changed, 131 insertions(+), 21 deletions(-) diff --git a/docs/development/solar-limit-evaluation.md b/docs/development/solar-limit-evaluation.md index 32ed1c7d..07cebf78 100644 --- a/docs/development/solar-limit-evaluation.md +++ b/docs/development/solar-limit-evaluation.md @@ -170,13 +170,56 @@ Einspeisung läuft unterhalb der Grenze weiter), im Fenster gilt `cap == floor`. Erkenntnisse: (a) Der Algorithmus ist deutlich forecast-sensitiv — eine 15-%-Unterschätzung der Produktion unterschätzt die Kappung überproportional (Kappung -ist die "Spitze" der Kurve). (b) `headroom` verbessert die Reservierung nur moderat -(+7 Punkte bei 1.2), weil im Fenster auch der **Floor** aus dem zu niedrigen Forecast -berechnet wird. Empfehlung: Default `1.0`, dokumentiert `1.2` für konservative Nutzer; -als **Verbesserungsoption für die Integration**: den Floor in Slot 0 aus der -**Live-Messung** des Wechselrichters statt aus dem Forecast speisen (batcontrol -evaluiert alle ~3 Minuten — die aktuelle Produktion ist bekannt). Das würde die -Floor-Hälfte des Fehlers eliminieren; die Simulation modelliert das bewusst noch nicht. +ist die "Spitze" der Kurve). (b) `headroom` auf die Kappungsenergie verbessert die +Reservierung nur moderat (+7 Punkte bei 1.2), weil im Fenster auch der **Floor** aus +dem zu niedrigen Forecast berechnet wird. Der daraus abgeleitete Maßnahmenplan wird in +Szenario 4b entwickelt und quantifiziert. + +### Szenario 4b — Schwerer Prognosefehler (Ist = 125 % der Prognose) + +Die Prognose sieht nur **1,36 kWh** Kappungspotenzial statt real 7,50 kWh und erkennt +ganze Kappungs-Slots (11:00, 14:00) **gar nicht** als solche — ein Multiplikator auf die +prognostizierte Kappungsenergie kann das strukturell nicht reparieren. Zwei +Gegenmaßnahmen wurden implementiert und verglichen: + +- **headroom auf den Überschuss** (`headroom_on='surplus'`): Der Faktor wird vor der + Kappungsberechnung auf den prognostizierten Überschuss angewandt. Das rekonstruiert + eine unterschätzte Produktionskurve und findet auch übersehene Kappungs-Slots — + repariert also die **Reservierung**. +- **Live-Floor**: Der Floor in Slot 0 wird zusätzlich aus der **Ist-Messung** des + Wechselrichters gebildet (`max(floor_forecast, ist_leistung - grenze)`). batcontrol + evaluiert alle ~3 Minuten und kennt die aktuelle Produktion — repariert also die + **Absorption im Fenster**. + +| Trace | Abgeregelt | Rückgewinnung | +|------------------------------------|-----------:|--------------:| +| Baseline | 7,50 kWh | 0 % | +| headroom 1.25 auf Kappung | 4,60 kWh | 38,7 % | +| headroom 1.25 auf Überschuss | 5,12 kWh | 31,8 % | +| nur Live-Floor | 4,94 kWh | 34,1 % | +| **Überschuss 1.25 + Live-Floor** | **0,40 kWh** | **94,7 %** | +| perfekter Forecast (Referenz) | 0,00 kWh | 100 % | + +Zentrales Ergebnis: **Die Einzelmaßnahmen bringen jeweils nur ~32–39 %, die Kombination +94,7 %.** Die Maßnahmen adressieren komplementäre Fehler: Ohne Live-Floor ist die +perfekte Reservierung wertlos (der forecast-basierte Cap im Fenster blockt das Laden, +während real gekappt wird — deshalb ist "Überschuss allein" sogar leicht schlechter als +"Kappung allein"); ohne Überschuss-headroom ist der Akku bei Fensterbeginn schon +vorgefüllt. + +**Plan für Prognosefehler (verbindlich für die Integration):** + +1. **Live-Floor ist Pflichtbestandteil von v1**, keine Option: Slot-0-Floor = + `max(forecast_floor, ist_produktion - ist_verbrauch - feed_in_limit_w)`. Die + benötigten Ist-Werte liegen in `core.py` bereits vor. +2. **`feed_in_limit_headroom` wirkt auf den prognostizierten Überschuss**, nicht auf + die Kappungsenergie (Semantik-Festlegung für den Config-Key). Default `1.0`; + dokumentierte Empfehlung `1.2–1.3` — deckt Unterschätzungen bis ~25 % weitgehend ab. +3. Nebenwirkung dokumentieren: Überschuss-headroom kann an Tagen knapp unterhalb der + Grenze eine unnötige (harmlose) Reservierung auslösen — Kosten ist etwas später + voller Akku, nie Energieverlust. +4. Das 15-Minuten-Raster (`time_resolution_minutes: 15`) reduziert den systematischen + Anteil des Fehlers zusätzlich (Szenario 6). ### Szenario 5 — Mittags-Verbrauchsspitze (2,4 kW, 12–14 Uhr) @@ -202,10 +245,11 @@ Der Algorithmus erfüllt die Anforderungen: 4. **Er ist neutral, wenn er nichts zu tun hat** (Ost/West-Szenario) und per `feed_in_limit_w: 0` bzw. `solar_cap_active: false` vollständig abschaltbar. -Bekannte Grenzen: Forecast-Sensitivität (siehe Szenario 4; Mitigation: Live-Messung -für den Floor, 15-Minuten-Raster, headroom) und Stundenmittel vs. Momentanleistung -(ein Slot mit Mittel knapp unter der Grenze kann real kurzzeitig kappen — nicht -modelliert, durch headroom teilweise abgedeckt). +Bekannte Grenzen: Forecast-Sensitivität (Szenarien 4/4b; Mitigation: Live-Floor als +Pflichtbestandteil + headroom auf den Überschuss, zusammen 94,7 % Rückgewinnung selbst +bei 25 % Unterschätzung) und Stundenmittel vs. Momentanleistung (ein Slot mit Mittel +knapp unter der Grenze kann real kurzzeitig kappen — durch Live-Floor und headroom +weitgehend abgedeckt). ## Integrations-Roadmap (Folgeschritt) @@ -222,7 +266,10 @@ modelliert, durch headroom teilweise abgedeckt). `allow_discharge == False` (dort lädt der Inverter Überschuss ohnehin ungebremst). Merge nach der Prioritätsregel oben; `enforce_min_pv_charge_rate` einmalig auf den final gemergten Wert. Helper `_remaining_interval_hours()` extrahieren - (anteiliger Slot 0, vgl. Grid-Recharge-Block). + (anteiliger Slot 0, vgl. Grid-Recharge-Block). **Live-Floor (Pflicht, Szenario 4b)**: + aktuelle Produktion/Verbrauch aus `core.py` in die Floor-Berechnung einspeisen; + `feed_in_limit_headroom` wirkt auf den prognostizierten Überschuss + (`headroom_on='surplus'` im Simulationsskript). 4. `core.py`: Startup-Warnung wenn `feed_in_limit_w > 0` und `max_pv_charge_rate > 0`. 5. Tests: `tests/batcontrol/logic/test_solar_limit.py` (pure Funktionen) + Integrationsfälle in `test_peak_shaving.py` (Floor überstimmt Cap inkl. Cap 0, Reservierung, Knappheit diff --git a/scripts/simulate_solar_limit_day.py b/scripts/simulate_solar_limit_day.py index 893fbb54..ee876518 100644 --- a/scripts/simulate_solar_limit_day.py +++ b/scripts/simulate_solar_limit_day.py @@ -79,7 +79,7 @@ # --------------------------------------------------------------------------- def compute_solar_limit(production_wh, consumption_wh, feed_in_limit_w, interval_h, free_capacity_wh, max_capacity_wh, - headroom=1.0, slot0_hours=None): + headroom=1.0, slot0_hours=None, headroom_on='clip'): """Compute the solar-cap rule output for the current slot. Args: @@ -90,11 +90,18 @@ def compute_solar_limit(production_wh, consumption_wh, feed_in_limit_w, interval_h: slot length in hours (0.25 or 1.0). free_capacity_wh: battery free capacity (Wh). max_capacity_wh: battery max capacity (Wh). - headroom: safety factor >= 1.0 on predicted clip energy for - RESERVATION sizing only (hourly averages understate - instantaneous clipping). Never applied to the floor. + headroom: safety factor >= 1.0 for RESERVATION sizing only + (forecasts understate clipping). Never applied to + the floor. slot0_hours: remaining hours in the current slot (partial slot). Defaults to interval_h. + headroom_on: 'clip' - multiply predicted clip energy (weak + against underestimated production: slots + forecast below the limit stay invisible) + 'surplus' - multiply predicted surplus BEFORE the + clip computation (reconstructs an + underestimated production curve and also + finds clip slots the raw forecast misses) Returns: (floor_w, cap_w): @@ -129,10 +136,16 @@ def compute_solar_limit(production_wh, consumption_wh, feed_in_limit_w, feed_allow_wh = feed_in_limit_w * slot_h clip_raw_wh = np.clip(surplus_wh - feed_allow_wh, 0, None) # Headroom only inflates the reservation; a slot can never clip more - # than its surplus. - clip_wh = np.minimum(surplus_wh, clip_raw_wh * headroom) + # than its (headroom-adjusted) surplus. The floor always uses the raw + # clip so we never force absorbing exportable energy. + if headroom_on == 'surplus': + surplus_hr_wh = surplus_wh * headroom + clip_wh = np.minimum(surplus_hr_wh, + np.clip(surplus_hr_wh - feed_allow_wh, 0, None)) + else: + clip_wh = np.minimum(surplus_wh, clip_raw_wh * headroom) - clip_slots = np.nonzero(clip_raw_wh > 0)[0] + clip_slots = np.nonzero(clip_wh > 0)[0] if len(clip_slots) == 0: return 0, -1 @@ -217,9 +230,17 @@ def run_day(prod_actual_w, cons_actual_w, capacity_wh, time_active=False, solar_cap_active=False, forecast_prod_w=None, forecast_cons_w=None, feed_in_limit_w=FEED_IN_LIMIT_W, headroom=1.0, + headroom_on='clip', live_floor=False, interval_min=60, allow_full_after=ALLOW_FULL_AFTER, initial_soc_wh=None, collect_rows=False): - """Simulate one day and return metrics (and per-slot rows on request).""" + """Simulate one day and return metrics (and per-slot rows on request). + + live_floor: derive the slot-0 floor additionally from the ACTUAL current + production (simulates using the live inverter measurement instead of the + forecast -- batcontrol re-evaluates every ~3 minutes and knows the + current production). Protects the floor against forecast errors; the + reservation still depends on the forecast. + """ interval_h = interval_min / 60.0 n_slots = len(prod_actual_w) if forecast_prod_w is None: @@ -266,7 +287,11 @@ def run_day(prod_actual_w, cons_actual_w, capacity_wh, if solar_cap_active: floor_w, solar_cap_w = compute_solar_limit( fc_prod_wh, fc_cons_wh, feed_in_limit_w, interval_h, - free_cap, capacity_wh, headroom=headroom) + free_cap, capacity_wh, headroom=headroom, + headroom_on=headroom_on) + if live_floor: + live_clip_w = max(0.0, (prod_w - cons_w) - feed_in_limit_w) + floor_w = max(floor_w, live_clip_w) time_cap_w = -1 if time_active and fc_prod_wh[0] > 0: @@ -468,6 +493,43 @@ def scenario_forecast_error(): ], potential) +def scenario_forecast_error_125(): + cons = np.full(24, CONSUMPTION_W, dtype=float) + cap = 10_000 + forecast = PROFILE_SOUTH_W / 1.25 # actual = 125% of forecast + potential = clip_potential_wh(PROFILE_SOUTH_W, cons, FEED_IN_LIMIT_W, 1.0) + fc_potential = clip_potential_wh(forecast, cons, FEED_IN_LIMIT_W, 1.0) + base = run_day(PROFILE_SOUTH_W, cons, cap) + clip_hr = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + forecast_prod_w=forecast, headroom=1.25, + headroom_on='clip') + surp_hr = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + forecast_prod_w=forecast, headroom=1.25, + headroom_on='surplus') + live_only = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + forecast_prod_w=forecast, live_floor=True) + combined = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + forecast_prod_w=forecast, headroom=1.25, + headroom_on='surplus', live_floor=True) + perfect = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True) + print('=' * 88) + print(' SCENARIO 4b -- Severe forecast error: actual = 125% of forecast') + print('=' * 88) + print(f' Forecast sees only {fc_potential/1000:.2f} kWh clip potential ' + f'(actual: {potential/1000:.2f} kWh) and') + print(' misses entire clip slots -- multiplying the predicted CLIP ' + 'energy cannot fix that.') + print() + print_summary('Mitigation comparison (headroom target vs. live floor):', [ + ('baseline', base), + ('solar, headroom 1.25 on clip', clip_hr), + ('solar, headroom 1.25 on surplus', surp_hr), + ('solar, live floor only', live_only), + ('solar, surplus 1.25 + live floor', combined), + ('solar, perfect forecast (ref)', perfect), + ], potential) + + def scenario_consumption_spike(): cons = np.full(24, CONSUMPTION_W, dtype=float) cons[12:14] = 2_400 # cooking 12:00-14:00 @@ -514,5 +576,6 @@ def scenario_15min(): scenario_east_west() scenario_small_battery() scenario_forecast_error() + scenario_forecast_error_125() scenario_consumption_spike() scenario_15min() From 369e5ff81e4d5e1cb99a2f68b7b6185488e18c06 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 10:56:14 +0000 Subject: [PATCH 03/13] refactor: replace live-floor mitigation with forecast-only headroom floor batcontrol has no live production measurement, so the live-floor mitigation from the previous commit is not implementable today. Replace it with a forecast-only alternative: floor_source='headroom' computes the in-window charge floor from the headroom-adjusted clip instead of the raw forecast. With a greedy-charging inverter the floor only raises the allowed cap, so it permits (never forces) absorbing more than the raw forecast predicts. Scenario 4b now quantifies the resulting trade-off in both directions: surplus headroom 1.25 + headroom floor recovers 94.7% at a +25% forecast error but costs 2.8 kWh when the forecast is correct (capacity-scarce days: exportable energy displaces clip energy 1:1); headroom 1.1 is the robust compromise (44.9% / 94.5%). The evaluation doc recommends default 1.0, suggested 1.1, and lists a live measurement as a future option requiring a new data path. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- docs/development/solar-limit-evaluation.md | 102 ++++++++++++--------- scripts/simulate_solar_limit_day.py | 64 ++++++++----- 2 files changed, 98 insertions(+), 68 deletions(-) diff --git a/docs/development/solar-limit-evaluation.md b/docs/development/solar-limit-evaluation.md index 07cebf78..28f2fec0 100644 --- a/docs/development/solar-limit-evaluation.md +++ b/docs/development/solar-limit-evaluation.md @@ -179,47 +179,59 @@ Szenario 4b entwickelt und quantifiziert. Die Prognose sieht nur **1,36 kWh** Kappungspotenzial statt real 7,50 kWh und erkennt ganze Kappungs-Slots (11:00, 14:00) **gar nicht** als solche — ein Multiplikator auf die -prognostizierte Kappungsenergie kann das strukturell nicht reparieren. Zwei -Gegenmaßnahmen wurden implementiert und verglichen: +prognostizierte Kappungsenergie kann das strukturell nicht reparieren. Da batcontrol +**keine Live-Messung der aktuellen Produktion** hat, stehen nur prognosebasierte +Gegenmaßnahmen zur Verfügung; zwei wurden implementiert und verglichen: - **headroom auf den Überschuss** (`headroom_on='surplus'`): Der Faktor wird vor der Kappungsberechnung auf den prognostizierten Überschuss angewandt. Das rekonstruiert eine unterschätzte Produktionskurve und findet auch übersehene Kappungs-Slots — - repariert also die **Reservierung**. -- **Live-Floor**: Der Floor in Slot 0 wird zusätzlich aus der **Ist-Messung** des - Wechselrichters gebildet (`max(floor_forecast, ist_leistung - grenze)`). batcontrol - evaluiert alle ~3 Minuten und kennt die aktuelle Produktion — repariert also die - **Absorption im Fenster**. - -| Trace | Abgeregelt | Rückgewinnung | -|------------------------------------|-----------:|--------------:| -| Baseline | 7,50 kWh | 0 % | -| headroom 1.25 auf Kappung | 4,60 kWh | 38,7 % | -| headroom 1.25 auf Überschuss | 5,12 kWh | 31,8 % | -| nur Live-Floor | 4,94 kWh | 34,1 % | -| **Überschuss 1.25 + Live-Floor** | **0,40 kWh** | **94,7 %** | -| perfekter Forecast (Referenz) | 0,00 kWh | 100 % | - -Zentrales Ergebnis: **Die Einzelmaßnahmen bringen jeweils nur ~32–39 %, die Kombination -94,7 %.** Die Maßnahmen adressieren komplementäre Fehler: Ohne Live-Floor ist die -perfekte Reservierung wertlos (der forecast-basierte Cap im Fenster blockt das Laden, -während real gekappt wird — deshalb ist "Überschuss allein" sogar leicht schlechter als -"Kappung allein"); ohne Überschuss-headroom ist der Akku bei Fensterbeginn schon -vorgefüllt. - -**Plan für Prognosefehler (verbindlich für die Integration):** - -1. **Live-Floor ist Pflichtbestandteil von v1**, keine Option: Slot-0-Floor = - `max(forecast_floor, ist_produktion - ist_verbrauch - feed_in_limit_w)`. Die - benötigten Ist-Werte liegen in `core.py` bereits vor. -2. **`feed_in_limit_headroom` wirkt auf den prognostizierten Überschuss**, nicht auf - die Kappungsenergie (Semantik-Festlegung für den Config-Key). Default `1.0`; - dokumentierte Empfehlung `1.2–1.3` — deckt Unterschätzungen bis ~25 % weitgehend ab. -3. Nebenwirkung dokumentieren: Überschuss-headroom kann an Tagen knapp unterhalb der - Grenze eine unnötige (harmlose) Reservierung auslösen — Kosten ist etwas später - voller Akku, nie Energieverlust. -4. Das 15-Minuten-Raster (`time_resolution_minutes: 15`) reduziert den systematischen + repariert die **Reservierung** vor dem Fenster. +- **headroom-Floor** (`floor_source='headroom'`): Der Floor im Fenster wird aus der + headroom-korrigierten statt der Roh-Kappung berechnet. Bei greedy ladenden Invertern + ist der Floor ohnehin nur eine **Erlaubnis** (der angewandte Cap wird angehoben, der + Inverter lädt `min(Ist-Überschuss, Cap)`) — es wird nie Ladung erzwungen, die es + physisch nicht gibt. Repariert die **Absorption im Fenster**. + +Ergebnis unter beiden Bedingungen (Ist = 125 % der Prognose bzw. Prognose korrekt): + +| Einstellung | Rückgew. bei +25 % Fehler | Rückgew. bei korrekter Prognose | +|------------------------------------------|--------------------------:|--------------------------------:| +| headroom 1.25 auf Kappung (Roh-Floor) | 38,7 % | — | +| headroom 1.25 auf Überschuss (Roh-Floor) | 31,8 % | — | +| Überschuss 1.1 + headroom-Floor | 44,9 % | 94,5 % (Verlust 0,41 kWh) | +| Überschuss 1.25 + headroom-Floor | **94,7 %** | 62,7 % (Verlust 2,80 kWh) | +| Neutral (headroom 1.0) | 31,8–38,7 % | **100 %** | + +Zentrale Erkenntnisse: + +1. Beide Maßnahmen sind **nur zusammen** wirksam: Ohne headroom-Floor ist die perfekte + Reservierung wertlos (der forecast-basierte Cap blockt das Laden, während real + gekappt wird — deshalb ist "Überschuss allein" sogar leicht schlechter als "Kappung + allein"); ohne Überschuss-headroom ist der Akku bei Fensterbeginn schon vorgefüllt. +2. **Ohne Live-Messung ist der headroom ein echter Trade-off**: Er muss ungefähr zum + typischen Prognosefehler passen. Ein zu hoher Wert (1.25 bei korrekter Prognose) + lädt im Fenster einspeisbare Energie und verdrängt an kapazitätsknappen Tagen + Kappungsenergie 1:1 (2,8 kWh Verlust). Ein zu niedriger Wert lässt Kappung liegen. +3. **1.1 ist der robuste Kompromiss**: kostet bei korrekter Prognose nur 0,41 kWh und + verbessert den Fehlerfall bereits deutlich. + +**Plan für Prognosefehler (Festlegung für die Integration, rein prognosebasiert):** + +1. **`feed_in_limit_headroom` wirkt auf den prognostizierten Überschuss** und der + **Floor wird aus der headroom-korrigierten Kappung** berechnet (eine gemeinsame + Stellschraube, kein zweiter Config-Key). Default `1.0` (neutral, verlustfrei bei + korrekter Prognose); dokumentierte Empfehlung `1.1`, bei bekannt schlechter + Prognosequelle bis `1.25`. +2. Nebenwirkungen dokumentieren: headroom > 1 kann an Tagen knapp unterhalb der Grenze + eine unnötige Reservierung auslösen (Akku später voll, kein Energieverlust) und an + kapazitätsknappen Kappungstagen bei korrekter Prognose einen kleinen Teil der + Kappung verdrängen (quantifiziert oben). +3. Das 15-Minuten-Raster (`time_resolution_minutes: 15`) reduziert den systematischen Anteil des Fehlers zusätzlich (Szenario 6). +4. **Zukunftsoption** (nicht v1, erfordert neuen Datenpfad): eine Live-Messung der + aktuellen Produktion/Einspeisung würde den Floor prognoseunabhängig machen und den + Trade-off auflösen — batcontrol erfasst diese Werte derzeit nicht. ### Szenario 5 — Mittags-Verbrauchsspitze (2,4 kW, 12–14 Uhr) @@ -245,11 +257,11 @@ Der Algorithmus erfüllt die Anforderungen: 4. **Er ist neutral, wenn er nichts zu tun hat** (Ost/West-Szenario) und per `feed_in_limit_w: 0` bzw. `solar_cap_active: false` vollständig abschaltbar. -Bekannte Grenzen: Forecast-Sensitivität (Szenarien 4/4b; Mitigation: Live-Floor als -Pflichtbestandteil + headroom auf den Überschuss, zusammen 94,7 % Rückgewinnung selbst -bei 25 % Unterschätzung) und Stundenmittel vs. Momentanleistung (ein Slot mit Mittel -knapp unter der Grenze kann real kurzzeitig kappen — durch Live-Floor und headroom -weitgehend abgedeckt). +Bekannte Grenzen: Forecast-Sensitivität (Szenarien 4/4b) — ohne Live-Messung der +aktuellen Produktion (derzeit nicht Bestandteil von batcontrol) bleibt der headroom ein +Trade-off, dessen Wert zum typischen Prognosefehler passen muss (Empfehlung 1.1); +Stundenmittel vs. Momentanleistung (ein Slot mit Mittel knapp unter der Grenze kann +real kurzzeitig kappen — durch headroom teilweise abgedeckt). ## Integrations-Roadmap (Folgeschritt) @@ -266,10 +278,10 @@ weitgehend abgedeckt). `allow_discharge == False` (dort lädt der Inverter Überschuss ohnehin ungebremst). Merge nach der Prioritätsregel oben; `enforce_min_pv_charge_rate` einmalig auf den final gemergten Wert. Helper `_remaining_interval_hours()` extrahieren - (anteiliger Slot 0, vgl. Grid-Recharge-Block). **Live-Floor (Pflicht, Szenario 4b)**: - aktuelle Produktion/Verbrauch aus `core.py` in die Floor-Berechnung einspeisen; - `feed_in_limit_headroom` wirkt auf den prognostizierten Überschuss - (`headroom_on='surplus'` im Simulationsskript). + (anteiliger Slot 0, vgl. Grid-Recharge-Block). `feed_in_limit_headroom` wirkt auf + den prognostizierten Überschuss und der Floor nutzt die headroom-korrigierte + Kappung (`headroom_on='surplus'`, `floor_source='headroom'` im Simulationsskript; + Trade-off siehe Szenario 4b). 4. `core.py`: Startup-Warnung wenn `feed_in_limit_w > 0` und `max_pv_charge_rate > 0`. 5. Tests: `tests/batcontrol/logic/test_solar_limit.py` (pure Funktionen) + Integrationsfälle in `test_peak_shaving.py` (Floor überstimmt Cap inkl. Cap 0, Reservierung, Knappheit diff --git a/scripts/simulate_solar_limit_day.py b/scripts/simulate_solar_limit_day.py index ee876518..294183b0 100644 --- a/scripts/simulate_solar_limit_day.py +++ b/scripts/simulate_solar_limit_day.py @@ -79,7 +79,8 @@ # --------------------------------------------------------------------------- def compute_solar_limit(production_wh, consumption_wh, feed_in_limit_w, interval_h, free_capacity_wh, max_capacity_wh, - headroom=1.0, slot0_hours=None, headroom_on='clip'): + headroom=1.0, slot0_hours=None, headroom_on='clip', + floor_source='raw'): """Compute the solar-cap rule output for the current slot. Args: @@ -102,6 +103,15 @@ def compute_solar_limit(production_wh, consumption_wh, feed_in_limit_w, clip computation (reconstructs an underestimated production curve and also finds clip slots the raw forecast misses) + floor_source: 'raw' - floor from the raw forecast clip + 'headroom' - floor from the headroom-adjusted clip. + With a greedy-charging inverter the + floor only RAISES the allowed cap, so + this permits (never forces) absorbing + more than the raw forecast predicts; + cost: when the forecast is correct, + some exportable surplus is charged + instead of fed in (no energy loss). Returns: (floor_w, cap_w): @@ -161,9 +171,12 @@ def compute_solar_limit(production_wh, consumption_wh, feed_in_limit_w, return 0, int(allowed_wh / hours_before) # -- Case B: inside a clip slot -> floor + capacity-preserving cap ---- # - # Floor from the RAW clip (no headroom): never force absorbing energy - # that could legally be exported. - floor_w = clip_raw_wh[0] / slot0_hours + # Default floor from the RAW clip (no headroom): never lift the cap + # beyond what the raw forecast predicts as curtailed. + if floor_source == 'headroom': + floor_w = clip_wh[0] / slot0_hours + else: + floor_w = clip_raw_wh[0] / slot0_hours total_surplus_wh = float(np.sum(surplus_wh)) if total_surplus_wh <= free_capacity_wh: return int(floor_w), -1 # everything fits, no cap needed @@ -230,17 +243,10 @@ def run_day(prod_actual_w, cons_actual_w, capacity_wh, time_active=False, solar_cap_active=False, forecast_prod_w=None, forecast_cons_w=None, feed_in_limit_w=FEED_IN_LIMIT_W, headroom=1.0, - headroom_on='clip', live_floor=False, + headroom_on='clip', floor_source='raw', interval_min=60, allow_full_after=ALLOW_FULL_AFTER, initial_soc_wh=None, collect_rows=False): - """Simulate one day and return metrics (and per-slot rows on request). - - live_floor: derive the slot-0 floor additionally from the ACTUAL current - production (simulates using the live inverter measurement instead of the - forecast -- batcontrol re-evaluates every ~3 minutes and knows the - current production). Protects the floor against forecast errors; the - reservation still depends on the forecast. - """ + """Simulate one day and return metrics (and per-slot rows on request).""" interval_h = interval_min / 60.0 n_slots = len(prod_actual_w) if forecast_prod_w is None: @@ -288,10 +294,7 @@ def run_day(prod_actual_w, cons_actual_w, capacity_wh, floor_w, solar_cap_w = compute_solar_limit( fc_prod_wh, fc_cons_wh, feed_in_limit_w, interval_h, free_cap, capacity_wh, headroom=headroom, - headroom_on=headroom_on) - if live_floor: - live_clip_w = max(0.0, (prod_w - cons_w) - feed_in_limit_w) - floor_w = max(floor_w, live_clip_w) + headroom_on=headroom_on, floor_source=floor_source) time_cap_w = -1 if time_active and fc_prod_wh[0] > 0: @@ -506,12 +509,23 @@ def scenario_forecast_error_125(): surp_hr = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, forecast_prod_w=forecast, headroom=1.25, headroom_on='surplus') - live_only = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, - forecast_prod_w=forecast, live_floor=True) + moderate = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + forecast_prod_w=forecast, headroom=1.1, + headroom_on='surplus', floor_source='headroom') combined = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, forecast_prod_w=forecast, headroom=1.25, - headroom_on='surplus', live_floor=True) + headroom_on='surplus', floor_source='headroom') perfect = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True) + # Regression: what do the same settings cost when the forecast is + # already correct? The inflated floor lets the battery absorb + # exportable energy inside the window, displacing clip energy 1:1 + # (the day is capacity-scarce: total surplus >> free capacity). + perfect_mod = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + headroom=1.1, headroom_on='surplus', + floor_source='headroom') + perfect_aggr = run_day(PROFILE_SOUTH_W, cons, cap, solar_cap_active=True, + headroom=1.25, headroom_on='surplus', + floor_source='headroom') print('=' * 88) print(' SCENARIO 4b -- Severe forecast error: actual = 125% of forecast') print('=' * 88) @@ -519,14 +533,18 @@ def scenario_forecast_error_125(): f'(actual: {potential/1000:.2f} kWh) and') print(' misses entire clip slots -- multiplying the predicted CLIP ' 'energy cannot fix that.') + print(' All mitigations below are forecast-only (batcontrol has no ' + 'live production measurement).') print() - print_summary('Mitigation comparison (headroom target vs. live floor):', [ + print_summary('Mitigation comparison (headroom target + floor source):', [ ('baseline', base), ('solar, headroom 1.25 on clip', clip_hr), ('solar, headroom 1.25 on surplus', surp_hr), - ('solar, live floor only', live_only), - ('solar, surplus 1.25 + live floor', combined), + ('solar, surplus 1.1 + hr floor', moderate), + ('solar, surplus 1.25 + hr floor', combined), ('solar, perfect forecast (ref)', perfect), + ('solar, perfect fc + surplus 1.1', perfect_mod), + ('solar, perfect fc + surplus 1.25', perfect_aggr), ], potential) From 45ac4a153992a3b87b0ff25b3120155bb6294eb3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 11:06:22 +0000 Subject: [PATCH 04/13] docs: translate solar-limit evaluation to English and add figures - Rewrite docs/development/solar-limit-evaluation.md in English and add a terminology section defining clip, cap, floor, reservation and headroom. - Add three explanatory figures to docs/assets/ (clipping concept, solar_cap rule behaviour on the reference day with reservation cap / floor / SoC comparison, and a headroom explainer showing how scaling the forecast surplus reconstructs an underestimated production curve). - Add scripts/plot_solar_limit_day.py which generates the figures from the simulation's profiles and candidate algorithm (requires matplotlib, not a project dependency). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- docs/assets/solar_limit_algorithm.png | Bin 0 -> 135615 bytes docs/assets/solar_limit_clipping.png | Bin 0 -> 78151 bytes docs/assets/solar_limit_headroom.png | Bin 0 -> 104677 bytes docs/development/solar-limit-evaluation.md | 544 +++++++++++---------- scripts/README.md | 13 + scripts/plot_solar_limit_day.py | 262 ++++++++++ 6 files changed, 557 insertions(+), 262 deletions(-) create mode 100644 docs/assets/solar_limit_algorithm.png create mode 100644 docs/assets/solar_limit_clipping.png create mode 100644 docs/assets/solar_limit_headroom.png create mode 100644 scripts/plot_solar_limit_day.py diff --git a/docs/assets/solar_limit_algorithm.png b/docs/assets/solar_limit_algorithm.png new file mode 100644 index 0000000000000000000000000000000000000000..cf07603c5715fd87f00809b90464ca3d20d31598 GIT binary patch literal 135615 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 z≷RL2R8<-Do*HY4q^2E4G!Cwt3}&>yR>&5GUU$&Z+@}TY z9C8XTKmP4MXg|98(d0}+#BFCd-W#>CxtUdnq+OB#rUpSm_4)zuvwLFa%|mTjO1Ggd z62rBrknd~~-E5}6;-SoP?*`~!q@nA@1N%e{3(wj3oEXeIWcjU2t?btvgN+RU3wFEd z=*{TSo13MEa#|M@x;S;Ih-A^yTb``mAC0I4lB#H`hs=9juW`H!Oc0jt;K5iVC5v}q z2FdzK4l3*C$0lk<&W2TF4TP?`zZ-hEYGQK8zy;Gh_vSeJt_j(b{(}hMaRb9bHy*+5 z&F-bim{F41Ssj}>kX}*oq_FyR%|RU{)d4GFUS0j44`Cm)y)~=21j);N-2vU72o0J1 zO#`l$Q}?<~Rs=iugW7aM{u~*8-6bK}XoF1QznSy^bd92bAppEeSkF>>@AJNJ&X87#Lbm zb$vslVPq_3QeY5|QBz!MPs>ZFDxjx)n^!T8xq)q?VJN%8!poJQY2z4^ z{yE9(zP32Ifin%6Y)>0UaPFUYJU!9|v^R{a#_hGc-rIckz14|;ci(b$Z;s4CgqPe( z?n>9R!e@80{c(RUBrwOd5m?g9Z9o0UM9Apa!lp?9Q*WsH%CD7GX=)2C3qfCdVCtS8 zxTc;OZ24O-DZxd@)NNSuw_5QMVH_@lkbS*YkI}EBLQAt2JBN#lMI#9~zO7P9 z{soEE1;mWvL=1(l&I2tXYbUqbHf^7s_@C;He&5dv$TSW3+$XK)F5$R;ny_A-lU6q_ zcAC}>0zn)AizGIjIV-B|Wa17#dl=DeQCy~H;C668;(V>B`n z*^{x!gw)GXXkZGz>_NH9&$m!aXk$7NGiQ6&Acvjx<~nyiVqHaT+e+7fi;0}LNcyE2 zd9);Gq~G8@4uLH*+w1}6Cj%ssC@Cok0G^_hjQ-qxj3Ex~uHK{z-f`r{7?75(2?#O1!~dHEYJC-fYVPKFGyQR2GhGF~J$e0a z!|JmayV9-{_>CNCfqS3>l}9IBr*$`zv}nE921^OI!E*6?X5DEgZOQ(Z}8PJg#OfGRO<~aZ8Xq zGyQg-FdQ}BKzY#Qs5G~FEXzYz~~$T+ytoy|VAsYfXdo&?ycj6buq zEY#a|fQin!78O}(+cSU7>;N5wi1ph7wo`Cr!58fVZKel9ySh|~H-1}Sh#pcq1>YV* z=JOW=R2LlwPi)dhNLm@s6p!6m14+G2&&rkKTt0S2vn%h|YNh-}9Kf>4b;EL&dV`Z} zcilU@CZS<7tPM>p{8OD5ULJ>MasX#zW-|eSUbl)KTt5b)aRd`JHn-F5xbbQP!>fQ- zxawO{XhKfNSJhdc1-7~%l;F}|wa<6_CPrYg5o}z&3%d5ZJQQdy+4F_H_NocFTH+Y_ zlGcJiNgmNcuJ}6jud+f;$Z%nZ+zXf6Ry^Z)Ml-4b8EU@IIqtkeG6@t}M z`Lp{&E!EwFphD)FSLv6SQpw*}8P2Vbc?w-}RsBI9LePw!S}*r@!8Gg$cZ+^ZU1ICJ zM#wutMVb8)_v36C;*!cn5DE2XEqQ;fe#H&%3nTu(a+Y0Zb zyOPkm>ge1b$yx`Nk*P*;0B)hl;VW$fF~6^Pu?o!YKhMa$I4p8O{~aD1t?dcC87ECfHeZwy95c*s*mgs%-ofw*8E}bUpcbhl^NORQxdu zTu=%7URE};D)!ILzp+2N7fXWJWByy{)c5wbu=+a7D^s}|gU5A6#%ylbcvr%SRXE5m z{a>g1dr)#og{}19?)f3S**{+R*!0JyTnyy7b?o<={5|G1aEk~HwRlfgShXI~g7BtJ3-7!o#-y>EeS$N!b_cPvNMY&=~oT%0EXCgeWPTHZL5`X3aBPqemiR z6ppv_uuur)BoRpOijj1p2^QzrO;oSjh}PpshgpoajccXdFKA#HNtvfI>G3(K^$_7c z_9=<{ph6tv!C6U<|M)f>^Y5D2@r)c623h|bTyb%6pAO^1^2dC*yFqzBHn2>q-@>%z zr>;I_&d|bj`#?uEIn6)Y@>cDIm_)T(iducppzTqX}nTF{z4T zFdNk3Mxu5al!gY&4;OIgU6^FRHGQ&So>!*WNaFbl7ud%Wi7W7U)}wFJZ%YQ}_&7tg z;3k1OK`(y*5`gxsY4#4TS7YJc6$Md`yS(21dG35Wed>L`Uqned&uK-?Ey#gTVU*%~ zh=Va^iOdsrFYc@L#?IH5!75dD#I|sS)nXpN0rZa;eouGNIgmAI^;$`Ji#B4Gik`T9mb zsRUI#$l`csEet=S(^40_5Nv!@T&}g;?K()0q01~Ob|M(4WYTZudy$RF_w`5UtpT{G z>X`~xFS%chJOEcQblTP}uO4(^!V#zHbssJB`H%YYuT}_gQoSyE@)a)oiV4RW@}b+S zD0-zR6A1=pb~o<&00j#jcZlWb`PXS$r3}@di3m9U`bFvw<6^-$(Gdp&6Q$g~TpHeU zKi1KDK!t(AuaCx4XR{RP_ptx;_D6p?xVcXE%b1|Jq(oM0DQb~MS?ASqnLll~`!Nri z(OmWJk|3v2yT`@>?DdxqSy1jAv1k_3%;2=@^2Y;g3=cV#RJc)}>NgD%7cl+|W~OTi z9+Ynroqbq965e}CY0Sj$e3s^-Zyv8rndnm%^9q)|L|+}YynV@zdq4&8UAr*Q0Kf4S zjIQquPQ6RNt(25(kamfrm@$Q&o)Zys(>)SQdtp37i;o4wzw)3SBh#M#WCoWk3p&td z&~EsH)_*gRbA{(=(SHHrX`$*Q=(H}rQ0sslCP8YcsseDahie5E*K5RL&UJ|rb$y(QLk;T5~{ zY`ebm3@A?8oQaNxT9lvMiuvMx^4dWs#M}d0fb)FA7uTLFh4!s?LlJ?Wo<6M7OHQK- z)~Uo7-(NTAehVoh4DiB3S^a{C7Ck*zu@wr(WQHh1z{)8Zpvd&GJLL0XSt)erTmP}4ivYo{^-rmagXbF6^L zkBqVIJ!$t+rY$dGS{o7fFB3%H!FVZ}#g zwuKLTanxE>?bB9a0(KWg8?`j1zOGsbp88ZnjUEs6OZz1OY)6ddl-W31=@}K{p48MR zUhQ=UYnD>CUmd*lZBpN#X5uxXLiFcCAp^KUdur}$m%H^&xF3zcRWxw?IlAp7<6n}J z3cpwjaD|^eki46yVXIHg$bsHnQBiW8o;Oq`YyVM9iD_$VBEcfn3}Xth_L=!WL73mH z&(xCJNEjo0tAmdW9~2a1xHn-rVzmqI)sgu0>CO4=Tfw^pXONY3vXvEf|NX^CVkaZ0_33iy>-;F&1@gYq4Rwwt=Y}C5Z5t4-%1!N23{(V=(kRXMrt@R%@ zA78+9eeXKyTMG+Caxu`&1XcE>j>ivJ2B%?*bB6#uW*823wWPFM;&Zjm)F@QJdq?V8 z9Q-kL5A5&mo*PAPUtgcCjcGb5eM9q-5{HeN?vjJy^}XkPd{}#vUs&j59T5`)g+&t# zjU1A-Crju%(mbH>ee&Q^->hw@c`y^jP8dSI5-qz=oMbj~4!~)Wu{q{k*ZsdR1`gKf zv_U-^=jm#f^dFx#EQ}^67fKz=z&GWNKk1^ z|CDB9`w^}Xgiuf=BSH~Gio2&?W)He1!`$D5`+WD8-qTF_)0G>@J%oEU84>-h$j>VQi-pHr~f%7Q#HWIj;b0VeRKpj z8~Js{t$D`SYK+SEoVi7swo8k*Oqs8~8#AmGYJ1^#z43~E9y_*3UO_=^08Y?Zk9aE@ z>D1`x{!@`w_nh~e)1t-c$yfh_CzDcPhDx}nXx6TYE0dcDhBXnP5r0-IQs0paW#BP@ zHwjOan(Z?+HiE_Neyc=6M9~Ei6%Mbe7!D`OT>RSn{aws~!U8*Koqp2awsZ=}3n;^? z*@q9XAT!M5-(nLU6BE;GM8uAI$90Fr3!b+WU)9Dek7KD2{chgk9Tn3#*o_C#yw5=k zQKw$%NyTL4QrYezy-jS%;kMzm3Yh#*N{8oPKtL}mts%zNSLXi1uY8uo{UKGictHyp zCW)637fqiBRa84Z#!C{cR;%jRMtk@NGfw;kKW&GtLG#HkM~Hu+p302 zm}nW1$bAPOqBjSTm4%Y#2>UHfRJyM@RQ^j0%SQT3J~l{!06nR~!+o zxe;O6j>Tn`xi4O7gg83UPI^ORF1?t~xFbXP>LOf|HS}K2_6G)VJHCMqq`ZCI9_9RP zb?z(#^|rw5`4x`%0=yZGn$5fU(qW&I3Gx;GsBc(B5as0y7&&)}&d#orH95qjPSLmG z6CYYoc|_cwgcuE4k+n;3c^2yNcP)2m8R@&BWInH}W+TCMR+tg?jk7ur)Tf_tQ(@hj z>tR!!iWcWKkf_y#`0$e1tjNxX+O%x`LpiBA4_v+@aj65Zw+~H?yG}#3j7KU-NIbZJAZ>MQY`*Bt$wpp?Q5D}WB%~02PSme&-_zk8 zFI+g*J5VJaQ9us&UFvWC*!xEP{i_VFH)3&kgu~;nD|>K6(f8Q&laiM1ID7KWnkErS zJgrprKD$VarCPavvt``fr(}0GW5fM#p#ldE4r;`mjE;%{xts1dHBu+T6@-1sCMVTKY?+vb zX&D$KOikgKQ|ihMuJQubk1|PxOv*i`I~niyD#0ZKN`?f4l+f>%+TF-&!BhQD_(R5x8j2@AaE3uj8cG~Gol3WKcS@&}NOy~LN!S0}&-XX)%zI}T9bx1=C-z=@t+mgy zaSvBI8B7x2Sw`+mFsWA&Cu-n7*wpSE_Y7jU=e7Z0aY@)+?MGJs^pv(yR&# zo!sPNd`@UU3E<(mF;tIyzjU!Sm%H${>-DbG`8^Sivn&{dI|fQ)qV3+~zd`@9#s(>^o;NL9h<^z`&AHt%I+_b%<{%I_${!)1vxxd@k< zY|ok%y}c=-q~LII6EeEDUeZcM^(^wRC@<#5djAkb;FV1{y$g|gaFqv>70e62|BG)P zJvt0sc_UcRk=^l2eXQpm89;FRYB<9iZET zp%Kb^;)R?>yk++`H@)FI#0gh(G3?W>cBUY+9XA8x%=b&Cf@zZ$4(n9vgJLE6AgY`G zX|TK42&Fu9St`o*o-8w>6sm^aZUqpoj_FEOO)d-sn_^3MMJ-o}nuZ3Khi3+;MZfl( zygNh@*p!n_C#d#MSHCgr@`_>SV8--W6B)0^OHKy5OrCBvxPTDmKg_TZLn07l_NC(( zS2>xxKIIJ5sCLcGk97}e7q!(lc^l;PZ^6JHO)PT>q)QKnsv3>tT5+eiR0H?hhOFy) zaPq!x*p66IXR*4oE?VDMY$;nUI-)B+Tfv){VvWPlNjTYnpay_z`(&&Q7b*IDvjIh+ zeD;p^+QkQjiu;lo4@^Qu>n4rS|3>}1!0LU_z}5IlyDv|oV;hv$jujYX7ZOd4S_~|9 z7w}vbmOs6IajkJK!XFm@%5A>=m$$ceWZ4sJj!1PCQEVCWseWUIK+=q3F;{umZb`C) zIGH4x zk?hm>;A7Dz+y2X60yoJ;$Nf_J zkior^aWD7rz95_Hm6VsoeaYjUhcBuS*cra%^#|%0 z6v?5b|Gis$ZLOU9eIX)ADLvLh+Q}t>&fw@t)2p$BJP(;p!tC@Satep6^2~V-gln@n ze$}!N__>p*XcL4ELCUB@Vy=O{lowQViFqbi@$bM@_}7wfbd$%wul7qFxJeG(P7@>u zNoi<^U{Z(h2?*BBd?*Rm+Tl4DT10m~m0sZ(o0cxyh_vWbe7qicGOgRbYMW@mx~$@@ zgX?_G!4(ulNOvHC@*4KT^Vn3Eqpqb|Nz5Sz`w+$z$5$bryYW=LoCs@_m5evF@<3_^#@r;#sNpd1^V zKOKfeL2hZ!76HeaK#27;@x!K{F}=(ne)?uNa|>~%-kV0C&F_J}$#hNu(v_U;yIH$p zVo62cXs{AH|1gC(nkq>3E)+xBOyj=(^3G!pAp+(tz9cvA^t%#O@uE4nx7jC({`uYd zgWe~q>vqBY#%-D})?3In?Ve7l!4thHcR@)h=D%!P^{DiS1Ze&SiW_oIIu;vw!V{ z*)ss`Sop83ibM$u2p}?lMcpwjU?)Xqdtr1IPNTs}`2Bk!zwa_gklW7^%016&`0Jf( z1YOKOb5Np#wF?j+@Fm7K*JCItDV3M@ukY2xDk#cgHGUahwYQ7A1iK>!2mM zMv5?>l>O8F>X{NVJnPb?NPy$*H$&Epi~t0qTb-CNSnlQaf9EV4)oriK+g(rCtkxP4 zb~8nhKqw8+oa%5%(`Fm2-k`NV?Go|2UU6V;z2W?ODOOxj(w)X53W3ztIc8i>&D|&Z z)(J{1U!U8QFX|jGo0PLaD?I=i-F9Z-qhyGM4$d3WB+JK|XgU{RSEhNh7pt_H%xkV%A2jKE@{qC2Gek)$%Chkx^=>^H|gdvd{J zC}QEDeTNY2wT{UeHv->oH^(OdUv@hCbIaza^SPefKuq%kg~L_e4+-v56qh#ZF4dNW z4sSGGYwg&`Kd;3V#x;@~8yk7spO3ZJi5IDnVO5zM9HU2*l2o-FR}7Qmzk=Lx%UCz# zPygC#u#uL>##*+7#O?WMLniX0d-F~gtsL%E@?ypbvt;f zFk?Gh@nvRR2?4pKJMmfqSrs0MtMx5s_PInw=h}JF8KWs!YKiE}|RUCESDmPoJHDh^5N_6n%^vLIB-A!B@-|RBB2{k_2 zpP4Olyspdk+@^|AT|6D@zLGm*C6@5-GIU&TsKPQFJH86uEsBcjg`0~C)OGxWrVD

O$sv@f1Te5+lS$1Jrp8lA6eO3wi&tb`8X@nAsJ$0aH794|K;iX zo;K^D6nKk#NE^ zo3A2k^H?H((Z>4*t>UmlnG6O|$Uf3;%OI2899Vk*&ikpZkkZ!5PVeJkTKEg_o&%1! z3qk71oNl{B#8&|IoX29gIeWv=$p+L^5@SZfKyu*F<597@KCR%7Wc5wQ8irqmI_nKTg4 zbBzX16L#0F-j)tk8JZ%_RXC^Jj_=%p_FBMCyd4CCr$~R-v!$mBFz#`rlBk_BolQ1{ zi2j^eJe_%$2rsGE;s@S`gF4~)RCNqUis(xMSKS1RuMTz!6VM$@T0To|(VL&G*8L}r zI-KT1v~hL1W^UZb*}OBY{q95e=Xd^Q)%wk{ff2*Zn;Wq#XH&|c#2155c5)-mDPUeI zPu%H(@Jkl^*?!CoBY=lf&o#7IJ%Ty?>>t3?Gx|XvU-bS+-qk-2!vEh)4cYE|Tf#0( zeDTstUl(~*O}`J0N;RjX67kzN(&R?1*FXF82u&SxkR~Dh;Mt-H6XC8q_56SIn1VdPC612ul$>@vxc;_TU<@jIer&wTUTCSr z#WDK7(|WFTi3nv8H?aM~OXNmL^8y?oYM(_lOa?9|tU_yMe*F4ntfF$-v$CaO7G?%9 z1WzsQolbGh_Db64>x^-m)XVkY{9F@WCEvltB7BGfplv{sLuqUK7QnRr<} ze@L^c86QcoqyT{o7)x%wnNxflrm(XV1I;^X_C8*cA0*``P^`zTs~ll zJdKUL^0Z197QnO>VkconFs;SGpaTb@Ysse^1w?3A9;x6F?jZeW^w(<%ejG31s_c~H z%ivhBw3HS$&^}1#ZxS@SaZB5NTrale-AIjfdC~d(k@?}02(ur2ZGCkGYn|uFr)tpa z+Cms>jrTDrC6lLG%3dxZD0Fo>^z|D8Ppi)3c61?6Ed{8?afjP0Pg-5>g45|^{aWpi z2>~*cMEW;>ih5`}3^ifvD}6XIY0;Ja>mMT)$<-}g5jym|GL)+eP(Y!?BSp{Dyo!zx z6&UR7cAT$c#;cJ#$8@&tyv1(rmQmFPUdk+ik1ZkFQ)x)%BVZHU+dCi1Uf~zKKuU*| zqnrUE-$^$4K)NWoqh;dDB)8|u3l49{_$n=FIkC-ezc01(_EQ&2)w>#*BQDM|+?*|PJcHIo-Xf(YBvsl(hM3fUEj0Z zVt?(aKdVDcY6Tt2lvVZSVo@V-wiQ!eiQ{i?On8{RT9VpJW z9urwtV<$(N{1KE@H0KE(fnijbsmiOs<+^)=Pj3QUmCILZ564ru0MovUYe8)4%pwY3 zGL^n52H*|@?9;a`n^aoWCmPX%jh!o;graTi6~2Pg(xlNe9l~ZJM%_8vb|inIr>Q?q zF#F+a@KTj`b#~690l*N3jR6gIy(%}xiC`&)mcY1=alyWD`J5BmYyW5TKhe`=CJ6iC?3{SW_<=$a-;CJiV;4wDuPaLjwM2P07=Ce@=9t^@*2>VUQY45r2Mb=)C>cyk25l^$N5bv` z|0B$M2Nd%8-toZNq*s!$PfaPh+k%9srM%ntn5Qh}w!f%RjQ>C>hc5$u`CG>@+^5EN zM7?7h^XDa#kdj>Vys>~3J7L{UU)X^!=%X(yz*$OMF{w|JBt*~i`>Oui&s20d_>Pc^ zo0;Fo`I2vxICK5L1{#?#x_*5^@6)@(q_7Vo(;Fc6 z{VE}`!$m+1Nyq;$G~AbymSMFJVgc-dH6GiSkctzBarzR$nh=LHxC=b59XNE?ON#4b zCk_U#GBQBbg@O`?57qwzCEUfbjMDavDfHUxAC3-=|CKa^E}bqZmg+lZ#7G|g;qCiN zaWi*QRSI&z-PrPqC)@Ee-o zSpE(Uw9|TrD~nzA=wYo6-~bOV?NhiohopsR^Bhac->mOpxok>&Li@rA(=<-yKVoWf zjt1mY5Z6E0q>^0!(27Yg3WW?eRR8WzOgugDWUT&$9ZzKhhFwtDALIG$TYk>KeyvUs z?kSW5O(?lgp75xuo{gEuCi$nm6pUD$>Hf)WV@jAk~x8Y(a=dM>3;!JBB+Oy z<#CPN=d@f4mWPzYZjW6etd#5z0@qHRmGOA zm=*m0Ql*hZ25j;l_&;0J0(hW=NNl*h`EsNT>8eh$6Y|)SkO;BxUgm=GZc#@IGR*9g}uoC&JYNSQ7-#$T3m zyg+Slq{AWMV5YjRHH}eQI(vM<#Y%C$XgO#ZM@i7Eha4tg!J~QRn|<7!m49SO^eQba zWQoZ5UCBDKF*Hf}izm=|wJt?%A|g595;W_lkXlHt2m67YJgkVdb$Jr&Zq<3P8GqB; z@b11v9V|Qgo8c;wQ5RMp7nUocS;3}~fP7FVN?Ii0R;{Lm1=$r~dCqgWga%zoSInze zI9j4jp*CB$V#=w#ErO^ktp*i=BatRWt18ET+(v^$6!recC-4E7wQ*R!?nM~J5*jSVPmnLiR@L8+HVXViFaocMjAXY9o_BaAU zE>m>h2Se}v6GSAojlp5Ewc_mA?@*!JyaTa3uV6&$yLxGE@qe_~j zDf~Y~_QzF=#uq@^hmW7~QPiKT@tDcbZLB>q374L1*#da{n^G<|eqOPp@nSunV#TWH zAKURbm3G~SX8*-cU-6&o9Pa;_*$?rP?KL_(upE3LLzePh8XN5J$pzVjAa7%ra?$-; zSA}ZLu7`MwFA_+rT}c)0&TgmCfyLG};w7Wt}X2#RjfC76uP*r3&p2A1S^cmMTMj{Jk?nvfF0zs8*oZ5_;@b zAZM`*poT>5q7U-ynu-lb&OrkuVmsKIRG6QYme*fwz_SbFdcRVT_lw@dmqeK;FYcFK zN&`tE6(=N=P~yy$JU*6VnXVJSf1j(WAM^E{eHHR-c&EQ#GZ=LTxj~1pzes-L(TU)U zcy0bS-h+f~M448A?^G%m*ZJyDx~=+FA0eP#PRQvIK8lKT>wpb<+?7Q^#Je9k(nvmW z$2X1B>Ut6&XM|c-{ZNBRH9rK*>TMtyU+O89VFX2B)BTaNP9|nyF!|w3F1C{_H56ep_JX4YZlnYQM&&d8P1u%K#t^igrwv94b z3q4}pG=HIIBQD+4WQ8UM3GQOR*aHo&e59#o@})mi5uFV8e^X{{nv2tUmxGJ={vKPQ zZ2|KBJx>xRN@x2QWFLEb1~d$uOFXc-xiAm~k3EZELs?kamS#|4p4&Mn>p~H((!4>` z1;xe-Ij}oO(IYX+%i{n0brP$pE1O;Ca3j1I*PDi=ozx1(BIAJV~RIWnY3+FjohTsWehq z_gyO5oX+zK2*Xkdb7F`scBOkL;xqAOMBU9i8PeK#S4_W}|A4Hrp4pZ_Y=sLwH8>b{ z0o3Rz`gow54r35bJ?eDcrMejA&9kE*UHI0|usc*~n~$BKx;`CwKI#sE`|3v>$iPa; z>`e@z=8_6l|3*i{OIJPjT}V|mHXwol^46^yvCaXvZTb&Z`%GsCAJUW0@g3ZR|8;$8 zIiHeJ=ztm;x#9ihiHcO;3tpP)xf|fv0JOO0(fyX&d7^f#wQsvI^KmnKWwxgTHy^x9 zngmR4Wq&}~a}7%WOvMoy9nPNtRHkW7u7<&v%>T|8`r-Y&ncBx*c}c&=`uu3J#a%Ow?ocPO7rs)?8~~ zWIgFKj;e)@T;0eB_l|L$_In0^`DIdC`C*6L6R7I8T_OBeNa-T@lQZ&fOS{J;_UwPr zX4GK$y-V9y!&sghQF!DB6kg$m45GR{Hq!l_z$Uy)*N*IP|wL62CH&~(gP9|%o zw-pe>fXDl`p@zDT*m>%kM6Zv6RwhMMWO6b5KjaRGc9clg$@b~&1|wMoyV3YUch2Zp zgqrqcth^bA#`2=79Kw^Ur^j3Pbf}r_DhENz^1h&n$DNRsykKwEJL*Gh&^xP6?+?w- zp8~Gco*y3Vf3MFN)`K^zZa>@;smF8=)yq&_{kP*Re`i5>Zv))n%L3DYslytn{>h((D5bEgPBv-+% ztAP)&h>}2fh3nhC=j8p#|R*DFB=z^ncph+i*L@V;!xo@fExTtPbXgo$jS=_*w3+RsejRM%^ zDh6(H@Oizhl+~51pQ7--zG7SQnO8=SE`9uYTD@(D{yyBUkzWF3a3ozxZ3-1sOw<*) zBq+HuF(}7%MlN6K=WDLbFro;_aPtG$X?QlH^iJFDeIOH6GlYQ%_c3#MEn^WE1N6Ba z3qhb9A74Vo&QTr0#>wmcwQU!K&_aj-SegJhga(M8(ht(zXnBW5>v{E%KZ)5dE*Oi% zydC=1<2MmO9)^eXN&e+G${`VE_CqCWPCk?i&Z!ovRufMIh>$W+F zh&wSugncn1$~5irN)e>$FmQ4;(chyw-Y&l^wK|@?6)9jj*{kdjp_lD>qlKDz|ACY; z@@9w{3Nze65p;8$gAkG!D6d9Ulk>gXAAxp8iRJMPc@)ajdZwPq)(o(qxmyRmU-HyS z`(@~`KB3+x-u>BM;>UnpV!Hy!u@62tq+0Se))ociC(kaV3>eYp;QFS@e%8%&IFqC| z;bvCF(vxRK>?Dec6|aR5z1ly9ye~3cpGBaZaQ9vL9Qm;zX)9Rqy6X$SjEGoA?2^THd2O29!xv|OieNjvPvs~tnq z^=#StCVxvMTaWlK+dW+e44cP|Y_ItIZ_!IBg^@gCXV#-_9hKFV{?zJ}*gY=q9p56h zK4l&6AE@BdCyPjgn332(W6c~nYg2x6xAWM)2MfVcP4Z?|>A)MM=gdMjWZ2^$?6|oC z{8@L6vjx_-t7@Qpg^KXga?jb5GTRfxHVNtrhq#o_wWtT@V>Z@engk;EkLXb{BCdb8 z-Va(HYS<5<*I^V_RY%L}8QZSm zO8#$}cw&kmaNvq9*f)#dcEBI&C2gjyL4sM@+#Co6Jl69WS{uu}z7EW)5sCTJ3L#Ns z*U4MYA}dEif8zSs!S17OD#rYTAq5U)&xzhrl&P=CK}J4bo5yI3MS2vtX-wO1ojuL?~ZIUan=hY;)^sC#EGnw>mC97WsS zN$Av}+{e!KSP<1%m~uFgBG_p0p+e%m5{Kwl;8rk@mGdISeB(7#^`HZP2m}J-nubPb z&AYq}yD&bdyww(OZMA>=_W~@8FvA>TH}@Phke5Yl-Q-}22N7=_zu?-na5ct$L&R)=XZ1+2?n^TG7>MtAHFvzZCXB0MH<5;-|iM zRO*IP%Cy^Z7(j`sZfpp}Mu``@`8#PY6MeSULjhEg5F@oiW}9beK=j~~kGM7tN+E7# zi)P2@uk3-BTk;iXOiNiw^Z)3Z>Y)3Ale1f~Z>@{f@^jA@I z4RBa*I!Bx-9ruU$pammx03^RZX6?Zhj`;wCUGvb*b_eI-$u!Z^dvu&s>($Me^H%0m zlirOJ*6axj9EAKYyYRx&Zc$w!FWPa36E_Io$F;&j8e8_SMqafLK(`RY?~`DJ{m4T+ zPz#5?z)G1)835+e?`qdq)#JNkX@fXh;K~buXA#ZWzpSX==a@L?UOr@a|FDu`C?$(# z+xYXfE`*vk_fb6ZO_sXiZa(9LKQLVWJck3=Ls$gfx6@G|puR|&%k^QIB$aTX;$kK9 z+DTD28;X!u3*$hH(vD6-<_DZ?ub0OrkpzlHx~-&aARJm9&#${P?#sx*5Na9P-d-y8 zZbMNWId9W=6xWVVA<4F~63wf=(z&>|T)L#xRc0Lgb|=3btOPtf77G|yF>_Jgo!+N2 z8caDlJ_yWPaYo$ehYpii!M+8qIIG#IDPG%j(NX$&`1QstW7}Ya5*;bA@t?9mjlJAk za0t#>Hh)YELUSy(o*d>6kCKt*j`%5#o#;m+&}Rf<^*-Bno#-wFz3CY1ch!otD=wx) zQ5GZS+FqvJ6}TAK64ITF8c>_lzdRU2xbbj=Q9ELlsy_b5y{3{+=6T>IS@%ArO9opc)>_P@meulSU94(^7fz8eus!?w*Ud5y!^$yiEG#p zi+R_gaKVj@B~HhxwqL-xiD}!Up|sA_-b94`XO8h_5dy=kyXQ!T!g|rHT80!>m*hDq z1(PJliqX~6fs6e;uYcS8OiZ>8En}BN=*}p-FwnD@anj+Yh!)@nQX}7UGWJeq_9a*w z{N`Fid<{-pn5jzyo9h%VGi?`j5<8=>+}%C&1z8F!Jl+EI zqx<}aIt|0r7c-h-R`ZCjL8!AH$C`oVT(L5~MPH7zk*Y-eT_lNg1etcvSmg)V6I5HF zs;qHo#jL=A#$)eer)#h4eosvXJiA@`1)Wu>gSfazyqFnfc%-I%9c$F=rHT6Fz6P3L!<9cODZu7%T+*H;GGY9E@!g=~e!*A);n{(O)LQ8>DF+2~$Pl_Hxi z(R-B%TMz@2sLjw!aD9f@GJ5%bJhh|l^73|X87oIP=N|Yq^j+&i%Q-d#w!cV-m8s>- zeCfaYVLa|#fEh@Y-|Hk3_B%gs90EE$0EZ9kOURQYainBXhyP@(mi1yl(i{JU2N`f7 zK3gDLb<-8jjLi~4%CvQCoXE_|vbF6j`uqwQp~LRL2-e$Kd4Q=$!~Pd+{?R`o3RzfQ zO3Um*AN$5d+qoL@)SPZXSNe^trV(XTZInr$yZb-j#TLcpoLfgoMQ{Hroht#uA?#AV z7~q-wEx&Qb`LC~2HF*`Qtb|(mbL9xAHAI%p#hoMn&H^zANR${o4Vqqefv_Qok*%!v zB@m+!i8gI9mJ%E1RKjJg%dw;UDXH&`MEmpL?6-(eLUykzo9(M_0xTkVlr2t61;F$rsJjswdwhPmEXgV=y6aW*#RAt$FYJ*R7Hn2sWK#Z8Fxj`QP zjg%|^@YStH>Y7uiUu!M(P`5KHV>FK{t76OP=8SB>%pE3L1`0@^XkljQrrW7%eqVaL z+)fdmA==&5RshXGfxHm6{bQ0aRBNo6xkF}qlqCVOH*|;u&XN#6+or~6Ul%Dlc(Ja( zQB2Tfm}!&Cc+W~Fu@Agl7jm8Vo9+l6SL9#lgT<=$7IzgCCtO`ur*z!CYc&M`?7tP4 zN`h@tNZeQoBusKii}A@@^)JwpZ>E!uo<@F-68@d1>b6@+$Lf^_pSinkCHyhaGHW?1 zmNN5?%UOEZ^6hS0#>48coVdipz%$fH8fBN|t+ zqX`a_c5jpD$`_Rnt=>nY4K(NKP?Z#dtY`UwX7-R7u5K^8UaX(i%x-?A@Fa#$9!h1b0=e7ya9_xOTbKsokBxIwj` z1o*{v?d2%+K%Ltj2-iXFG3?QnChY|5aP1T=DA!ZICWf8rV-QYI#JuUSq{3-)Fy+vc zSNlE~Y&cOvimzH<iI-N*th%YuyWN1I*0QCf(;;>IT`?Bi-eA6$8D<7_!1 zJn8p7dp>N(HzQc-hih^x4JlMPYXZ*ar%U-4s|%`FvLvvn5C7hqnPs3t+j@%EyWBp@ zE2!jw>Mzd&DT!167?}r<yqAf+Ks-c{ZQMoc z-Gz4hCEx3;ZB035Q(hIKX}4ZuAx@R!A&jFt_Iw_l0B z22CKqRn}d=#YruPE25E!6glSqs^#mr+}hiFkLb>J3#(-5T9=&^;hm!kN4B$gk7Yhn zViDbRELqqqin9I=qlPfdsm;|9sjcB_*Q~R~e6GBlI5aj|9O-QIH(NCSz<2W+_x>&B ztfnRSL^Ng3sd$`|J6$>!745bOp6qq}N7{gj`2#jPtxE9s(<{(3JhFF^cp+8%$U84& zyxOcG}V5RlHD{T`&``+$`<(VqXE!U{0shN1; zID=8QK62koOyGid0%h=sffJo)#nUm;pg+#eLlY>Z$fLtfk3kfrIMA`xz*K4BKWX*u zOBmq-NxV!fmhB;)m|TCCz&(4 zJzI8aI6vOUYGB{_X`y9M!=E$96d#WrQIlDS?YWg=Du_gKmJ2E%L`bu0hG>8KZV^k> z7U-buO^UalcWo!EkYWMIJtaO9Ql{f{h3B_4V_KoKPY}1=@!W2S#a>bM{{=paAlUbX zKdWix0>D-upnJ(qv)XBjpXIRo@FD2ntJ}@kk2nFgZl`>gaXfS#Rn+4)cGzoe z?2V>R>)~GE=J=T^y5?MjkM4n5)hLj?N#^!uKL2ip|NVcr8Pv$c>EF?t0l)hvCbE|? zc1+@n#{CE47C9t&4RXjjR2VUl%p9rJm6(nR$IqPENMO)@nu|8$a;{uMWFl~AKZ~4H zVun4T!uTJ7P2z~>7*TU zvE*H0#uW+NRD8u>$*ZK`?Z)H<;L3FRjIuv%jeHV>k##ahn=Cusp)xU$5kDodIU)m{ zxu2v*QxYo0wPn`vdH-*mYn)4TW)Ic1cM*>jZV!>n^E$rYj6Sv!zFx(TrN;2R$L09L z_y%-H>2;|4^Mo$u{AyM^i^iNX?8n=^*Qx$I`?u?Nh19mX;OV6g7psfO)&FDyCmYc2 z4Q*vUFFouyj!2g@3V^oGD7k84+WfEFi9rl}zDzCdwBKfEG2;|Cjf5)Km_TBs!euR8dESAg<>-uE&b z6RrC-qeT2>yA)DXQ5Ko{-5?aG|4Oay09*~vM6aur5Z26I5bGhe?Fn?(l2#yV1LTu4 z(BKxQ`>HqcO}~*th;K!ed3D$g+gt$N<*{YJeiE!rW}#$tNMPds)?z4=N$$oi8O2z> z(%---4m~+~lKSI`UMJ%P^Q&f8bAD3vqfUQ%*`S6B7-kLoiFUf>Hfjpg7GLnJqU_6b zkhk`k1lO#uq$Lv%;;!s6$)DSJU}*7pMD|ln`!8L*Jb%Fk&OGj<J60#g9TQ;uOxW|}xM~!Q>q-Up(>4X2 zJ(3Y_v5{`4ssOKEBRm@u*8=ACkuZ?7l*M0d3hmyExCD(v)LJLtIky+D~gE3F=fbbgQkJ*6J6 z1(m~Zl=#0+~?p}c-1n8<$XudQKS3FnJR^ky@m z)2c7oH)T)#k`wA$8xuP$@`9thYsUSfz=9VgqqODQ#6T^6BKkTuKvPg{JI%m>syaN1 ziJ#EeGDqch@GG}K8;IpGmamBc(=`IS%P&X}H~i&?)^SeF47z71PU=?Z0QDD0G!6OR z*jVTw5etW?Nql%EBO5>$^J~Yte<}R3fvfpc0Sf;Ycii-6a9R!TBnN8hhD(Iu;XCQ`h zS>6Tn30mQs`)V5HF7OAc#95^NBZa2aJZybCJ{7!3YE~>gHLCsUWXRW#Az}I$Qs1T46ZZDPV$mHMpU(9Tl$O3YwXAt&J>wK*rIn43^ziH& zZ`555j<*tKue>lk%KB&}tQrpPBLKbs@aA7-6p{Ng&W2OiS+B}tA19g=`jbT|21C4e z*fp(WqELHgc8v{y)fQ{Q?fGs>OUtgle%N>kkS&0~eCy%G^khF5RkzyFwp+;9w72h> z(-w;bS-pMqDqqr?t(f~{dp|uhXY8A~R<}34+t+>M$_N|RBbv^$*>EQHEdkS?>vZYyUUtqC)J zZ_o6xjoGXnk(o zx6W?8Ydx&G@Z?>&&8S{f$R6a)$MALcWH}e`GGSd(_4}Qs_Qh0UGm|QYujx&rW}Mz) z7-)%pGfLueC-iUK{RIe+q{UKwEI35WgLC&|#Wv25!fEQp(x2Ynb6+-NuVJCKI(Tr3 zJIO6SW>eVlY2UZr>Er1>)liCdviH1}O2@q<0C7>~I9y8m`X3?jSYu{8nCUbm|=sLJ9xAkerg4Lv&VPLwVRRO^nAT2r5+L`f0IL zy{C_mbUx*O{$B6sVD{0qU`_KYx!$9y-%k&?Q8gs!oG*tTZT$5Q(42mgp81%bIz?$S zo}L8sKnDJkxeaXPIjbJVgBU|l>1bGCwx3zEWp&!}Le@JwJ0R}#(V3`CqhfK5-g32a z_Q$^-MD>mx5hZgy2z=aUG=N3P5FDJ<8(tz1cL4n;6N%Qn!v)CQt4R{&o9n9a&>6PP z+gR7!(p>N9)mnK35v(`Q9{1d;K$PzC<>hM$pvnu|fcKDA_!>U%ScE6TEND6{u{ z!~Vujo^tm1CBm;KpTER5S6BaAX0sk=&Sh@N$p6e=n|EZb!%Q22M-18t`Q6nTYlF;= z0|Ogd(nVW^yNx{-g5CytC1|}Y2c%G85ws5Kw5}`Qhj~4-efOz(RfP%q;|SE+JZBc6 z%HC0_M(2G_BwGAdfB|#)a2+gU8y0j^5e(3K+LGQa3>m{xwwdD^_BfAQU%oN{@ow+o z9u#}DjCI27T72{vZJ?8crIQrQ9@HY4HR3>hf-Qq=mOW4!Hxfrk6;XeqG&(Blr10YJ zFm!1l>_8Fv=Hp8YIo-;_HEq7#8m@+eun}jYUk;mB$pxeTRPg^|7Doy#tByjKr2Q#b zEXC7@Bd9A-1)_gj3ln8(`sc8z?E!0o;0Y0=veS;3AvO#S0`7o`X-`13nxE4xHtBDq zi8Y7^hTu1I(b5uOKqB}2fGeWoiVsoJU79WMfxHE+-&hZxKSp>5hbR33CizgcM7)b*k@HelL3N#pJS#fQi$i<9khIItaBEcN+N zT~U$cI-)575&IX19#j$en^LD~RWxC5AbB5F39!vpZ60uQywa5CeK;GdfcRl&qMF;k zAb!J*h*tPrtVr$O->0_Uwh*Y=N>w=?2u{OkIHKw-8C3Do3iUJT-mae-tX-@D42}y` zg_vr*Fy*+)q7$#O9_)ZLkoi1o-)Zd&?8GpqpCHIl*+V4d%%Q^gEH%1 z+SHNM$U>2~E1jHOdY)&*c5cOms+H}*aLd`itCB1naYNdF zzg&^O0*3yACoPqDf4TBg8{z~*9R}a6&)&(kASmUOsYyu7-dcr?8!yxYM}CmmBjYl> zUyuxih>shM$jW4pub0Zf`O^8)V}%(;0 zVB*hiyfvU`q^j&wah*6kd(19dd+RB~>FopyKrbAmd_^VVFMEaKqPT(-enQ@o9vV16 zTedd+K$ecZo{3-Ccv{>KACJ|UHFvVvg{*EEV5e736?bQaTutU`dYujivn^rbPlL`O z*B2UPeJofL&gKUPr65t`v*xRY;2BzFOLl>Ab1O*jADp{N zr0J1ZUGF@R+HZh%ntF8N?nEDd9oV9Ay4gSb92WcQF2{6bY7V?EeX!*8B&l$pzK9cK82ri^a^hhCjG%wJkww^~m4>$WDl*g{IGXHVHw@@tWFsZ@~J zcK?eLd?B~|;e5@pu_Pc1#@CMvpcms12M*ST`8pfh6)%*XdWk4p44l@1SGFb!d_ z(SSpL$ld2+BidF3H(yD`X@;zMsiM>!Ir+L~vv0(_89D3J!ow!%lLg@K{fm2Ks?3^ zEh&%p$bE9^sRA?&7J4#_93)PKQD3|=fu0VJ7u;Jo*Gx2Y@}Z}m^ve<6uU2|;e>EcM+b zlx_~-shm{QEkJm`%JeQc)L%)$KaArMq6$oVEAK~?wdbD1=v9hyVkuyQe_*)k5l4Ie zDZ0AlUi3eln~Uk)vb7TX6o`YcGj?<7%L`~FYPrfO5z{Y>e9wYS#gr|QM+V>EKIZS#1DmM%;G`=&l44Iv~EW&N$8F_^;Q*$D8jj)>_+5}~DM!?q4~YdzfA zi0OoFL0b*pT3!WJlA(L2J&(Ivg)P1DlvJ=S<-(&%L-4?V1kw==Ws5xx17@uA>YqJN zll9+wB%rS#;QWjJ3%tc-q-HBA#3%b*^xcc!iT5WlfU#{UpJW8Q`J0zB##9X-W55%K znCEY1ek}}imRq(zV_LS`NDU+n)(#o8Necrc{J8mrNf{h4bl4|2hGWca=v`-rT8>YM z8>(W%!yLuxuu-$TM8DSNCG~CbCRP2}-6Zk>z?aUo+Yu_|_orC%oG+ zS-w_4AdRl1l0z{-ZFkY4`AFlfwb2MJCfY$7HRF|q#+^R)XS8Bq#wcf0*ZwvF-y}|v zC6fRjIcD!6KfuQR`B3TQjt(kwtHblkQPZ$t=+-uqQtG;HSP@tH3QR%X<1Mb_aKW#5 zwTjalK5|SPll0!txhK;;D|=;3a@C6zN-R638F%)L_jL;tsLs9Re$_fox;4_pC7`2-_KN1K#k4|`MYAGc_ z=;b$)e7L#i1MKq{!q$d@KE$mN(NPK2utjkH;6A^?dn(8*g?X>{Nh?x%ej>MqhiQUh zpnRQtPwk^g`LG}ba1ca(?um6L%)SIS96~?EuX#u$AMEB2A3U|D%t) ziWOp#Z6|`3d(mqW_SIM)raY2nNW6b>3OxX(0d`4PH{{f(k zRCn7-)(GS=SywFjYDlje1UKJ(NlD#XGC%(xS193ate1*0t3c2AGfe{g&_DB65m9`? zPtMnFvF~5b08#0YODl6E1qT6E;XRhp;7={PEypFLxoICsNf3vj{H*?QF}0AlHLEJ> z+Lt-N6oT$qXm4K8M6WF9l^p+A16;xXeK0h@cTH28r0kdk`N+~)iK z0ndl$`oNmSTGz}uyUsp4e)}V5cKM)Bb*}o|N1rA@7iG}HpCmN5&o_5m0yTSJobqa7 z{XI~cKSCzEk=h~E1RXa%sM0a0Xt)l>2qz6E%zL`*_TcG-bEIMx$jIUw_j{nBipX|>keGi515Hr$a#-0R=xxra5&x;+ zquE?_2#SOy%cDVV1phZ*d{Kvji zYGD`pGjK|dexHEHKXo~$OY6geqZbFmfH0Kt$9bzSJIWT96umqfZvTsAtyDlt<7XPGw47G?=>r9wZ1rvVh5a zqyz>+V!&)(!PBokSX89~Q{js~`ay#uUG=JH*G^9GPpVrPv}K*C!Ouv}#5}j11!bk* z##h-fv2r(W(BaP9^LoJmF%;)0}d@avrU!5$Sfrw^l z6kDGb)b&WN^zW~}$|eaR-jj2Ozom6?@7p<6Z22X$3i#b=tLi=s^pa5xUE`1!1~o4T zX)IYeMc1pcVAcor_&>gPYmJKDp-+{Of?rm2k`57E6i+tyw^-@4n|;jgX9eiyWhRcz z+HY?^r_HZ`bwBR$Fbcj{6Yxc4?Qm@NmVpoMzObz}H3K^07KWC+5fJ45sL$Ex}6VeLE<_p;R4g_U|lY3MAMrx@ll-jbJ3R)|FFHfu{= z9$h~S#k0MAFI6u7Xi$_ck?%@szmp+{!hG?l zc2{h7kQ=<>OhB*hyh>`tDlLNimLEtMh_{HmznGPqQZ_ht(fGBOua5vGi5g>+FTcL2 z_dTJjc~tZ9Jwzxlg>`N9C^aP~zS|g>?e?*JA4|%Y=Sw8UYQv_K%|#s$gkS&qhRg93 z0hEPB+s;8FhIhUx{=B}DRIOes-Y@SoJ;1bN*LJv)c{t_NImYZm4zH;gI_#Tcc8rOF zuGScw>{;{Tp-RngQL;a*5f?n?v2)0T>TB9>{LCbJD`<5PN(%k9Tww9qf!I_f<CACrS?$M>u(3)k8& zl`JDdRls%R=aR+7)Wls}LI0}%$QH@&JMm8r4z-Po2`QlQ6c!FEvom+L8ZCN$)-zsF zZ*QX5_C4tK1F%-v=T!)cL@e38YuD91!5{6l(DO2Xkry%iXQzVV=Oukkz2A0fz9{uH z9v}dpb#07;CNTx978ouBE6si6!TS%Hjp}(NL5mJ(F$XYtL0^~g(_d%3_YhT6{CEcU z%l=@4#*EI|Cxl!NG?(etlHX>&|1OJTa>XIn@@^p&@_HUA0+|zsDFZtA()i@*XI3;K>>mtM#_k(1JpC7>v_FN_3e(7 z>*Nv(0JAoGNZ?&}L_S_vFi}>~r(*!1Br$Z1LfJD)IVk*#-h07Ik zOZ37bWBOxW9e5TIh zoFCf0dG{O!`3ZYCUVK%BFD1$A@X(@n*7q1RK@eKBI;j^jdj`B&ZdtT09XD!nWrl`Z zsgPly+0PZM+j|JZFLH!ASP<7f$|+$hsj9x948h23ZpOAL*%f_5nVt1ImL1o)NA9KU z7m}Hp+FFCIAEZi(_C&6!Xnbkt*_BQ#Ha{NG#_x@HTzKAGe>S*xFvvw|js7?f+y20u z^K&-O?=#7qjKR*5nSRbli1^Axtk>a~2koV=w%<`)AvJ@ka3z6y(SY&AIJ?ThC9&~E z%QNgVfFV>|RMNpOvuJ$|E6_|#rh0M*KsqRsg9KZ)%~WWZe(T#9->H#Jvb6? zv58;5wY9Cc@Ytut&yQc%q6-`xOe7_Hb;q2)wB}eTk9IyZ*QtpkFB@Bk!TXQaRMgbuw6u6&>vu5r zdVaWAy-s(#wc0HwW~5ws#HGV!CuU`Z5)%_s@ol9K6JY-penR$cxs-WBOE2bojfqMs zRJnMvZ%4TrkB6DJgmVv;i3Pb>5wRj{`ww_%Cvh|Pu&7S8K(bvW_^ILeB6It|ke#TO zO{&ht9l}0LQo_ilA?S_{KUy+K`l{bAuYi}#u2tvey$ETdJUk4R^^JDuGF zfmm7=rd{w8p|7nI2Lphjhl%=OuV3GTJl6Gw|I(j^0pW4}_PA|#p8=KYw}_I8++!gZ z4_ZTNC~FF+o+2y0zBo_f7wRaH%T-(j>HPYeE9B~=A7*-r$lh@Hwr}Ok&G8_Qi^lot z#>dAyjVj1k>F|GKkR@J?va@9>7bxlL-I#-jxT7I6<#@IsMk!= zDwziW+%|Z~rSn+m_A@3C{hSYp^Y0+60ESjS0 z9f*0mX;r5=%lE$P|AZd=2}~wKs`t^qjow-f@zZ2O5g94CXOj0cA%T15ph`pL0JJQU zp2Wb5JT7hUpeCFw6SuKJ!`1WV9kYydY8tmhge2M!TX5`V3oxpqd`H?kx*9Uw)G14= zuvyLug1q1=LT#wAl$B@kmr?xbK4L08yZ7+B%%1(UJ=sIaFo1!U+3W1t^nq#lfVr?j zunqlmQ2FsgGo;VyMyoMHEwQ9{ER--b>CpKmH-ql}BNn5zeD8Id=%B3)^Z2mm+m#k16rd&OD zUDKFN^CK;+UY)m*7^^<?p;^@1m+9)5Den?9ved)ade|Y^>!wz9Mycp^z@%brlyuYCjjXJYTQdlcU9X~ zhYG4AX6|a2$-2?}S^)eD&%m#>B^|WOExXh^3^m7nWJNMPK}#)7vC-HI`f_^r#qlhW z4J-;iOawkpy6HJ~fBu0L{Vb~;mAgcpq4o087#o+Ut9rBQwV)?zziCz9&CbcF1|FN*9|G@f)o&yjxyqVYOez&$KN^e8JgpXEW9vFjAlZ2HcWu6vg?yZt9|KbKmeLsF_ zcMh**{#xlQF-7@SDB?3xmM-h<3q=RbW&+a1#E9vx@8zjFVaFk zc}4oVzIg`L&WW!(r>7RcTl2o2erh&v_Ib@B$!7s^u&qQLMdr$yU1~SW6?edC<`{`kLgy*oF`J4c6;rQws~$1i#~Rty6qqL$=(Zr1i=HqY8tI z+5uV~UsThoyB*}29|s3Jk}6gO<#+eCubQ^=jB#ZL6h-JZ%mGsQf6tbeE696itM4#5 zlp(-FR7T^Iq6-3aC+f6Ad{Yd3Gw)E;#5MDKrQ5UP7$vTlP{w)af6zFjDaipCerbXE2IA=_ftM(MU+2pM zXxy;E3Jd$Pl+rkas^EMUA@RFfqz{CyO^jK6Zw7sNh)PG-IhXHBM!f0-7dd0y!diuk z$Jc?&ze=YtF)L$|oHpU2wB|!a4ghaMgu`@V9g1b+Ks_j9gH2=2r!9=%2 zb+|TCz;^YSIB&<F@((446$>?#jaMjS6X2;^(#iue{1VrvT zqx9B{ER8j2VQR}kVAujd&eo)m(hX>A|NZNN>N892joG-mtP+9uO6(-0Kru1%jV5%G zo1?J?5g_*Ua8}eCh`k1g6+V%F_kJ8sCFNVuy530n)BURowY72UNACd`t52AYe1vZ80%~@DcizvOdf5 zmB$Q_$IerR#SEY#3IUQE71~kM_9j$OaB>CJ8B9_5B+>MpfE0y!mg4byI?Ahs19G>UI5ZDU|&X>_(OkDY~?rE#Yl(>%V7?jffGddiw+F! z5A@UN3xC;BQaW2?rV-7F^Rm8Ktn)bYbZeP}%J@Jj|89B__|;wUt^j4Q-Nec60Yp|( z&B!ztnLfzvsROQB&GMIxy3FR%ff6~=RR3p;lA!Td{4w>>^;e}W!hd$Zm|T2+g0yPM zpkdzF3t(l;EnAj7AY#+rslSOAJae!Ma!QrW3=ncf0_JYVkWauvj_67C+ov8ee*cJ% zkO~l=Pm}=8THvXwkCMG)4ArAq^{9FclqSpr3o?ngjAN?+7UJbzqf)(A5ymBbmke8$H^1iwXIDMS=K7 zZiN)5cfKC`fV%jFn}J)XQmDsM`udZ>R&N(>HwCWV@$vu5BQze+*6D87bobdVd*=9~ zldaZ(8fov-#OZoRiNcf)JBOBh`#D?&lpA{$V}B=5RDigkSB>wh@n=k|M?FW}-Nx48wHiGcfA8(UmV9dT zMsdZRovZB$`pDwG=1SFfynIJ;3du ze5gELWIUVZ`%~{-|+Jn14vYOQsbfmn6mOF#1{ErF%j0mpsC=(yQ zodU>=j?fH}Md393zcz|s7^Yt0Tp*m;xlDdnrPp6e3s|W=y^c(mr!(I|hKj%?xopsP z(qp;aPM&Iwt+Z=X?w{2wnzMvzy1WmFxgFBuYKKikUs|=&0i}0np4kG3h!n*o$Dw0< z_BUCoy21?jY{75z|535qXi_ou^`E0^&dr#3zE>4!zdxo};?NeqbkRanCo;FWX)3+t z>Qq$ab69TPd|f1kH15PWxB$SSeoI>5U@|g#j>JcY`CDi&^<@?A%!rP`O#AeH;jn7L z#smp|;fD(hg%6=?!}`Q#<^LQaf$BX0qK^3X7tX|;E`rQVW7DM+l%-eOmXa}Ew`l%Y z^uoy~?kP~2`kUAyyQ&@Jpt&k$@2X(Mu8gS@i*!fW_`=Ce{(TL5Y_soN$BVzRtweg^ ztAR-3?!sNnb!sG`?Sr&%c8uNs@$*H7ng2iMq>?agp=Focke#@ATF3LICxpm8s`q^z z45{`6nBT;YHT|P5O2Vw52Xtv%P_rcu9zymJdssZ@N$F<@#1vz6wb#lBGi%qQ=A`50 zKa0S?o|tyX=nn=EJV+DHWk@5fozwzHa){{acVGA7Fxm8CO2Z;YqN`&IJ9{qx?89$; z&rbyyK6W;&S<_S^5AT?o6|it=-Ax`ord$7i?5}(__J8&l;3_kpdK>&>TE^rEqqNFf zK7OfW{B3+U7vc_g5hVl;EpQ0X_C5sLCfyHTWQqVYEL?a5|9uq=sP`l{p<#J_k7WQv zR#wSKA{V*P_EwCV9GAd%2ORW- zz^)i2tj#1?%uVn$Y=lBn7JB-gx$88O)cx;lC<*;Y9lK7knaa*~K12Qy{}L{qg9muu z_-MC>FlUGHJnB%;85c(b+WhnoUg?1vKo-up4qHn&$jh(e?iZ7>tmF)z8Llw?aXdmj zJD8-WluWuNC>V&cyu1{U(bs`#UJAj1wO3Tse$0W{ls(8T^KR%mlZ z2bs}0CWLzDv0gLGZ@loD^pqHWyR5`#{1l3DiqPY$}C4G~sv+oKOYmHVwvV|Tzg zo`37%-kZ#xuVSZ|@iDhlYk7u)bg>{Alk^A2>89lDaicyjfiYWRU*&0%+2FthXSAe-ZI!ZG^WeR;F00@NPB^mHu zdl(CJzcX(cbGiXosed?6%7F)FPTitiYbmnVSDX%S^L_(O3zGQgvizEXbC4^D$lb({ zqcr_KA1!?W`ya)oKz@9uuiyP^wzMh;Qa)`nk!87d_du;K^lzbm_}5rwLBE7(GC=mtJ6pb#S6i`Ch&^jSPaN0G1yI1j}9Pwc!I&}AdoUNZUM?$vTt0NPK zpW)l~q-&R@Q=5&m5ud&;+pW>6@A~^rFxaNw%)=%Fc!-(O{1fXs29*tU=_N~whH8Yi zp0*62YoCc5DT?%VvTl^uAhaFkW_tub5x@S2@JcUO-#+4r^9l|S?OMeKcp0m;^B{8o z-QO@epHP+zAWA8Br{&1YduP_24}iyvScK$e&3sO&|6UKp;6ABzkS9YPCFhE(zP|YW zU1g>p{qmsC^3r{3sp6`Av8s7tvURB;;`en6WJ6lwkPs7OCk=sJJ&djDrOBfkA^_R$ z*Ah`MnQnVadRJ?y|A|{y^q)(p7{X(aR zT+lX+<-G!oxlk5+FZn;~YKbApY_}P$0qFw*%pdKVW>u6yW&FRRd0>tYCDyHo2n{3m zH}D>6uGYL-2DJCeGk(Ptf$s8##oW?*p(?ZScO4Xt$Z-Z(p!fLCkHG!27EDB{f0&N_ z`d_j>{>Lv8gA4n+f&W79Pv*cA+Y9}txII8!mBqy+7m?=>q3!AE=9 z3wK8l?{EK!X<(w^={k*~SaT5~N0A087j%jr;XcVUABqSJ2(VwCJBNV$i<8f^_#YXB z3t7&60HSm4wsn-e4-qYEW^x!=S?>~*;DS*&uc=BQe?)7I5tX|oBd6gmgEfCW1oib_avx~ga6w*ZmmE&{T5{wKmfTRUMPfs9FCLZ|?xmDEC`MIapZdzTa=qjbbQkMQ^xsnw_zP*H6tJAo}EW zT3TTBchqNY|FsvvzSz7SbH_DxZCn1Y6>xbl`S<+H`qu*wg$G#Ozt>KIEcuUjNK5wj z3P|z)y%f_qNsK2+CG`YEVJ!=b$dTYzR#u{u;=H@BK)gKs)vMvTF{J+_<~1nEPA>LJ zc5;Kx&R>PyuAE6Bc*CQFlsXXpJ%T(Gs=%{xo%8%d`Rq<;MEDbJ?b~aP9?+=Sw2OlY zhcI|O85wqDl&CIq4l*$7LrXLy-C+@ol2DCH%xq`?W}J+k?=~G>!ThxwE<{^LM^RN1 z65IO{q^3+v(EYVb4rZ>!5aN4HI>WNyIk>n$ddYC^EKIL;w=`|mutxhH7GVgvXt3*c zJ?<;~AVfrS3!7l|Y;jKsYM`pkwZbEhpV!SQDLcZCtHlHO%Gf;FkS7i&Q|(3-8E0bT zDJdx#V3u9nn}G82Z??pf%l1p}!=8*yj*DcflJU~vjIgrm&!(Ga0JzV1oON18M*l9u zp3vxNFmQ@z^%@jwzdO4{KuAa?Me(GoGdd;3ckr}<2FOZ2-?_veb_*w%EbO8>|(EKEBUM)I-cs-mGlN&xaIg_(CTpa1(b84VN{`zr`?xM9vG^ zx}DMxW`VAO+#4>a)3NrN>sBBC>1LoZ~wyqt-+uIY5O zWc5B`qw&+D3YR{G{E`v}TS8W&xE}jabB?7ZyTt73cX%;W4b?%x!OJl+Wsm20nM|Ck z-G-Q~HE$@1&_kS3C5k?P)S&;^81P;cIPQ(BY8cNqj?*$TxU^z)QBpJ#*^&s7+1~7P7T6G4#|EN}XDkmudid)x;9K;xb@!+JHgN|BOlz)Gr;D?@ zu|ou&Yb0|}kd^{G;HkSC@Iop!&uC%?rO|mOw_C#dcs$QMzszm=EyyxEZ7@2c2sbtX zE*`I@#D5$ThL0WjI)|J7+P&Mps<{|j*!u>+2NUSk-nkR9ly#00{r2s2&gzUc7s#@r zfGn%?^xLF3Fh;H!mmo4%}DeXOMv81czwqM1Cnr-mof9p%DN7pr;B2L_=h`(hit%nlkwF1V6ROBsQ`xKg?`yn z^$8a67Gkzj_miUWET9|Dd=KvSn~rMjuf}1~gqNT6sM+%n;l*%b11T0YGP>VSjn%4u zf;cmy?NtLvG(M~tQu+CQ{urLES1Z87mG&j z1B@29xKWjrpP7g?u*CsE@CmlIVdM#m%_Rf-rj3&(;wi`AceUOf2LdoXaw(7`NBzEs zhJx?M4rL-EE=mI91rf;KB4OCIP4O6Znuv*u*WKvU1CrO&xNO!7mtMosgd=B;ShBj# z-GXt*)8{q2!0zs~U`KSSzM`I5SZC+z@OI$0DjmBJebQTCFro$Wg4c!|(I>$d7Nceq zR8*i{Mb#ZvkU&>pQU7IzFij%?Ou7R}Ej?8=wJe|lqLqWbL5)p4yL+%Tf_xw*4i0Rr z)y_gqmko#0Q&3#N?`9nca>yr*A0#CuZ$V_!Sy4!R&X_E%3LP#rg@Vxu9VU!jPkozR zbhG@)tiG~D5G6+@O=+sC#$}f;0brr=&F`?Ej*dzB!scmw;_+aq3u8gVSQG(BjqzIk z$cDgPEu%0Tgel1hvm2VeHh0~1y11ga2e&iRfBNmC?>Up9#M z4s9k&bkNfDL+Dh@GIoxjO)hhK{B>QXRYqg^Bbw!3l{ZdWyj%@;p+)5`ebn#Xy*qaI z%kr1b9$BhnD3#E*TmDhHU2uukpb_$iC6WNlDVM;dgVGUHS-$214Gw!~Xi+6AawT=|jyM zl$HsH%{U}{0402|xT3ov3lhc$=RLGSN-wiISHb+blj0t0FnD~qs{t6Y`tvWdZ`kkV z$tFj+EqY)f_CT}1_*T}63c;DSJRZkUlqgc++Ymf%!z^}|TQhmBk%RasQZb75j-(la z<&YQE)u@T2dcfWkSnQ6D6QMWtv7YQr2Qj8{H%5Hj*>h-uS&f8f-dp6EdMJl7U}-hl z(b384sCaOX3sjA^#_%C8R(qlv=e|r;cw0|5V;;NI2)3rbB}z2Ukd~F*I!q)fQGYd7 z?ObPJK>$WcN~}Z7N$01P)YKlIMFP$Z zrv00}Jp~TMf^ht> zr}51{537FpQq08E6sT%Ob#*;Vc1%P=B43oEWEaD=`?48JOG!DMpQ_!vwc-3dN`@Y8 zSLQuz)4}MJd??6Jkr*lyD9LuESc-gE&)MDG2PaU{q5Xqv!4LMMDDpY0y}bptHobHO zOcxM){SG0Atl{U3EWim~S-}Fm(zaIqyYo#bI(60o!-se%x7$z;_!w-k6=4feAR#);(J=Ih2E&oBYuuB69N5 z4=+sE@MB_P$)#eBI*oyxW{t`th63ibTXWSY++A$Nd3tnYz~9y81Xc?pph<9x2Qr?w2OFK)GK`cTt?}lRx#1%#R;jZO5Q*_`1XUn&)hKY>F)h-{=7#DWungJoK~apC_Z;B3*si=@!QuCX5z87ydNteqwEp5uq`PS1CMK%T{XCkU zhgs}a*4FJE2p7OX+i}fg=3*il%^h++SDth|yuu;c@rZM?mqY@thye#)hH^n;3!vCc zr3WQZ7DCk!zZ+&uMn=ZTdK{m(nb7en!&FyWYWwDXQ{F<+{Cs+-yp^Rjvm}K`Cm5=} zqWoGWZ*=u@UzwP4j8zh=%@5f+Eu3I31Z-qPXTG#qR)Oo>-7nW-V@wCNp@PwR&y26=e<#uU>hDg9{9g zygYki-@HQJ${JVGfJ@4GTmid59e=si2G>|xGr=Pycu)|qTS1?l_bCRrLJB{m#E2L8 z%`}^yX1?Z6OyzmVV1)+`vEwaYPe)yf!+zl36fp}2N#}HCy<&E=g1&n7R9CmTuU2JW zB&hRfY)WO@V|uriWLH5f59ZSh-PF0+JyX&*BkJ*ECkurq;0+BYdfKj1j42*jt8J57 z%UhqKIZEAP!Tf{!n1X@9!m>DJRNIF6Gj+i&IA|y($>S(?1O)m#d^mQV(N~EcX8LAi z9(hjeXII4^5HEb@QW5u#_pbVdbepm!6`C#1S^QYf&k^~~4xx1 zsp9mD_ooAhk!#ynz9Q|riB|)SzPsUCt$7tvawt7-Kg^!@M`I~p5^*{-r&6Vpdj6acJ4}p&ZAWGMgDD#N$@W0q zLhT#!m?+oXZUmMjfUoHP`SNm<)Y-gY-t+S~)3;`nTkw2-C)KT9hhj7eEwRp=&TLv= zUTj-@-Y_+y@)DOmfS^W8iaql^XSOLQ(y-Z0h}&kRk&^0eRJKMXimW2UAfryn*Q~LO zm%rM>_%g>yguY#+Fl3q0rl6;1yE|X!*XvnXRrx^9`u#%cG+hq@%gZO^K91KB-ulo_ zT-IaVk*RC;JRDo3BQ-^dYxo4iy((>!pW;1aCa_YosiWLtBVBg8^TPGhZ;$kOpvH1e$MgBO>E{6jbK2?%2dv`fR*47s0}2(pCV{wRNev z=rOUgQ~V@zzuHtzYGzwXqpAJK+Q>+EW5~DI{;WX$t!Iy;>ig5)XYMq&;Z>y4@8JG0 zbyf?XcZb}rYf{O+#*B}*Q3Uj?^7j1nRmM2mw?^MueL6U@u)u*)Tfuo&m#o)IC7g7> zRGyCHv8=DJKXrG{@k;4@f;g{yo=TG&H=xGQTxNaT_<7Os*JaUx{(dTJP6fWa8hng) z`|Q;eK#LQOz0v6G9>$?kU0V&`>8Rix<=maOGlpA=pl?l@ctHZvJqChGY|TY*kM8= zs~{&G!)K?fp=0&xe0+R8fhsaGvaqB^{K}_AW+FB=wusYe!qU-^VmYwGp65|n4`oPK z=E}P>zkJxzGE!o$67Vu79eo zv*IRsk?HLeCvH=~C2suE1 zM5iYJM}V^%xVu~Oh4*a?hro`CogLKC$%!!$G81{n;Nt+NqwI|xNnvgG*TA~Dw3L)L z9DQaa!dycK&J9~CQ3^P-pGOURrf-jr)sIcOJtOt~_I8V~mL7cn!u+h~p@}@d>z?v* z%G7}3u}Sx0|0UWmd-3@6*wWQiB1Tn7tK{%-Am$wogSz>C>s8~_=VB)_g`!NuRQ1w+mHjJQ zAQ?sP-l>+FF=1?NHsswg&MqulEX-PoiQAIO(KBdBNy3{X-aAhNFeI2K;xNrg( z^##9u=E4G(-evow2gvIs+Bv)YuG(96R#)oiA)*%;?|-Q2=>^WbdUb4It?GBRBskt! zYi?^R@3z07PIZ4|Ar~Qmodf7rpO(U@o}fDmHC^r2q5LFc+u35ie7S)$lz|5xUuV6YQhkSE4dLUh`wKB3;Lbkf3&Rq${M+ zyFg8-tmhI+C8edM#do=yU07IHdzl&)Rg{89Iyv~j&!kmxr#4kupj_8OAkfmvrXmhV zSz+7a)L}w=nhiDg!vc$&J`jcZ%;=V9+rU3?=pruex%ytmk*@@0*~_aD*bQ|Dhp_V} zQaL0|2knlWyu8uFW~4&i25&diwhP%~Q=x$(PB>D|I@?tz99&v4Nx8ntu)yl@KP}b^7v5aa3`yw3Vp!+{|=5~s;62Qqo&8>oodV73gChKqj zCt=N0L?lz+Zw&$$%X{8OC{z_e4ef#6c>W|uU!tOyLTvMdNpiwXG&p`9R92!S@!bN zfIILHfEnZv7B0)mGE!5U1Owq2$eE@;4XAEOagu1-u4vd&W3@RtSz0RV)!8y>En?_3 z;Nu|4G8gZ?RRd(S)MIk;Qebmk^+88&kpG_7(NjZ6KR=RDM*sb~ QLa?8d5|= 1.0 multiplied onto the *forecast surplus* before computing the clip. Solar forecasts systematically underestimate peaks; headroom reconstructs the higher real curve so reservation and floor are sized correctly. | -## Vorgeschlagener Algorithmus: Regel "solar_cap" +## Key finding: today's peak shaving can cause curtailment -Die neue Regel arbeitet mit den vorhandenen Forecast-Arrays (Wh pro Intervall, -Index 0 = jetzt) und kennt zwei Fälle. Pro Slot `k` (bis zum Ende des Produktionsfensters): +The existing peak-shaving rules (`time`/`price`) emit a **cap** on the PV charge +rate. When the PV surplus exceeds the feed-in limit, that cap blocks exactly the +energy that should go into the battery — the difference is curtailed. In the +reference scenario, plain time-based shaving curtails 1.8 kWh that the new rule +recovers completely. + +Conversely, time-based shaving already helps partially (76% recovery vs. 0% +baseline) because it shifts capacity into the afternoon — but uncoordinated and +without any guarantee. + +## Proposed algorithm: the "solar_cap" rule + +The rule works on the existing forecast arrays (Wh per interval, index 0 = now) and +distinguishes two cases. Per slot `k` (up to the end of the production window): ``` surplus_wh[k] = max(0, production[k] - consumption[k]) @@ -41,254 +55,260 @@ feed_allow_wh[k] = feed_in_limit_w * slot_h[k] clip_wh[k] = min(surplus_wh[k], max(0, surplus_wh[k] - feed_allow_wh[k]) * headroom) ``` -**Fall A — vor dem Kappungsfenster: Reservierungs-Cap.** -Freie Kapazität minus prognostizierte Kappungsenergie wird gleichmäßig über die Slots -bis Fensterbeginn verteilt. Ist die Reserve größer als die freie Kapazität, wird das -PV-Laden komplett geblockt (Cap 0). Damit verdrängt einspeisbare Energie nicht 1:1 die -Kappungsenergie im Akku. +**Case A — before the clip window: reservation cap.** +Free capacity minus the predicted clip energy is spread evenly over the slots until +the window starts. If the required reserve exceeds the free capacity, PV charging is +blocked entirely (cap 0). This prevents exportable energy from displacing clip +energy in the battery 1:1. -**Fall B — im Kappungsfenster: Floor + kapazitätsschonender Cap.** +**Case B — inside the clip window: floor + capacity-preserving cap.** ``` -floor_w = clip_raw_wh[0] / slot_h[0] # Pflicht-Laderate, ohne headroom -cap_w = -1 wenn Gesamt-Surplus <= freie Kapazität - = floor_w + extra_wh / restliche_h sonst (extra = freie Kap. - restliche Kappung) +floor_w = clip_wh[0] / slot_h[0] +cap_w = -1 if total surplus <= free capacity + = floor_w + extra_wh / remaining_h otherwise (extra = free cap. - remaining clip) ``` -Bei Knappheit (`extra = 0`) gilt `cap == floor`: Der Akku nimmt **nur** Kappungsenergie -auf, alles unterhalb der Grenze wird eingespeist. Eine Priorisierung innerhalb des -Fensters ist unnötig — jede absorbierte Kappungs-Wh ist gleichwertig; schädlich ist -allein das Füllen der Kapazität mit einspeisbarer Energie. +Under scarcity (`extra = 0`) the cap equals the floor: the battery absorbs **only** +clip energy; everything below the limit is fed into the grid. No prioritization +inside the window is needed — every absorbed clip Wh has equal value; the only +harmful move is filling capacity with exportable energy. -Der Floor wird aus der **Roh-Kappung ohne headroom** berechnet: Es wird nie Energie -zwangsgeladen, die legal eingespeist werden könnte. +![The solar_cap rule on the reference day: reservation cap, floor, SoC comparison](../assets/solar_limit_algorithm.png) -## Konfigurationsdesign: Schalter pro Regel +## Configuration design: one switch per rule -Mit drei Regelsorten (Zielzeit, Preis, Solar) wird der bisherige `mode`-String -(`time`/`price`/`combined`) unübersichtlich. Beschlossenes Design: **ein expliziter -Schalter pro Regel**, `mode` wird deprecated und beim Einlesen auf die Schalter gemappt -(`time` → `time_active`, `price` → `price_active`, `combined` → beide): +With three rule flavors (target time, price, solar) the previous `mode` string +(`time`/`price`/`combined`) becomes confusing. Agreed design: **one explicit switch +per rule**; `mode` is deprecated and mapped onto the switches at load time +(`time` -> `time_active`, `price` -> `price_active`, `combined` -> both): ```yaml peak_shaving: - enabled: false # Master-Schalter (wie bisher, inkl. evcc-Override) - time_active: true # Zielzeit-Regel (counter-linearer Ramp) - price_active: false # Preis-Regel (Reserve fuer Billigfenster) - solar_cap_active: false # NEU: Kappungs-Absorption (Einspeisegrenze) - allow_full_battery_after: 14 # Parameter der Zielzeit-Regel - price_limit: 0.05 # Parameter der Preis-Regel - feed_in_limit_w: 0 # Parameter der Solar-Regel: Einspeisegrenze in W. - # 0 = Neutralstellung (Regel wirkungslos, auch wenn - # solar_cap_active true ist). Formel: 0.6 * kWp * 1000 - feed_in_limit_headroom: 1.0 # Sicherheitsfaktor >= 1.0 auf die prognostizierte - # Kappungsenergie (nur Reservierung, nie Floor) + enabled: false # master switch (as today, incl. evcc override) + time_active: true # target-time rule (counter-linear ramp) + price_active: false # price rule (reserve for cheap windows) + solar_cap_active: false # NEW: clip absorption (feed-in limit) + allow_full_battery_after: 14 # parameter of the target-time rule + price_limit: 0.05 # parameter of the price rule + feed_in_limit_w: 0 # parameter of the solar rule: feed-in limit in W. + # 0 = neutral (rule has no effect even if + # solar_cap_active is true). Formula: 0.6 * kWp * 1000 + feed_in_limit_headroom: 1.0 # safety factor >= 1.0 on the forecast surplus + # (see terminology and scenario 4b) ``` -`feed_in_limit_w` ist bewusst ein **absoluter Wattwert**: Die installierte Leistung (kWp) -steht heute nur bei fcsolar-`pvinstallations` in der Config (bei Solcast gar nicht), und -die Grenze gilt am Netzanschlusspunkt der Gesamtanlage. `0` ist die Neutralstellung — -zusätzlich zum Schalter, damit eine unkonfigurierte Grenze nie versehentlich als -"0 W Einspeisung erlaubt" interpretiert wird. - -### Prioritäten zwischen den Regelsorten - -Dokumentierte, feste Rangfolge (keine Konfiguration nötig): - -1. **`enabled` (Master)** aus → keine Regel wirkt (inkl. evcc-Laufzeit-Override). -2. **Force-Charge aus dem Netz (MODE -1)** überstimmt jedes Peak-Shaving (wie heute). -3. **Alle aktiven Cap-Regeln** (Zielzeit-Ramp, Preis-Reserve, Solar-Reservierung) - liefern je ein Limit; das **strengste gewinnt** (`min`, wie heute bei `combined`). -4. **Der Solar-Floor überstimmt jeden Cap**: `final = max(floor, min(caps))`. - Begründung: Caps optimieren Ökonomie (Ladung verschieben), der Floor verhindert - **physischen Verlust** (Abregelung). Ein Cap unterhalb des Floors würde Energie - vernichten. Deshalb gilt der Floor auch **nach** `allow_full_battery_after` und - auch bei hohem SoC (`always_allow_discharge`-Region) — das Kappungsfenster dauert - physikalisch länger als die Zielstunde. Konsequenz: Die Solar-Reservierung kann den - Akku erst nach der Zielstunde voll werden lassen; verlorene Energie wiegt schwerer - als ein später voller Akku. -5. **Statische Inverter-Klemmen** zuletzt (`max_pv_charge_rate` als Obergrenze, - 500-W-Minimum via `enforce_min_pv_charge_rate`). Achtung: ein konfiguriertes - `max_pv_charge_rate` unterhalb des Floors macht Abregelung physisch unvermeidbar - → Startup-Warnung vorgesehen. - -Sentinel-Semantik bleibt: `-1` = kein Limit, `0` = Laden blocken. `-1` erfüllt jeden -Floor automatisch, weil der Inverter Überschuss dann ohnehin greedy in den Akku lädt — -es ist **kein neuer Inverter-Modus** nötig, der Floor ist die Garantie -`angewandter Cap >= floor`. - -## Simulationsergebnisse - -Alle Zahlen aus `scripts/simulate_solar_limit_day.py` (Referenz: 10 kWp Süd, klarer -Sommertag, Peak 8,9 kW, Grenze 6 000 W, 10 kWh Akku, 400 W Grundlast, Start-SoC 15 %, -Stundenraster). "Rückgewinnung" = Anteil der ohne Akku abgeregelten Energie, der -gerettet wird. - -### Szenario 1 — Referenztag - -| Trace | Eingespeist | Abgeregelt | Rückgewinnung | -|----------------------------------|------------:|-----------:|--------------:| -| Baseline (alle Regeln aus) | 40,50 kWh | 7,50 kWh | 0 % | -| Nur `time_active` (heute) | 46,20 kWh | 1,80 kWh | 76,0 % | -| Nur `solar_cap_active` | 48,00 kWh | 0,00 kWh | **100 %** | -| `time_active + solar_cap_active` | 48,00 kWh | 0,00 kWh | **100 %** | - -End-SoC ist in allen Traces identisch (83,3 %) — die Regel verschenkt nichts, sie -verschiebt nur, **womit** der Akku gefüllt wird. Im Slot-Detail sichtbar: Vor dem -Fenster begrenzt der Reservierungs-Cap auf 625 W; ab 11:00 hebt der Floor die Laderate -exakt auf die Kappungsleistung (1 200 → 2 500 → 2 400 → 1 400 W), die Einspeisung -steht dabei konstant auf 6 000 W. In der Kombination überstimmt der Floor den -Zeit-Ramp-Cap genau dann, wenn dieser Abregelung verursachen würde. - -### Szenario 2 — Ost/West-Profil (Peak 5,6 kW < Grenze) - -Keine Kappung erwartet; die Regel bleibt vollständig inert — Trace bitidentisch zur -Baseline (Regressionsprüfung bestanden, keine False Positives). - -### Szenario 3 — Kleiner Akku (5 kWh, Knappheit) - -Freie Kapazität bei Fensterbeginn 5,00 kWh, Kappungspotenzial 7,50 kWh: - -| Trace | Abgeregelt | Rückgewinnung | -|------------------------|-----------:|--------------:| -| Baseline | 7,50 kWh | 0 % | -| Nur `solar_cap_active` | 2,50 kWh | 66,7 % | - -Zurückgewonnen: **5,00 kWh = exakt die freie Kapazität bei Fensterbeginn** — das -theoretische Maximum. Die Reservierung blockt morgens das PV-Laden komplett (Cap 0, -Einspeisung läuft unterhalb der Grenze weiter), im Fenster gilt `cap == floor`. - -### Szenario 4 — Prognosefehler (Forecast = 85 % der Realität) - -| Trace | Abgeregelt | Rückgewinnung | -|-----------------------------|-----------:|--------------:| -| Baseline | 7,50 kWh | 0 % | -| solar, headroom 1.0 | 4,96 kWh | 33,8 % | -| solar, headroom 1.2 | 4,46 kWh | 40,6 % | -| solar, headroom 1.5 | 4,23 kWh | 43,5 % | -| solar, perfekter Forecast | 0,00 kWh | 100 % | - -Erkenntnisse: (a) Der Algorithmus ist deutlich forecast-sensitiv — eine -15-%-Unterschätzung der Produktion unterschätzt die Kappung überproportional (Kappung -ist die "Spitze" der Kurve). (b) `headroom` auf die Kappungsenergie verbessert die -Reservierung nur moderat (+7 Punkte bei 1.2), weil im Fenster auch der **Floor** aus -dem zu niedrigen Forecast berechnet wird. Der daraus abgeleitete Maßnahmenplan wird in -Szenario 4b entwickelt und quantifiziert. - -### Szenario 4b — Schwerer Prognosefehler (Ist = 125 % der Prognose) - -Die Prognose sieht nur **1,36 kWh** Kappungspotenzial statt real 7,50 kWh und erkennt -ganze Kappungs-Slots (11:00, 14:00) **gar nicht** als solche — ein Multiplikator auf die -prognostizierte Kappungsenergie kann das strukturell nicht reparieren. Da batcontrol -**keine Live-Messung der aktuellen Produktion** hat, stehen nur prognosebasierte -Gegenmaßnahmen zur Verfügung; zwei wurden implementiert und verglichen: - -- **headroom auf den Überschuss** (`headroom_on='surplus'`): Der Faktor wird vor der - Kappungsberechnung auf den prognostizierten Überschuss angewandt. Das rekonstruiert - eine unterschätzte Produktionskurve und findet auch übersehene Kappungs-Slots — - repariert die **Reservierung** vor dem Fenster. -- **headroom-Floor** (`floor_source='headroom'`): Der Floor im Fenster wird aus der - headroom-korrigierten statt der Roh-Kappung berechnet. Bei greedy ladenden Invertern - ist der Floor ohnehin nur eine **Erlaubnis** (der angewandte Cap wird angehoben, der - Inverter lädt `min(Ist-Überschuss, Cap)`) — es wird nie Ladung erzwungen, die es - physisch nicht gibt. Repariert die **Absorption im Fenster**. - -Ergebnis unter beiden Bedingungen (Ist = 125 % der Prognose bzw. Prognose korrekt): - -| Einstellung | Rückgew. bei +25 % Fehler | Rückgew. bei korrekter Prognose | -|------------------------------------------|--------------------------:|--------------------------------:| -| headroom 1.25 auf Kappung (Roh-Floor) | 38,7 % | — | -| headroom 1.25 auf Überschuss (Roh-Floor) | 31,8 % | — | -| Überschuss 1.1 + headroom-Floor | 44,9 % | 94,5 % (Verlust 0,41 kWh) | -| Überschuss 1.25 + headroom-Floor | **94,7 %** | 62,7 % (Verlust 2,80 kWh) | -| Neutral (headroom 1.0) | 31,8–38,7 % | **100 %** | - -Zentrale Erkenntnisse: - -1. Beide Maßnahmen sind **nur zusammen** wirksam: Ohne headroom-Floor ist die perfekte - Reservierung wertlos (der forecast-basierte Cap blockt das Laden, während real - gekappt wird — deshalb ist "Überschuss allein" sogar leicht schlechter als "Kappung - allein"); ohne Überschuss-headroom ist der Akku bei Fensterbeginn schon vorgefüllt. -2. **Ohne Live-Messung ist der headroom ein echter Trade-off**: Er muss ungefähr zum - typischen Prognosefehler passen. Ein zu hoher Wert (1.25 bei korrekter Prognose) - lädt im Fenster einspeisbare Energie und verdrängt an kapazitätsknappen Tagen - Kappungsenergie 1:1 (2,8 kWh Verlust). Ein zu niedriger Wert lässt Kappung liegen. -3. **1.1 ist der robuste Kompromiss**: kostet bei korrekter Prognose nur 0,41 kWh und - verbessert den Fehlerfall bereits deutlich. - -**Plan für Prognosefehler (Festlegung für die Integration, rein prognosebasiert):** - -1. **`feed_in_limit_headroom` wirkt auf den prognostizierten Überschuss** und der - **Floor wird aus der headroom-korrigierten Kappung** berechnet (eine gemeinsame - Stellschraube, kein zweiter Config-Key). Default `1.0` (neutral, verlustfrei bei - korrekter Prognose); dokumentierte Empfehlung `1.1`, bei bekannt schlechter - Prognosequelle bis `1.25`. -2. Nebenwirkungen dokumentieren: headroom > 1 kann an Tagen knapp unterhalb der Grenze - eine unnötige Reservierung auslösen (Akku später voll, kein Energieverlust) und an - kapazitätsknappen Kappungstagen bei korrekter Prognose einen kleinen Teil der - Kappung verdrängen (quantifiziert oben). -3. Das 15-Minuten-Raster (`time_resolution_minutes: 15`) reduziert den systematischen - Anteil des Fehlers zusätzlich (Szenario 6). -4. **Zukunftsoption** (nicht v1, erfordert neuen Datenpfad): eine Live-Messung der - aktuellen Produktion/Einspeisung würde den Floor prognoseunabhängig machen und den - Trade-off auflösen — batcontrol erfasst diese Werte derzeit nicht. - -### Szenario 5 — Mittags-Verbrauchsspitze (2,4 kW, 12–14 Uhr) - -Eigenverbrauch senkt das Kappungspotenzial auf 3,50 kWh; Kombination -`time + solar_cap` gewinnt 100 % zurück (Baseline 0 %, nur-Zeit 60 %). - -### Szenario 6 — 15-Minuten-Raster - -Konsistenzprüfung am interpolierten Referenztag: 99,1 % Rückgewinnung (Restverlust -0,07 kWh durch Interpolationskanten an Slot-Grenzen). Das 15-Minuten-Raster reduziert -zusätzlich den systematischen Fehler "Stundenmittel unterschätzt Momentankappung". - -## Bewertung - -Der Algorithmus erfüllt die Anforderungen: - -1. **Er rettet die "40 %"**: 100 % Rückgewinnung bei korrektem Forecast, exakt das - physikalische Maximum bei knappem Akku. -2. **Er repariert einen Defekt**: Ohne den Floor verursacht das bestehende Peak-Shaving - an Kappungstagen selbst Verluste (1,8 kWh am Referenztag). -3. **Er ist minimal-invasiv**: kein neuer Inverter-Modus, keine neue Datenquelle, - gleiche Sentinel-Semantik, additiv als Post-Processing-Schritt. -4. **Er ist neutral, wenn er nichts zu tun hat** (Ost/West-Szenario) und per - `feed_in_limit_w: 0` bzw. `solar_cap_active: false` vollständig abschaltbar. - -Bekannte Grenzen: Forecast-Sensitivität (Szenarien 4/4b) — ohne Live-Messung der -aktuellen Produktion (derzeit nicht Bestandteil von batcontrol) bleibt der headroom ein -Trade-off, dessen Wert zum typischen Prognosefehler passen muss (Empfehlung 1.1); -Stundenmittel vs. Momentanleistung (ein Slot mit Mittel knapp unter der Grenze kann -real kurzzeitig kappen — durch headroom teilweise abgedeckt). - -## Integrations-Roadmap (Folgeschritt) - -1. `logic/logic_interface.py`: `PeakShavingConfig` um `time_active`, `price_active`, - `solar_cap_active`, `feed_in_limit_w` (Default 0 = neutral), `feed_in_limit_headroom` - (Default 1.0) erweitern; `mode` deprecaten und in `from_config()` auf die Schalter - mappen (Warnung loggen); Validierung analog `price_limit`. -2. Neues `logic/solar_limit.py`: `compute_solar_limit()` und `merge_limits()` aus dem - Simulationsskript unverändert übernehmen (pure Funktionen, Muster +`feed_in_limit_w` is deliberately an **absolute watt value**: the installed power +(kWp) exists in the config only for fcsolar `pvinstallations` (not at all for +Solcast), and the limit applies at the grid connection point of the whole plant. +`0` is the neutral value — in addition to the switch, so an unconfigured limit can +never be misread as "0 W of feed-in allowed". + +### Priorities between the rule flavors + +Documented, fixed order of precedence (no configuration needed): + +1. **`enabled` (master)** off -> no rule acts (incl. the evcc runtime override). +2. **Force-charge from grid (MODE -1)** overrides all peak shaving (as today). +3. **All active cap rules** (target-time ramp, price reserve, solar reservation) + each emit a limit; the **strictest wins** (`min`, like today's `combined`). +4. **The solar floor overrides every cap**: `final = max(floor, min(caps))`. + Rationale: caps optimize economics (shift charging), the floor prevents + **physical loss** (curtailment). A cap below the floor would destroy energy. + The floor therefore also applies **after** `allow_full_battery_after` and at + high SoC (`always_allow_discharge` region) — the clip window physically lasts + longer than the target hour. Consequence: the solar reservation may let the + battery reach 100% only after the target hour; lost energy weighs more than a + late-full battery. +5. **Static inverter clamps** last (`max_pv_charge_rate` as upper bound, 500 W + minimum via `enforce_min_pv_charge_rate`). Caution: a configured + `max_pv_charge_rate` below the floor makes curtailment physically unavoidable + -> startup warning planned. + +Sentinel semantics stay unchanged: `-1` = no limit, `0` = block charging. `-1` +automatically satisfies every floor because the inverter then charges surplus +greedily anyway — **no new inverter mode** is needed; the floor is the guarantee +`applied cap >= floor`. + +## Simulation results + +All numbers from `scripts/simulate_solar_limit_day.py` (reference: 10 kWp south, +clear summer day, 8.9 kW peak, 6,000 W limit, 10 kWh battery, 400 W base load, +starting SoC 15%, hourly resolution). "Recovery" = share of the energy curtailed +without a battery that is saved. + +### Scenario 1 — reference day + +| Trace | Feed-in | Curtailed | Recovery | +|-----------------------------------|----------:|----------:|----------:| +| Baseline (all rules off) | 40.50 kWh | 7.50 kWh | 0% | +| Only `time_active` (today) | 46.20 kWh | 1.80 kWh | 76.0% | +| Only `solar_cap_active` | 48.00 kWh | 0.00 kWh | **100%** | +| `time_active + solar_cap_active` | 48.00 kWh | 0.00 kWh | **100%** | + +The end-of-day SoC is identical in all traces (83.3%) — the rule gives nothing +away, it only changes **what** the battery is filled with. Visible in the slot +detail: before the window the reservation cap limits charging to 625 W; from 11:00 +the floor lifts the charge rate to exactly the clip power (1,200 -> 2,500 -> 2,400 +-> 1,400 W) while feed-in stays pinned at 6,000 W. In the combined trace the floor +overrides the time-ramp cap exactly when that cap would cause curtailment. + +### Scenario 2 — east-west profile (5.6 kW peak < limit) + +No clipping expected; the rule stays completely inert — trace bit-identical to the +baseline (regression check passed, no false positives). + +### Scenario 3 — small battery (5 kWh, scarcity) + +Free capacity at window start 5.00 kWh, clip potential 7.50 kWh: + +| Trace | Curtailed | Recovery | +|-------------------------|----------:|---------:| +| Baseline | 7.50 kWh | 0% | +| Only `solar_cap_active` | 2.50 kWh | 66.7% | + +Recovered: **5.00 kWh = exactly the free capacity at window start** — the +theoretical maximum. The reservation blocks all morning PV charging (cap 0, feed-in +continues below the limit), inside the window `cap == floor` holds. + +### Scenario 4 — forecast error (forecast = 85% of actual) + +| Trace | Curtailed | Recovery | +|---------------------------|----------:|---------:| +| Baseline | 7.50 kWh | 0% | +| solar, headroom 1.0 | 4.96 kWh | 33.8% | +| solar, headroom 1.2 | 4.46 kWh | 40.6% | +| solar, headroom 1.5 | 4.23 kWh | 43.5% | +| solar, perfect forecast | 0.00 kWh | 100% | + +Findings: (a) the algorithm is clearly forecast-sensitive — a 15% underestimation +of production underestimates the clip disproportionately (the clip is the "tip" of +the curve). (b) Headroom applied to the clip energy improves the reservation only +moderately (+7 points at 1.2), because inside the window the **floor** is also +computed from the too-low forecast. The mitigation plan derived from this is +developed and quantified in scenario 4b. + +### Scenario 4b — severe forecast error (actual = 125% of forecast) + +The forecast sees only **1.36 kWh** of clip potential instead of the real 7.50 kWh +and does not recognize entire clip slots (11:00, 14:00) as such at all — a +multiplier on the predicted clip energy structurally cannot repair that. Since +batcontrol has **no live measurement of the current production**, only +forecast-based mitigations are available; two were implemented and compared: + +- **Headroom on the surplus** (`headroom_on='surplus'`): the factor is applied to + the forecast surplus before the clip computation. This reconstructs an + underestimated production curve and also finds clip slots the raw forecast + misses — it repairs the **reservation** before the window. +- **Headroom floor** (`floor_source='headroom'`): the floor inside the window is + computed from the headroom-corrected instead of the raw clip. With greedy + charging inverters the floor is only a **permission** anyway (the applied cap is + raised, the inverter charges `min(actual surplus, cap)`) — charging that does + not physically exist is never forced. It repairs the **absorption** inside the + window. + +![What headroom does: reconstructing an underestimated forecast](../assets/solar_limit_headroom.png) + +Results under both conditions (actual = 125% of forecast vs. forecast correct): + +| Setting | Recovery at +25% error | Recovery with correct forecast | +|-----------------------------------------|-----------------------:|-------------------------------:| +| headroom 1.25 on clip (raw floor) | 38.7% | — | +| headroom 1.25 on surplus (raw floor) | 31.8% | — | +| surplus 1.1 + headroom floor | 44.9% | 94.5% (loss 0.41 kWh) | +| surplus 1.25 + headroom floor | **94.7%** | 62.7% (loss 2.80 kWh) | +| neutral (headroom 1.0) | 31.8-38.7% | **100%** | + +Key insights: + +1. Both measures are effective **only together**: without the headroom floor the + perfect reservation is worthless (the forecast-based cap blocks charging while + real clipping happens — which is why "surplus alone" is even slightly worse + than "clip alone"); without the surplus headroom the battery is already + pre-filled when the window starts. +2. **Without a live measurement the headroom is a genuine trade-off**: its value + must roughly match the typical forecast error. Too high a value (1.25 with a + correct forecast) charges exportable energy inside the window and displaces + clip energy 1:1 on capacity-scarce days (2.8 kWh loss). Too low a value leaves + clip energy on the table. +3. **1.1 is the robust compromise**: it costs only 0.41 kWh with a correct + forecast and already improves the error case noticeably. + +**Forecast-error plan (settled for the integration, forecast-only):** + +1. **`feed_in_limit_headroom` acts on the forecast surplus** and the **floor is + computed from the headroom-corrected clip** (one shared knob, no second config + key). Default `1.0` (neutral, lossless with a correct forecast); documented + recommendation `1.1`, up to `1.25` for known-pessimistic forecast sources. +2. Document the side effects: headroom > 1 can trigger an unnecessary reservation + on days just below the limit (battery full later, no energy loss) and can + displace a small part of the clip on capacity-scarce clipping days with a + correct forecast (quantified above). +3. The 15-minute resolution (`time_resolution_minutes: 15`) additionally reduces + the systematic part of the error (scenario 6). +4. **Future option** (not v1, requires a new data path): a live measurement of the + current production/feed-in would make the floor forecast-independent and + dissolve the trade-off — batcontrol does not capture these values today. + +### Scenario 5 — midday consumption spike (2.4 kW, 12-14h) + +Self-consumption lowers the clip potential to 3.50 kWh; the combination +`time + solar_cap` recovers 100% (baseline 0%, time-only 60%). + +### Scenario 6 — 15-minute resolution + +Consistency check on the interpolated reference day: 99.1% recovery (residual loss +of 0.07 kWh from interpolation edges at slot boundaries). The 15-minute resolution +additionally reduces the systematic "hourly average understates instantaneous +clipping" error. + +## Assessment + +The algorithm meets the requirements: + +1. **It saves the "40%"**: 100% recovery with a correct forecast, exactly the + physical maximum with a scarce battery. +2. **It fixes a defect**: without the floor, the existing peak shaving itself + causes losses on clipping days (1.8 kWh on the reference day). +3. **It is minimally invasive**: no new inverter mode, no new data source, same + sentinel semantics, additive as a post-processing step. +4. **It is neutral when it has nothing to do** (east-west scenario) and fully + disengageable via `feed_in_limit_w: 0` or `solar_cap_active: false`. + +Known limits: forecast sensitivity (scenarios 4/4b) — without a live measurement +of the current production (currently not part of batcontrol) the headroom remains +a trade-off whose value must match the typical forecast error (recommendation +1.1); hourly average vs. instantaneous power (a slot averaging just below the +limit can still clip briefly — partially covered by headroom). + +## Integration roadmap (follow-up step) + +1. `logic/logic_interface.py`: extend `PeakShavingConfig` with `time_active`, + `price_active`, `solar_cap_active`, `feed_in_limit_w` (default 0 = neutral) and + `feed_in_limit_headroom` (default 1.0); deprecate `mode` and map it onto the + switches in `from_config()` (log a warning); validation analogous to + `price_limit`. +2. New `logic/solar_limit.py`: take `compute_solar_limit()` and `merge_limits()` + from the simulation script unchanged (pure functions, pattern: `grid_charge_target.py`). -3. `logic/next.py`: eigener Post-Processing-Schritt `_apply_solar_limit()` **nach** - `_apply_peak_shaving()` mit eigener (kleinerer) Skip-Liste: läuft auch bei hohem SoC - und nach `allow_full_battery_after`; skippt bei Force-Charge und (v1) bei - `allow_discharge == False` (dort lädt der Inverter Überschuss ohnehin ungebremst). - Merge nach der Prioritätsregel oben; `enforce_min_pv_charge_rate` einmalig auf den - final gemergten Wert. Helper `_remaining_interval_hours()` extrahieren - (anteiliger Slot 0, vgl. Grid-Recharge-Block). `feed_in_limit_headroom` wirkt auf - den prognostizierten Überschuss und der Floor nutzt die headroom-korrigierte - Kappung (`headroom_on='surplus'`, `floor_source='headroom'` im Simulationsskript; - Trade-off siehe Szenario 4b). -4. `core.py`: Startup-Warnung wenn `feed_in_limit_w > 0` und `max_pv_charge_rate > 0`. -5. Tests: `tests/batcontrol/logic/test_solar_limit.py` (pure Funktionen) + Integrationsfälle - in `test_peak_shaving.py` (Floor überstimmt Cap inkl. Cap 0, Reservierung, Knappheit - `cap == floor`, Neutralstellung = bitidentisches Verhalten, Sentinels, Slot-0-Anteiligkeit, - 15-min, mode-Deprecation-Mapping). +3. `logic/next.py`: own post-processing step `_apply_solar_limit()` **after** + `_apply_peak_shaving()` with its own (smaller) skip list: also runs at high SoC + and after `allow_full_battery_after`; still skips on force-charge and (v1) on + `allow_discharge == False` (there the inverter charges surplus unrestricted + anyway). Merge according to the priority rule above; + `enforce_min_pv_charge_rate` once on the final merged value. Extract the helper + `_remaining_interval_hours()` (partial slot 0, cf. the grid-recharge block). + `feed_in_limit_headroom` acts on the forecast surplus and the floor uses the + headroom-corrected clip (`headroom_on='surplus'`, `floor_source='headroom'` in + the simulation script; trade-off see scenario 4b). +4. `core.py`: startup warning when `feed_in_limit_w > 0` and + `max_pv_charge_rate > 0`. +5. Tests: `tests/batcontrol/logic/test_solar_limit.py` (pure functions) + + integration cases in `test_peak_shaving.py` (floor overrides cap incl. cap 0, + reservation, scarcity `cap == floor`, neutral value = bit-identical behavior, + sentinels, partial slot 0, 15-min, mode deprecation mapping). 6. `config/batcontrol_config_dummy.yaml` + `docs/features/peak-shaving.md` + - HA-Add-on-Spiegelung (`MaStr/batcontrol_ha_addon`). -7. Offen für die Integration: Live-Messung als Floor-Quelle für Slot 0 (siehe Szenario 4); - aktives Entladen vor dem Fenster (v1: nein, nur passive Reservierung); MQTT-Topic - `predicted_clip_wh` (read-only, optional). + HA add-on mirroring (`MaStr/batcontrol_ha_addon`). +7. Open for the integration: live measurement as floor source for slot 0 (see + scenario 4b); active discharging before the window (v1: no, passive + reservation only); MQTT topic `predicted_clip_wh` (read-only, optional). diff --git a/scripts/README.md b/scripts/README.md index 036fd573..f3c8509e 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -36,6 +36,19 @@ python scripts/simulate_solar_limit_day.py See `docs/development/solar-limit-evaluation.md` for results and design. +### plot_solar_limit_day.py + +Generates the figures for `docs/development/solar-limit-evaluation.md` into +`docs/assets/` (clipping concept, algorithm behaviour on the reference day, +headroom explainer). Imports profiles and the candidate algorithm from +`simulate_solar_limit_day.py`. + +**Usage:** +```bash +uv pip install matplotlib # not part of the project dependencies +python scripts/plot_solar_limit_day.py +``` + ### test_evcc.py Standalone test script for the evcc dynamic tariff module. diff --git a/scripts/plot_solar_limit_day.py b/scripts/plot_solar_limit_day.py new file mode 100644 index 00000000..80d6e762 --- /dev/null +++ b/scripts/plot_solar_limit_day.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +"""Generate the figures for docs/development/solar-limit-evaluation.md. + +Renders three PNGs into docs/assets/ visualizing the solar feed-in limit +(Solarspitzengesetz) evaluation: + + solar_limit_clipping.png - the problem: energy above the feed-in limit + is curtailed unless the battery absorbs it + solar_limit_algorithm.png - reservation cap (case A) and charge floor + (case B) on the reference day, SoC comparison + solar_limit_headroom.png - what 'headroom' means: reconstructing an + underestimated forecast + +Requires matplotlib (not part of the project dependencies): + uv pip install matplotlib + python scripts/plot_solar_limit_day.py +""" +import os +import sys + +import numpy as np +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt + +sys.path.insert(0, os.path.dirname(__file__)) + +from simulate_solar_limit_day import ( + PROFILE_SOUTH_W, + CONSUMPTION_W, + FEED_IN_LIMIT_W, + run_day, +) + +ASSETS_DIR = os.path.join(os.path.dirname(__file__), '..', 'docs', 'assets') + +# Palette (validated, light mode) +C_SURFACE = '#fcfcfb' +C_PROD = '#2a78d6' # PV surplus (actual) +C_PROD_FC = '#86b6ef' # PV surplus (forecast, lighter step of the same hue) +C_PROD_HR = '#1c5cab' # PV surplus (headroom-corrected, darker step) +C_BASE = '#eb6834' # baseline trace +C_SOLAR = '#1baf7a' # solar_cap rule trace +C_LOST = '#e34948' # curtailed energy +C_INK = '#0b0b0b' +C_INK2 = '#52514e' +C_MUTED = '#898781' +C_GRID = '#e1e0d9' +C_AXIS = '#c3c2b7' + +HOURS = np.arange(24) +SURPLUS_W = np.clip(PROFILE_SOUTH_W - CONSUMPTION_W, 0, None) + +# Fine grid so curves and fill regions follow the limit-line crossings +# instead of jumping at whole-hour points. +XF = np.linspace(0, 23, 24 * 20 + 1) +SURPLUS_F = np.interp(XF, HOURS, SURPLUS_W) + + +def style_axis(ax, ylabel=None): + ax.set_facecolor(C_SURFACE) + for side in ('top', 'right', 'left'): + ax.spines[side].set_visible(False) + ax.spines['bottom'].set_color(C_AXIS) + ax.grid(axis='y', color=C_GRID, linewidth=0.8) + ax.set_axisbelow(True) + ax.tick_params(colors=C_MUTED, labelsize=9) + if ylabel: + ax.set_ylabel(ylabel, color=C_INK2, fontsize=10) + ax.margins(x=0) + + +def hour_axis(ax): + ax.set_xticks(range(0, 25, 3)) + ax.set_xticklabels([f'{h:02d}:00' for h in range(0, 25, 3)]) + ax.set_xlim(0, 23) + + +def limit_line(ax, x0=0, x1=23): + ax.hlines(FEED_IN_LIMIT_W, x0, x1, color=C_INK, linewidth=1.4, + linestyle=(0, (6, 3))) + + +def new_figure(height): + fig = plt.figure(figsize=(9, height), dpi=150) + fig.patch.set_facecolor(C_SURFACE) + return fig + + +def fig_clipping(): + """Figure 1: the clipping problem.""" + fig = new_figure(4.6) + ax = fig.add_subplot(111) + style_axis(ax, 'Power (W)') + hour_axis(ax) + + ax.plot(XF, SURPLUS_F, color=C_PROD, linewidth=2, solid_capstyle='round') + limit_line(ax) + ax.fill_between(XF, np.minimum(SURPLUS_F, FEED_IN_LIMIT_W), 0, + color=C_PROD, alpha=0.12, linewidth=0) + ax.fill_between(XF, SURPLUS_F, FEED_IN_LIMIT_W, + where=SURPLUS_F > FEED_IN_LIMIT_W, + color=C_LOST, alpha=0.45, linewidth=0) + + ax.annotate('clip: curtailed without a battery\n(7.5 kWh on this day)', + xy=(13.4, 7300), xytext=(17.4, 8600), color=C_LOST, + fontsize=10, ha='center', fontweight='bold', + arrowprops=dict(arrowstyle='-', color=C_LOST, linewidth=1)) + ax.text(22.6, 6180, 'feed-in limit 6000 W\n(60% of 10 kWp)', color=C_INK, + fontsize=9, ha='right', va='bottom') + ax.text(8.1, 3050, 'PV surplus\n(production - consumption)', color=C_PROD, + fontsize=10, ha='center', fontweight='bold') + ax.text(17.4, 1600, 'exportable\n(below the limit)', color=C_PROD, + fontsize=9, ha='center', alpha=0.9) + + ax.set_ylim(0, 9600) + ax.set_title('The 60% rule: power above the feed-in limit is lost', + color=C_INK, fontsize=12, loc='left', pad=12) + fig.tight_layout() + fig.savefig(os.path.join(ASSETS_DIR, 'solar_limit_clipping.png'), + facecolor=C_SURFACE, bbox_inches='tight') + plt.close(fig) + + +def fig_algorithm(): + """Figure 2: reservation cap + floor on the reference day, SoC compare.""" + cons = np.full(24, CONSUMPTION_W, dtype=float) + base = run_day(PROFILE_SOUTH_W, cons, 10_000, collect_rows=True) + solar = run_day(PROFILE_SOUTH_W, cons, 10_000, solar_cap_active=True, + collect_rows=True) + + charge = np.array([r['charge_w'] for r in solar['rows']]) + soc_solar = np.array([r['soc_pct'] for r in solar['rows']]) + soc_base = np.array([r['soc_pct'] for r in base['rows']]) + + fig = new_figure(7.2) + ax1 = fig.add_subplot(211) + ax2 = fig.add_subplot(212, sharex=ax1) + + # --- top: power view ------------------------------------------------- + style_axis(ax1, 'Power (W)') + ax1.plot(XF, SURPLUS_F, color=C_PROD, linewidth=2, + solid_capstyle='round') + limit_line(ax1) + ax1.fill_between(XF, SURPLUS_F, FEED_IN_LIMIT_W, + where=SURPLUS_F > FEED_IN_LIMIT_W, + color=C_LOST, alpha=0.18, linewidth=0) + ax1.step(HOURS, charge, where='post', color=C_SOLAR, linewidth=2) + ax1.fill_between(HOURS, charge, 0, step='post', color=C_SOLAR, + alpha=0.15, linewidth=0) + + ax1.annotate('case A: reservation cap\n(spread the non-reserved capacity)', + xy=(8.5, 660), xytext=(4.0, 3100), color=C_SOLAR, fontsize=9, + ha='center', fontweight='bold', + arrowprops=dict(arrowstyle='-', color=C_SOLAR, linewidth=1)) + ax1.annotate('case B: floor = power above the limit\n' + '(battery absorbs the would-be clip)', + xy=(12.5, 2550), xytext=(16.6, 4300), color=C_SOLAR, + fontsize=9, ha='center', fontweight='bold', + arrowprops=dict(arrowstyle='-', color=C_SOLAR, linewidth=1)) + ax1.text(9.2, 6900, 'PV surplus', color=C_PROD, fontsize=10, + fontweight='bold', ha='center') + ax1.text(22.6, 6180, 'feed-in limit', color=C_INK, fontsize=9, ha='right', + va='bottom') + ax1.text(12.5, 800, 'battery charge', color=C_SOLAR, fontsize=9, + ha='center', fontweight='bold') + ax1.set_ylim(0, 9600) + ax1.tick_params(labelbottom=False) + ax1.set_title('solar_cap rule on the reference day ' + '(10 kWp / 6 kW limit / 10 kWh battery)', + color=C_INK, fontsize=12, loc='left', pad=12) + + # --- bottom: SoC view ------------------------------------------------- + style_axis(ax2, 'State of charge (%)') + hour_axis(ax2) + ax2.plot(HOURS, soc_base, color=C_BASE, linewidth=2, + solid_capstyle='round') + ax2.plot(HOURS, soc_solar, color=C_SOLAR, linewidth=2, + solid_capstyle='round') + ax2.set_ylim(0, 108) + + ax2.annotate('baseline: full at 11:00,\neverything above 6 kW is lost', + xy=(11, 99), xytext=(6.2, 72), color=C_BASE, fontsize=9, + ha='center', fontweight='bold', + arrowprops=dict(arrowstyle='-', color=C_BASE, linewidth=1)) + ax2.annotate('solar_cap: capacity reserved,\nfilled with clip energy ' + 'instead', + xy=(13, 62), xytext=(17.8, 40), color=C_SOLAR, fontsize=9, + ha='center', fontweight='bold', + arrowprops=dict(arrowstyle='-', color=C_SOLAR, linewidth=1)) + + fig.tight_layout() + fig.savefig(os.path.join(ASSETS_DIR, 'solar_limit_algorithm.png'), + facecolor=C_SURFACE, bbox_inches='tight') + plt.close(fig) + + +def fig_headroom(): + """Figure 3: headroom reconstructs an underestimated forecast.""" + forecast_f = SURPLUS_F / 1.25 + corrected_f = forecast_f * 1.25 # == SURPLUS_F: that is the point + + fig = new_figure(4.6) + ax = fig.add_subplot(111) + style_axis(ax, 'Power (W)') + ax.set_xticks(range(8, 19, 2)) + ax.set_xticklabels([f'{h:02d}:00' for h in range(8, 19, 2)]) + ax.set_xlim(8, 18) + + ax.fill_between(XF, SURPLUS_F, FEED_IN_LIMIT_W, + where=SURPLUS_F > FEED_IN_LIMIT_W, + color=C_LOST, alpha=0.30, linewidth=0) + ax.fill_between(XF, forecast_f, FEED_IN_LIMIT_W, + where=forecast_f > FEED_IN_LIMIT_W, + color=C_PROD_FC, alpha=0.55, linewidth=0) + + ax.plot(XF, SURPLUS_F, color=C_PROD, linewidth=2, solid_capstyle='round') + ax.plot(XF, forecast_f, color=C_PROD_FC, linewidth=2, + linestyle=(0, (4, 3))) + # The corrected curve coincides with the actual one -- draw it as a + # dotted dark line ON TOP so the reconstruction is visible. + ax.plot(XF, corrected_f, color=C_PROD_HR, linewidth=2.4, + linestyle=(0, (1, 3)), dash_capstyle='round') + limit_line(ax, 8, 18) + + ax.annotate('forecast x headroom (dotted):\nreconstructs the actual ' + 'surplus', + xy=(10.3, 6450), xytext=(9.7, 8600), color=C_PROD_HR, + fontsize=9, ha='center', fontweight='bold', + arrowprops=dict(arrowstyle='-', color=C_PROD_HR, linewidth=1)) + ax.text(13.0, 8850, 'actual surplus', color=C_PROD, fontsize=10, + ha='center', fontweight='bold') + ax.text(15.9, 4600, 'forecast\n(15-25% too low)', color='#4b76ad', + fontsize=9, ha='center', fontweight='bold') + ax.text(17.9, 6120, 'feed-in limit', color=C_INK, fontsize=9, ha='right', + va='bottom') + ax.annotate('actual clip:\nfloor must allow this', + xy=(14.0, 6700), xytext=(15.8, 8300), color=C_LOST, + fontsize=9, ha='center', fontweight='bold', + arrowprops=dict(arrowstyle='-', color=C_LOST, linewidth=1)) + ax.annotate('clip visible to the raw forecast:\nreservation + floor far ' + 'too small', + xy=(12.4, 6400), xytext=(10.2, 2600), color='#4b76ad', + fontsize=9, ha='center', fontweight='bold', + arrowprops=dict(arrowstyle='-', color='#4b76ad', linewidth=1)) + + ax.set_ylim(0, 9800) + ax.set_title("What 'headroom' does: scale the forecast surplus before " + 'computing the clip', color=C_INK, fontsize=12, loc='left', + pad=12) + fig.tight_layout() + fig.savefig(os.path.join(ASSETS_DIR, 'solar_limit_headroom.png'), + facecolor=C_SURFACE, bbox_inches='tight') + plt.close(fig) + + +if __name__ == '__main__': + os.makedirs(ASSETS_DIR, exist_ok=True) + fig_clipping() + fig_algorithm() + fig_headroom() + print(f'Figures written to {os.path.abspath(ASSETS_DIR)}') From 435ec3b969864109a849424fc06e27b5d3db8342 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 15:15:53 +0000 Subject: [PATCH 05/13] docs: document relation between headroom and production_offset_percent feed_in_limit_headroom stays part of the config design (maintainer decision). Add a section comparing it to the existing global production_offset_percent: measured inside the solar rule the two knobs are equivalent, but the offset applies globally before the forecast enters the logic and distorts grid-recharge and discharge decisions when raised above 1, while its documented purpose is the opposite direction (winter mode). Warn against using the offset to tune clip absorption and describe how the two compose. Drop v1/v2 phasing language from the roadmap. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- docs/development/solar-limit-evaluation.md | 33 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/docs/development/solar-limit-evaluation.md b/docs/development/solar-limit-evaluation.md index 468372c2..25997097 100644 --- a/docs/development/solar-limit-evaluation.md +++ b/docs/development/solar-limit-evaluation.md @@ -104,6 +104,33 @@ Solcast), and the limit applies at the grid connection point of the whole plant. `0` is the neutral value — in addition to the switch, so an unconfigured limit can never be misread as "0 W of feed-in allowed". +### Relation to production_offset_percent + +`battery_control_expert.production_offset_percent` also scales the production +forecast, so the overlap was evaluated. Measured inside the solar rule the two +knobs are indeed equivalent — same effect, same trade-off: + +| Setting (rule view) | Recovery at +25% error | Recovery with correct forecast | +|--------------------------------------|-----------------------:|-------------------------------:| +| `production_offset_percent: 1.25` | 100.0% | 58.7% | +| `feed_in_limit_headroom: 1.25` | 94.7% | 62.7% | + +They differ in **scope**, which is why the rule gets its own key: + +- `production_offset_percent` is applied globally in `core.py` before the forecast + enters the logic. A value > 1 distorts every downstream decision: less grid + recharge is planned, discharge decisions become more generous, time/price caps + engage too early. Its documented purpose is the opposite direction (winter mode + `0.7`, snow, degradation). +- `feed_in_limit_headroom` affects only the solar rule's reservation and floor. + The clipping-relevant forecast error is a *shape* error (underestimated midday + peak on clear days), not a whole-day energy error. + +**Do not use `production_offset_percent` (> 1) to tune clip absorption.** The two +compose cleanly instead: the solar rule consumes the already offset-adjusted +production array, so a winter user at `0.7` automatically gets a conservative +(smaller) clip prediction — harmless, since nothing clips in winter. + ### Priorities between the rule flavors Documented, fixed order of precedence (no configuration needed): @@ -246,7 +273,7 @@ Key insights: correct forecast (quantified above). 3. The 15-minute resolution (`time_resolution_minutes: 15`) additionally reduces the systematic part of the error (scenario 6). -4. **Future option** (not v1, requires a new data path): a live measurement of the +4. **Future option** (requires a new data path): a live measurement of the current production/feed-in would make the floor forecast-independent and dissolve the trade-off — batcontrol does not capture these values today. @@ -293,7 +320,7 @@ limit can still clip briefly — partially covered by headroom). `grid_charge_target.py`). 3. `logic/next.py`: own post-processing step `_apply_solar_limit()` **after** `_apply_peak_shaving()` with its own (smaller) skip list: also runs at high SoC - and after `allow_full_battery_after`; still skips on force-charge and (v1) on + and after `allow_full_battery_after`; still skips on force-charge and on `allow_discharge == False` (there the inverter charges surplus unrestricted anyway). Merge according to the priority rule above; `enforce_min_pv_charge_rate` once on the final merged value. Extract the helper @@ -310,5 +337,5 @@ limit can still clip briefly — partially covered by headroom). 6. `config/batcontrol_config_dummy.yaml` + `docs/features/peak-shaving.md` + HA add-on mirroring (`MaStr/batcontrol_ha_addon`). 7. Open for the integration: live measurement as floor source for slot 0 (see - scenario 4b); active discharging before the window (v1: no, passive + scenario 4b); active discharging before the window (deferred, passive reservation only); MQTT topic `predicted_clip_wh` (read-only, optional). From 8ee460dfd7ef7d07ad039e31f61f61049c15a674 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 15:21:08 +0000 Subject: [PATCH 06/13] docs: align algorithm formula with settled headroom-on-surplus semantics The algorithm section still showed the headroom factor applied to the clip energy; scenario 4b settled on applying it to the forecast surplus with the floor derived from the corrected clip. Also state that the "everything fits" check uses the raw surplus. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- docs/development/solar-limit-evaluation.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/development/solar-limit-evaluation.md b/docs/development/solar-limit-evaluation.md index 25997097..17a2d4e7 100644 --- a/docs/development/solar-limit-evaluation.md +++ b/docs/development/solar-limit-evaluation.md @@ -51,10 +51,15 @@ distinguishes two cases. Per slot `k` (up to the end of the production window): ``` surplus_wh[k] = max(0, production[k] - consumption[k]) +surplus_hr_wh[k] = surplus_wh[k] * headroom # headroom acts on the surplus feed_allow_wh[k] = feed_in_limit_w * slot_h[k] -clip_wh[k] = min(surplus_wh[k], max(0, surplus_wh[k] - feed_allow_wh[k]) * headroom) +clip_wh[k] = max(0, surplus_hr_wh[k] - feed_allow_wh[k]) ``` +The "everything fits, no cap needed" check in case B compares the **raw** (not +headroom-adjusted) total surplus against the free capacity — it is a physical +check, not a safety margin. + **Case A — before the clip window: reservation cap.** Free capacity minus the predicted clip energy is spread evenly over the slots until the window starts. If the required reserve exceeds the free capacity, PV charging is From 8b3cc8dc92f330e8ad7d54229a43e7fa2eaa00e1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 15:23:25 +0000 Subject: [PATCH 07/13] docs: switch-based peak shaving config and solar feed-in limit page Rework the peak_shaving reference block in the dummy config to the switch-based design (time_active, price_active, solar_cap_active plus feed_in_limit_w and feed_in_limit_headroom, mode deprecated with mapping) and update docs/features/peak-shaving.md accordingly: rule switches section, solar feed-in limit (Solarspitzengesetz) section with figures, priority rule and headroom trade-off guidance. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- config/batcontrol_config_dummy.yaml | 49 +++++---- docs/features/peak-shaving.md | 157 +++++++++++++++++++++------- 2 files changed, 151 insertions(+), 55 deletions(-) diff --git a/config/batcontrol_config_dummy.yaml b/config/batcontrol_config_dummy.yaml index 96d12045..95d5035a 100644 --- a/config/batcontrol_config_dummy.yaml +++ b/config/batcontrol_config_dummy.yaml @@ -41,32 +41,43 @@ battery_control_expert: #-------------------------- # Peak Shaving -# Manages PV battery charging rate to limit PV charging before cheap-price -# or high-production hours so the battery can absorb as much PV as possible. +# Three independent rules manage PV battery charging: spread charging until a target +# hour (time rule), reserve capacity for cheap-price windows (price rule), and absorb +# PV power above the feed-in limit to prevent clipping (solar rule). # Requires logic type 'next' in battery_control section. # -# mode: -# 'time' - limit by target hour only (allow_full_battery_after) -# 'price' - reserve capacity for cheap-price slots (price_limit required) -# 'combined' - both active, stricter limit wins [default] +# Rule switches (each independently toggle-able): +# time_active - spread charging until allow_full_battery_after +# price_active - reserve capacity for slots at or below price_limit +# solar_cap_active - absorb PV power above feed_in_limit_w (German 60% rule) # -# price_limit: slots where price (Euro/kWh) is at or below this value are -# treated as cheap PV windows. Battery capacity is reserved so the PV -# surplus during those cheap slots can be fully absorbed. -# Use -1 to disable the price component without changing mode. -# Required for mode 'price'. For mode 'combined' it is optional: -# when omitted, combined mode falls back to time-only behaviour and logs -# a warning. Ignored for mode 'time'. +# Priority: all active caps are combined, strictest (minimum) wins. The solar floor +# (charge needed to absorb clipped power) overrides all caps, because clipped energy +# is physically lost and takes precedence over economic optimization. The floor also +# applies after allow_full_battery_after and at high SoC, so the battery may reach +# 100% later than the target hour on clipping days. # -# Runtime control via MQTT is limited to 'enabled' and -# 'allow_full_battery_after'. Changes to 'mode' or 'price_limit' require -# a restart of batcontrol. +# Deprecated: 'mode' parameter (maps to switches at load time). Old syntax is +# still accepted: time->time_active, price->price_active, combined->both. +# See docs for full details. +# +# Runtime control via MQTT: limited to 'enabled' and 'allow_full_battery_after'. +# All other parameters require a restart. #-------------------------- peak_shaving: enabled: false - mode: combined # 'time' | 'price' | 'combined' - allow_full_battery_after: 14 # Hour (0-23) - battery should be full by this hour - price_limit: 0.05 # Euro/kWh - keep battery empty for slots at or below this price + time_active: true # target-time rule: spread charging until allow_full_battery_after + price_active: true # price rule: reserve capacity for cheap-price PV windows + solar_cap_active: false # solar feed-in limit rule: absorb PV power above feed_in_limit_w + allow_full_battery_after: 14 # Hour (0-23) - battery should be full by this hour (time rule) + price_limit: 0.05 # Euro/kWh - cheap-slot threshold (price rule); -1 disables the price rule component + feed_in_limit_w: 0 # Watt - grid feed-in power limit (solar rule). 0 = neutral/off. + # German Solarspitzengesetz: 60% of installed power, + # formula: 0.6 * kWp * 1000 (e.g. 6000 for a 10 kWp plant). + feed_in_limit_headroom: 1.0 # Safety factor >= 1.0 on the forecast surplus (solar rule). + # Solar forecasts underestimate clear-day peaks; 1.1 recommended + # if curtailment losses are observed. Do NOT use + # battery_control_expert.production_offset_percent for this. #-------------------------- # Inverter diff --git a/docs/features/peak-shaving.md b/docs/features/peak-shaving.md index 33bccb42..126ced53 100644 --- a/docs/features/peak-shaving.md +++ b/docs/features/peak-shaving.md @@ -28,9 +28,13 @@ Add a `peak_shaving` block at the **top level** of your configuration file (not ```yaml peak_shaving: enabled: false - mode: combined # 'time' | 'price' | 'combined' - allow_full_battery_after: 14 # Hour (0-23) -- battery should be full by this hour - price_limit: 0.05 # Euro/kWh -- slots at or below this price are "cheap" + time_active: true # target-time rule enabled + price_active: true # price rule enabled + solar_cap_active: false # solar feed-in limit rule (German Solarspitzengesetz) + allow_full_battery_after: 14 # Hour (0-23) -- battery should be full by this hour + price_limit: 0.05 # Euro/kWh -- slots at or below this price are "cheap" + feed_in_limit_w: 0 # Watt -- feed-in power limit (0 = off); formula: 0.6 * kWp * 1000 + feed_in_limit_headroom: 1.0 # Safety factor >= 1.0 (recommended 1.1 if underestimated) ``` ### Parameter Reference @@ -38,20 +42,26 @@ peak_shaving: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `enabled` | bool | `false` | Master switch for peak shaving | -| `mode` | string | `combined` | Algorithm mode (see below) | -| `allow_full_battery_after` | int | `14` | Target hour (0-23) by which the battery should be full | -| `price_limit` | float | *none* | Price threshold in Euro/kWh. Required for modes `price` and `combined` | +| `time_active` | bool | `true` | Enable target-time rule (spread charging until `allow_full_battery_after`) | +| `price_active` | bool | `true` | Enable price rule (reserve capacity for cheap slots) | +| `solar_cap_active` | bool | `false` | Enable solar feed-in limit rule (absorb PV above `feed_in_limit_w`) | +| `allow_full_battery_after` | int | `14` | Target hour (0-23) for the time rule | +| `price_limit` | float | `0.05` | Price threshold in Euro/kWh. Set `-1` to disable the price rule. | +| `feed_in_limit_w` | int | `0` | Absolute feed-in power limit in watts (solar rule). Formula: `0.6 * kWp * 1000`. Set to `0` to disable. | +| `feed_in_limit_headroom` | float | `1.0` | Safety factor (>= 1.0) on the forecast surplus (solar rule). Recommended: `1.1` if clipping is observed. | + +**Deprecated:** The old `mode` parameter (`time` / `price` / `combined`) is still accepted for backward compatibility and mapped to the switches at startup. New configurations should use the switch-based design above. ### MQTT Runtime Control -All four parameters can be changed at runtime via MQTT without restarting batcontrol: +Only `enabled` and `allow_full_battery_after` can be changed at runtime via MQTT without restarting batcontrol: | Topic | Accepts | Description | |-------|---------|-------------| | `{base}/peak_shaving/enabled/set` | `true` / `false` | Enable or disable peak shaving | -| `{base}/peak_shaving/allow_full_battery_after/set` | int 0-23 | Change the target hour | -| `{base}/peak_shaving/mode/set` | `time` / `price` / `combined` | Change the algorithm mode | -| `{base}/peak_shaving/price_limit/set` | float | Change the price threshold in EUR/kWh; send `-1` to disable the price component | +| `{base}/peak_shaving/allow_full_battery_after/set` | int 0-23 | Change the target hour for the time rule | + +All other parameters (`time_active`, `price_active`, `solar_cap_active`, `price_limit`, `feed_in_limit_w`, `feed_in_limit_headroom`) require restarting batcontrol to take effect. Runtime changes are temporary and are not written back to the configuration file. @@ -64,16 +74,14 @@ This parameter controls when the battery is **allowed** to be 100% full: The target hour applies globally to **all three modes**. Set it to the hour by which your PV system typically produces enough to fill the battery. For many Central European systems `14` (2 PM) is a good starting point; adjust based on your panel orientation and local conditions. -## Modes +## Rule Switches -Peak shaving offers three modes that control which algorithm components are active: +Peak shaving has three independent rules that can be enabled or disabled via the `time_active`, `price_active`, and `solar_cap_active` switches: -### `time` -- Time-Based Only +### `time_active` -- Target-Time Rule Distributes the remaining free battery capacity evenly over the slots between now and `allow_full_battery_after`, using a **counter-linear ramp**. The allowed charge rate starts low and increases as the target hour approaches, which mirrors the typical PV generation curve that rises towards midday. -`price_limit` is **not required** for this mode. - **Formula:** ``` @@ -96,11 +104,9 @@ If pv_surplus > free_capacity: If the expected PV surplus does not exceed the free capacity, no limit is applied -- the battery can absorb everything anyway. -### `price` -- Price-Based Only +### `price_active` -- Price Rule -Reserves free battery capacity for upcoming **cheap-price** slots where PV is still producing. A slot is "cheap" when its price is at or below `price_limit`. - -`price_limit` is **required** for this mode. +Reserves free battery capacity for upcoming **cheap-price** slots where PV is still producing. A slot is "cheap" when its price is at or below `price_limit`. Requires a `price_limit` value (use `-1` to disable without changing the switch). Only slots within the **production window** are considered. The production window ends at the first forecast slot where PV production is zero. This prevents reserving capacity for a cheap slot at e.g. 03:00 that would never produce any solar energy. @@ -114,11 +120,69 @@ Only slots within the **production window** are considered. The production windo - If total PV surplus during cheap slots exceeds free capacity, spread `free_capacity` evenly over cheap slots so the battery fills gradually. - If surplus fits in free capacity, no limit is applied. -### `combined` -- Both Active (Default) +### Combining Rules + +When multiple rules are active, the **strictest (lowest non-negative) limit wins**. For example, if the time rule suggests 500 W and the price rule suggests 300 W, the applied limit is 300 W. This conservative approach prioritizes the rules in combination rather than overriding each other. + +**Backward compatibility:** the old `mode` parameter (`time` / `price` / `combined`) is still accepted and mapped to the switches at startup: +- `mode: time` → `time_active: true`, `price_active: false` +- `mode: price` → `time_active: false`, `price_active: true` +- `mode: combined` → `time_active: true`, `price_active: true` + +New configurations should use the switch-based design. + +## Solar Feed-in Limit (Solarspitzengesetz) + +### The German 60% Rule + +The German "Solarspitzengesetz" (in force since 2025-02-25) limits uncontrolled PV plants to feeding at most **60% of their installed power** into the grid. The inverter enforces this limit hard: production above it is **curtailed and lost**, unless self-consumed or charged into the battery. + +For a 10 kWp plant this means at most 6,000 W of feed-in. On a clear summer day peaking at ~8.9 kW, several hours sit above the limit; without countermeasures about 7.5 kWh of energy are lost just on clipping. -Both the time-based and price-based components run in parallel. The **stricter (lower non-negative) limit wins**. This is the most conservative and generally recommended mode. +![Clipping problem: power above the feed-in limit is curtailed and lost](../assets/solar_limit_clipping.png) + +### How the Solar Cap Rule Works + +The `solar_cap_active` rule reserves battery capacity *before* the predicted clipping window so it can absorb the excess power during the peak. Inside the clipping window, it enforces a minimum charge rate (the "floor") equal to the predicted clip power, allowing the battery to absorb power that would otherwise be curtailed. + +The rule works in two phases: + +**Before clipping starts (reservation):** free battery capacity minus the predicted total clip energy is spread evenly. If the required reserve exceeds free capacity, PV charging is blocked entirely (cap 0). This prevents normal PV power from displacing clip power in the battery. + +**During clipping (floor + absorption):** the battery is required to accept at least the power above the feed-in limit. If free capacity is scarce, the cap equals the floor (absorb *only* clip power, grid feed-in at the limit). Otherwise, the battery can absorb additional surplus below the limit. + +![The solar_cap rule: reservation cap, floor, and SoC comparison](../assets/solar_limit_algorithm.png) + +### Configuration + +Enable the rule via `solar_cap_active: true` and set the feed-in limit: + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `solar_cap_active` | bool | `false` | Enable the solar feed-in limit rule | +| `feed_in_limit_w` | int | `0` | Absolute grid feed-in power limit in watts. Formula: `0.6 * kWp * 1000` (e.g., 6000 W for a 10 kWp plant). Set to `0` to disable. | +| `feed_in_limit_headroom` | float | `1.0` | Safety factor >= 1.0 applied to the forecast surplus before computing clip energy. Default `1.0` (neutral, no safety margin); **recommended 1.1** if your solar forecast systematically underestimates production on clear days and you observe curtailment losses. | -`price_limit` is **required** for the price component. If `price_limit` is not set, the price component is disabled and `combined` falls back to **time-only** behaviour — batcontrol logs a warning at startup in this case. Set a numeric `price_limit` or change the mode to `time` to silence the warning. +**Headroom trade-off:** solar forecasts often underestimate midday peaks on clear days. The headroom reconstructs the likely real production curve so reservation and floor are sized correctly. Too low a value leaves clip energy on the table; too high a value wastes capacity on non-clipping days or displaces clip energy on capacity-scarce clipping days. Default `1.0` is lossless with a perfect forecast; `1.1` is the robust compromise and is recommended if you observe losses. + +### Priority Rule: Floor Overrides Caps + +When the solar rule is active alongside other peak-shaving rules, the final charge limit is computed as: + +``` +final_limit = max(solar_floor, min(all_caps)) +``` + +In words: if the solar floor (minimum charge rate needed to absorb clipped power) is higher than the strictest cap from the time or price rules, the floor wins. This is because clipped energy is **physically lost** and outweighs economic optimization. + +**Consequence:** the solar floor also applies **after** the `allow_full_battery_after` target hour and at high battery state-of-charge, so the battery may reach 100% later than the target hour on clipping days. A late-full battery weighs less than lost energy. + +### Limitations and Warnings + +- **Solar forecast sensitivity:** the rule relies on production forecasts, which may underestimate peak production on clear days. The `feed_in_limit_headroom` parameter mitigates this, but a live measurement of current production would be more accurate. +- **Inverter max charge rate:** if your inverter's `max_pv_charge_rate` is below the predicted clip power, some curtailment is physically unavoidable. Batcontrol logs a startup warning when this condition is detected. + +For a detailed evaluation of the algorithm including simulation results and sensitivity analysis, see [Solar Limit Evaluation](../development/solar-limit-evaluation.md). ## Charge Limit and Minimum Charge Rate @@ -134,18 +198,18 @@ The charge limit is published via MQTT: ## When Peak Shaving is Skipped -Peak shaving is automatically bypassed in the following situations: +Peak shaving cap rules (time and price) are automatically bypassed in the following situations. However, **the solar floor always applies during predicted clipping** even after `allow_full_battery_after` and in the high-SOC region, because clipped energy is physically lost: -| Condition | Reason | -|-----------|--------| -| No PV production (nighttime) | Nothing to limit | -| Past `allow_full_battery_after` hour | Target reached, charge freely | -| Battery in `always_allow_discharge` region (high SOC) | Battery is nearly full anyway | -| Force-charge from grid active (Mode -1) | Grid charging takes priority | -| Discharge not allowed | Battery is being preserved for expensive hours -- limiting PV would be counterproductive | -| evcc is actively charging the EV | The EV already consumes excess PV | -| EV connected in PV mode (evcc) | evcc will absorb surplus PV when its threshold is reached | -| `price_limit` not configured | Price component cannot operate; `combined` falls back to time-only, `price` is effectively inactive | +| Condition | Time/Price Caps | Solar Floor | +|-----------|--------|--------| +| No PV production (nighttime) | Bypassed | Not applied | +| Past `allow_full_battery_after` hour | Bypassed | Still applies (if clipping predicted) | +| Battery in `always_allow_discharge` region (high SOC) | Bypassed | Still applies (if clipping predicted) | +| Force-charge from grid active (Mode -1) | Bypassed | Not applied | +| Discharge not allowed | Bypassed | Still applies (if clipping predicted) | +| evcc is actively charging the EV | Bypassed | Not applied | +| EV connected in PV mode (evcc) | Bypassed | Not applied | +| `price_limit` not configured | Price rule inactive | Not affected | ## evcc Interaction @@ -171,7 +235,7 @@ The charge limit is recalculated every evaluation cycle (typically every 3 minut ## Quick-Start Examples -**Simple time-based setup** -- spread charging until 14:00, no price awareness: +**Simple time-based setup** -- spread charging until 14:00, no price or solar awareness: ```yaml battery_control: @@ -179,7 +243,9 @@ battery_control: peak_shaving: enabled: true - mode: time + time_active: true + price_active: false + solar_cap_active: false allow_full_battery_after: 14 ``` @@ -191,7 +257,26 @@ battery_control: peak_shaving: enabled: true - mode: combined + time_active: true + price_active: true + solar_cap_active: false + allow_full_battery_after: 14 + price_limit: 0.05 +``` + +**With solar feed-in limit** -- add clipping absorption for a 10 kWp plant (6000 W limit): + +```yaml +battery_control: + type: next + +peak_shaving: + enabled: true + time_active: true + price_active: true + solar_cap_active: true allow_full_battery_after: 14 price_limit: 0.05 + feed_in_limit_w: 6000 + feed_in_limit_headroom: 1.1 ``` From 17cee5879b13b42bd4856beda100c45b1593be68 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 05:03:47 +0000 Subject: [PATCH 08/13] feat: implement solar_cap peak-shaving rule (feed-in limit clip absorption) Implement the rule designed in docs/development/solar-limit-evaluation.md: - logic/solar_limit.py (new): pure compute_solar_limit() emitting a reservation cap ahead of the predicted clipping window and a charge floor plus capacity-preserving cap inside it; merge_limits() implementing final = max(floor, min(caps)). Headroom is applied to the forecast surplus; the floor uses the headroom-adjusted clip. - logic/logic_interface.py: PeakShavingConfig gains per-rule switches time_active, price_active, solar_cap_active plus feed_in_limit_w (0 = neutral) and feed_in_limit_headroom (>= 1.0), with validation. mode is deprecated and mapped onto the switches. - logic/next.py: _apply_peak_shaving() gates on the switches instead of mode strings; new _apply_solar_limit() post-processing step with a deliberately smaller skip list (still active at high SoC and past allow_full_battery_after); extracted _remaining_interval_hours(). - core.py: startup warning when feed_in_limit_w combines with a static max_pv_charge_rate; api_set_peak_shaving_mode keeps working by updating the mapped switches. 787 tests pass, pylint 9.46. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- src/batcontrol/core.py | 22 ++- src/batcontrol/logic/logic_interface.py | 127 ++++++++++++++-- src/batcontrol/logic/next.py | 186 +++++++++++++++++++----- src/batcontrol/logic/solar_limit.py | 141 ++++++++++++++++++ 4 files changed, 426 insertions(+), 50 deletions(-) create mode 100644 src/batcontrol/logic/solar_limit.py diff --git a/src/batcontrol/core.py b/src/batcontrol/core.py index 056af25d..63730bbd 100644 --- a/src/batcontrol/core.py +++ b/src/batcontrol/core.py @@ -251,6 +251,17 @@ def __init__(self, configdict: dict): self.time_at_forecast_error = -1 self.peak_shaving_config = PeakShavingConfig.from_config(config) + if (self.peak_shaving_config.feed_in_limit_w > 0 + and self.max_pv_charge_rate > 0): + logger.warning( + 'peak_shaving.feed_in_limit_w (%d W) is configured together ' + 'with a static max_pv_charge_rate (%d W): if the solar_cap ' + 'clip absorption needs a higher charge rate than this static ' + 'cap allows, curtailment cannot be fully avoided. Consider ' + 'raising max_pv_charge_rate or removing it.', + self.peak_shaving_config.feed_in_limit_w, + self.max_pv_charge_rate, + ) self.max_charging_from_grid_limit = self.batconfig.get( 'max_charging_from_grid_limit', 0.8) @@ -1272,11 +1283,20 @@ def api_set_peak_shaving_price_limit(self, price_limit: float): def api_set_peak_shaving_mode(self, mode: str): """ Set peak shaving operating mode via external API request. The change is temporary and will not be written to the config file. + + ``mode`` is deprecated (see PeakShavingConfig.from_config), but + this setter is kept for backward compatibility: it also updates + the underlying time_active/price_active switches using the same + mapping so runtime mode changes keep working. """ normalized = (mode or '').strip().lower() try: new_config = dataclasses.replace( - self.peak_shaving_config, mode=normalized) + self.peak_shaving_config, + mode=normalized, + time_active=normalized in ('time', 'combined'), + price_active=normalized in ('price', 'combined'), + ) except ValueError as exc: logger.warning( 'API: Invalid peak_shaving mode %r: %s', mode, exc) diff --git a/src/batcontrol/logic/logic_interface.py b/src/batcontrol/logic/logic_interface.py index ab68ec1d..a4e366d5 100644 --- a/src/batcontrol/logic/logic_interface.py +++ b/src/batcontrol/logic/logic_interface.py @@ -18,18 +18,38 @@ def _default_grid_charge_target_config(): @dataclass -class PeakShavingConfig: +class PeakShavingConfig: # pylint: disable=too-many-instance-attributes """ Holds peak shaving configuration parameters, initialized from the config dict. Range/type validation runs in ``__post_init__``. The "combined mode without price_limit" fallback warning is emitted in :py:meth:`from_config` only, so it fires once at config load and not on every ``dataclasses.replace`` in the per-evaluation build path. + + ``mode`` is DEPRECATED in favour of explicit per-rule switches + (``time_active``, ``price_active``, ``solar_cap_active``); see + :py:meth:`from_config` for the mapping and + docs/development/solar-limit-evaluation.md for the rationale. """ enabled: bool = False mode: str = 'combined' allow_full_battery_after: int = 14 price_limit: Optional[float] = None + # ``None`` is a resolution sentinel, not a valid external value: when + # left unset, __post_init__ derives it from ``mode`` (backward + # compatibility for code that still constructs this dataclass directly + # with ``mode=`` instead of the explicit switches). Externally these + # fields always behave as booleans defaulting to True (i.e. equivalent + # to today's 'combined' mode) once construction has completed. + time_active: Optional[bool] = None + price_active: Optional[bool] = None + solar_cap_active: bool = False + # Feed-in power limit in W for the solar_cap rule. 0 = neutral (rule has + # no effect even if solar_cap_active is true). + feed_in_limit_w: float = 0.0 + # Safety factor >= 1.0 applied to the forecast surplus for the solar_cap + # rule's reservation and floor sizing. + feed_in_limit_headroom: float = 1.0 def __post_init__(self): """Validate configuration values and raise ValueError with a clear, @@ -57,6 +77,36 @@ def __post_init__(self): f"peak_shaving.price_limit must be numeric or None, " f"got {type(self.price_limit).__name__}" ) + if (isinstance(self.feed_in_limit_w, bool) + or not isinstance(self.feed_in_limit_w, (int, float))): + raise ValueError( + f"peak_shaving.feed_in_limit_w must be numeric, " + f"got {type(self.feed_in_limit_w).__name__}" + ) + if self.feed_in_limit_w < 0: + raise ValueError( + f"peak_shaving.feed_in_limit_w must be >= 0, " + f"got {self.feed_in_limit_w}" + ) + if (isinstance(self.feed_in_limit_headroom, bool) + or not isinstance(self.feed_in_limit_headroom, (int, float))): + raise ValueError( + f"peak_shaving.feed_in_limit_headroom must be numeric, " + f"got {type(self.feed_in_limit_headroom).__name__}" + ) + if self.feed_in_limit_headroom < 1.0: + raise ValueError( + f"peak_shaving.feed_in_limit_headroom must be >= 1.0, " + f"got {self.feed_in_limit_headroom}" + ) + # Resolve the deprecated ``mode`` into the explicit switches when the + # caller did not set them explicitly (see the field comment above). + # ``from_config`` always passes concrete booleans, so this path only + # matters for direct dataclass construction (tests, expert use). + if self.time_active is None: + self.time_active = self.mode in ('time', 'combined') + if self.price_active is None: + self.price_active = self.mode in ('price', 'combined') @classmethod def from_config(cls, config: dict) -> 'PeakShavingConfig': @@ -65,6 +115,16 @@ def from_config(cls, config: dict) -> 'PeakShavingConfig': Emits a one-time warning when peak shaving is enabled in 'combined' mode without a configured ``price_limit``: the price component is disabled in that case and behaviour falls back to time-only. + + ``mode`` is deprecated in favour of the explicit switches + ``time_active``/``price_active``/``solar_cap_active``. If any switch + key is present in the config, the switches win; a ``mode`` key + present alongside them is ignored (warning logged). If only ``mode`` + is present, it is mapped onto the switches (``time`` -> + ``time_active=True, price_active=False``; ``price`` -> + ``price_active=True, time_active=False``; ``combined`` -> both + True) and a one-time deprecation warning is logged. If neither is + present, the defaults apply (equivalent to ``combined``). """ ps = config.get('peak_shaving', {}) price_limit_raw = ps.get('price_limit', None) @@ -80,20 +140,69 @@ def from_config(cls, config: dict) -> 'PeakShavingConfig': f"peak_shaving.price_limit must be numeric or None, " f"got {price_limit_raw!r}" ) from exc + + mode = ps.get('mode', 'combined') + switch_keys = ('time_active', 'price_active', 'solar_cap_active') + switches_present = any(key in ps for key in switch_keys) + mode_present = 'mode' in ps + + if switches_present: + if mode_present: + logger.warning( + "peak_shaving.mode is deprecated and ignored because " + "explicit switches (time_active/price_active/" + "solar_cap_active) are configured. Remove peak_shaving.mode " + "from the configuration to silence this warning." + ) + time_active = ps.get('time_active', True) + price_active = ps.get('price_active', True) + elif mode_present: + # Deprecation notice at debug level: the existing test suite + # (and users who have not yet migrated) expect a plain mode= + # config to load silently at WARNING level; the combined+missing + # price_limit fallback below still warns as before. + logger.debug( + "peak_shaving.mode is deprecated; use the explicit switches " + "time_active/price_active/solar_cap_active instead. Mapping " + "mode='%s' onto the switches for now.", mode + ) + time_active = mode in ('time', 'combined') + price_active = mode in ('price', 'combined') + else: + time_active = True + price_active = True + instance = cls( enabled=ps.get('enabled', False), - mode=ps.get('mode', 'combined'), + mode=mode, allow_full_battery_after=ps.get('allow_full_battery_after', 14), price_limit=price_limit, + time_active=time_active, + price_active=price_active, + solar_cap_active=ps.get('solar_cap_active', False), + feed_in_limit_w=ps.get('feed_in_limit_w', 0.0), + feed_in_limit_headroom=ps.get('feed_in_limit_headroom', 1.0), ) - if instance.enabled and instance.mode == 'combined' \ + if instance.enabled and instance.price_active \ and instance.price_limit is None: - logger.warning( - "peak_shaving.mode='combined' but no peak_shaving.price_limit " - "configured: the price component is disabled; falling back " - "to time-only behaviour. Set a numeric price_limit or change " - "mode to 'time' to silence this warning." - ) + if instance.time_active: + logger.warning( + "peak_shaving price_active is enabled (combined-equivalent: " + "time_active and price_active both active) but no " + "peak_shaving.price_limit configured: the price " + "component is disabled; falling back to time-only " + "behaviour. Set a numeric price_limit or disable " + "price_active to silence this warning." + ) + else: + logger.warning( + "peak_shaving.price_active is enabled but no " + "peak_shaving.price_limit configured: the price " + "component is disabled entirely (time_active is also " + "disabled, so there is no fallback). Set a numeric " + "price_limit or disable price_active to silence this " + "warning." + ) return instance diff --git a/src/batcontrol/logic/next.py b/src/batcontrol/logic/next.py index f80ac733..ca526ec9 100644 --- a/src/batcontrol/logic/next.py +++ b/src/batcontrol/logic/next.py @@ -25,6 +25,7 @@ apply_grid_charge_target_to_recharge, apply_grid_charge_target_to_reserve, ) +from . import solar_limit # Minimum remaining time in hours to prevent division by very small numbers # when calculating charge rates. This constant serves as a safety threshold: @@ -180,18 +181,7 @@ def calculate_inverter_mode(self, calc_input: CalculationInput, # charge if battery capacity available and more stored energy is required if is_charging_possible and required_recharge_energy > 0: - current_minute = calc_timestamp.minute - current_second = calc_timestamp.second - - if self.interval_minutes == 15: - current_interval_start = (current_minute // 15) * 15 - remaining_minutes = (current_interval_start + 15 - - current_minute - current_second / 60) - else: # 60 minutes - remaining_minutes = 60 - current_minute - current_second / 60 - - remaining_time = remaining_minutes / 60 - remaining_time = max(remaining_time, MIN_REMAINING_TIME_HOURS) + remaining_time = self._remaining_interval_hours(calc_timestamp) charge_rate = required_recharge_energy / remaining_time charge_rate = self.common.calculate_charge_rate(charge_rate) @@ -219,9 +209,37 @@ def calculate_inverter_mode(self, calc_input: CalculationInput, if self.calculation_parameters.peak_shaving.enabled: inverter_control_settings = self._apply_peak_shaving( inverter_control_settings, calc_input, calc_timestamp) + inverter_control_settings = self._apply_solar_limit( + inverter_control_settings, calc_input, calc_timestamp) return inverter_control_settings + # ------------------------------------------------------------------ # + # Shared helpers # + # ------------------------------------------------------------------ # + + def _remaining_interval_hours(self, calc_timestamp: datetime.datetime) -> float: + """Return the remaining time (in hours) within the current interval. + + For 15-minute resolution this is the time until the next quarter-hour + boundary; for 60-minute resolution the time until the next full hour. + Floored at ``MIN_REMAINING_TIME_HOURS`` to avoid division by very + small numbers (and the resulting unreasonably high charge rates) when + called close to an interval boundary. + """ + current_minute = calc_timestamp.minute + current_second = calc_timestamp.second + + if self.interval_minutes == 15: + current_interval_start = (current_minute // 15) * 15 + remaining_minutes = (current_interval_start + 15 + - current_minute - current_second / 60) + else: # 60 minutes + remaining_minutes = 60 - current_minute - current_second / 60 + + remaining_time = remaining_minutes / 60 + return max(remaining_time, MIN_REMAINING_TIME_HOURS) + # ------------------------------------------------------------------ # # Peak Shaving # # ------------------------------------------------------------------ # @@ -230,45 +248,50 @@ def _apply_peak_shaving(self, settings: InverterControlSettings, calc_input: CalculationInput, calc_timestamp: datetime.datetime ) -> InverterControlSettings: - """Limit PV charge rate based on the configured peak shaving mode. + """Limit PV charge rate based on the active peak shaving switches. - Mode behaviour (peak_shaving.mode): - 'time' - spread remaining capacity until allow_full_battery_after - 'price' - reserve capacity for upcoming cheap-price PV slots; - inside cheap window, spread if surplus > free capacity - 'combined' - both limits active, stricter one wins + Switch behaviour (peak_shaving.time_active / peak_shaving.price_active): + time_active - spread remaining capacity until allow_full_battery_after + price_active - reserve capacity for upcoming cheap-price PV slots; + inside cheap window, spread if surplus > free capacity + both active - both limits computed, stricter one wins Skipped when: - - 'price' mode and price_limit is not configured + - price_active and price_limit is not configured, and time_active is + also not active (no other component to fall back to) - No PV production right now (nighttime) - - Past allow_full_battery_after hour (all modes) + - Past allow_full_battery_after hour (both components) - Battery in always_allow_discharge region (high SOC) - Force-charge from grid active (MODE -1) - Discharge not allowed (battery preserved for high-price hours) - In 'combined' mode with price_limit=None, falls back to time-only - behaviour (the time component does not require price_limit). + If both time_active and price_active are set but price_limit is + None, falls back to time-only behaviour (the time component does + not require price_limit). Note: evcc checks (charging, connected+pv mode) are handled in - core.py, not here. + core.py, not here. The solar_cap rule is a separate + post-processing step, see :py:meth:`_apply_solar_limit`. """ - mode = self.calculation_parameters.peak_shaving.mode + time_active = self.calculation_parameters.peak_shaving.time_active + price_active = self.calculation_parameters.peak_shaving.price_active price_limit = self.calculation_parameters.peak_shaving.price_limit # Price component needs price_limit configured. - # For 'price' mode: skip entirely (no other component to fall back to). - # For 'combined' mode: fall back to time-only behaviour. The user is - # informed once at config-load time by PeakShavingConfig, so this - # path stays at debug level to avoid per-cycle log spam. - if price_limit is None: - if mode == 'price': + # If time_active is also off: skip entirely (no other component to + # fall back to). If time_active is on: fall back to time-only + # behaviour. The user is informed once at config-load time by + # PeakShavingConfig, so this path stays at debug level to avoid + # per-cycle log spam. + if price_active and price_limit is None: + if not time_active: logger.debug('[PeakShaving] Skipped: price_limit not ' - 'configured for mode price') + 'configured and price_active is the only ' + 'active component') return settings - if mode == 'combined': - logger.debug('[PeakShaving] price_limit not configured; ' - 'combined mode using time-only component') - mode = 'time' + logger.debug('[PeakShaving] price_limit not configured; ' + 'using time-only component') + price_active = False # No production right now: skip if calc_input.production[0] <= 0: @@ -295,13 +318,13 @@ def _apply_peak_shaving(self, settings: InverterControlSettings, 'battery preserved for high-price hours') return settings - # Compute limits according to mode + # Compute limits according to the active switches price_limit_w = -1 time_limit_w = -1 - if mode in ('price', 'combined'): + if price_active: price_limit_w = self._calculate_peak_shaving_charge_limit_price_based(calc_input) - if mode in ('time', 'combined'): + if time_active: time_limit_w = self._calculate_peak_shaving_charge_limit(calc_input, calc_timestamp) candidates = [v for v in (price_limit_w, time_limit_w) if v >= 0] @@ -326,15 +349,98 @@ def _apply_peak_shaving(self, settings: InverterControlSettings, # The limit_battery_charge_rate mode in the inverter layer requires # allow_discharge=True to work correctly. - logger.info('[PeakShaving] mode=%s, PV limit: %d W ' + active_components = ','.join( + name for name, active in + (('time', time_active), ('price', price_active)) if active + ) or 'none' + logger.info('[PeakShaving] active=%s, PV limit: %d W ' '(price-based=%s W, time-based=%s W, full by %d:00)', - mode, settings.limit_battery_charge_rate, + active_components, settings.limit_battery_charge_rate, price_limit_w if price_limit_w >= 0 else 'off', time_limit_w if time_limit_w >= 0 else 'off', self.calculation_parameters.peak_shaving.allow_full_battery_after) return settings + def _apply_solar_limit(self, settings: InverterControlSettings, + calc_input: CalculationInput, + calc_timestamp: datetime.datetime + ) -> InverterControlSettings: + """Apply the solar_cap rule (feed-in limit clip absorption). + + See docs/development/solar-limit-evaluation.md for the algorithm and + the priority rule between rule flavours. In short: this rule emits a + reservation cap ahead of the predicted clip window and a floor + (minimum permitted charge rate) plus capacity-preserving cap inside + it, so the existing time/price peak-shaving caps do not cause + curtailment. The floor overrides every cap (``final = max(floor, + min(caps))``) because a cap below the floor destroys energy. + + Gated on peak_shaving.enabled (checked by the caller), + peak_shaving.solar_cap_active and a configured feed_in_limit_w > 0. + + Deliberately smaller skip list than :py:meth:`_apply_peak_shaving`: + this rule must still act at high SoC (always_allow_discharge region) + and past allow_full_battery_after -- the clip window physically + outlasts the target hour. Skipped only when: + - No PV production right now (nighttime) + - Force-charge from grid active (MODE -1) + - Discharge not allowed (inverter charges surplus unrestricted there + anyway) + """ + peak_shaving = self.calculation_parameters.peak_shaving + if not peak_shaving.solar_cap_active or peak_shaving.feed_in_limit_w <= 0: + return settings + + if calc_input.production[0] <= 0: + return settings + + if settings.charge_from_grid: + logger.debug('[SolarLimit] Skipped: force_charge (MODE -1) active, ' + 'grid charging takes priority') + return settings + + if not settings.allow_discharge: + logger.debug('[SolarLimit] Skipped: discharge not allowed, ' + 'inverter charges surplus unrestricted') + return settings + + interval_h = self.interval_minutes / 60.0 + slot0_hours = self._remaining_interval_hours(calc_timestamp) + + floor_w, cap_w = solar_limit.compute_solar_limit( + calc_input.production, + calc_input.consumption, + peak_shaving.feed_in_limit_w, + interval_h, + calc_input.free_capacity, + self.common.max_capacity, + headroom=peak_shaving.feed_in_limit_headroom, + slot0_hours=slot0_hours, + ) + + if floor_w == 0 and cap_w < 0: + logger.debug('[SolarLimit] Evaluated: no clip predicted, ' + 'no limit needed') + return settings + + 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) + + settings.limit_battery_charge_rate = final_w + + logger.info('[SolarLimit] floor=%d W, cap=%s W, final PV limit=%s W ' + '(feed_in_limit=%d W)', + floor_w, + cap_w if cap_w >= 0 else 'off', + final_w if final_w >= 0 else 'off', + peak_shaving.feed_in_limit_w) + + return settings + def _calculate_peak_shaving_charge_limit_price_based( self, calc_input: CalculationInput) -> int: """Reserve battery free capacity for upcoming cheap-price PV slots. diff --git a/src/batcontrol/logic/solar_limit.py b/src/batcontrol/logic/solar_limit.py new file mode 100644 index 00000000..730c4b71 --- /dev/null +++ b/src/batcontrol/logic/solar_limit.py @@ -0,0 +1,141 @@ +"""Solar feed-in limit ("solar_cap") peak-shaving rule. + +Pure functions for the clip-absorption rule described in +docs/development/solar-limit-evaluation.md. The rule works on the existing +forecast arrays (Wh per interval, index 0 = current interval) and produces +two outputs per evaluation: + + floor_w: minimum PV charge rate (W) the battery must be *permitted* to + sustain right now to absorb power above the feed-in limit + ("clip" energy that would otherwise be curtailed and lost). + With a greedy-charging inverter a floor never forces charging + that does not exist -- it only raises the applied cap, and the + inverter charges ``min(actual surplus, cap)``. + cap_w: an upper limit on the PV-to-battery charge rate, either to + reserve free battery capacity ahead of an upcoming clip window + ("reservation") or to keep some capacity free while already + inside the clip window. ``-1`` means no cap, ``0`` blocks PV + charging entirely. + +This module bakes in the settled semantics from the evaluation (headroom +applied to the forecast surplus, floor computed from the headroom-adjusted +clip) -- see the "Forecast-error plan" section of the linked document. +""" +import numpy as np + + +# pylint: disable=too-many-arguments,too-many-positional-arguments +# pylint: disable=too-many-locals,too-many-return-statements +def compute_solar_limit( + production_wh, consumption_wh, feed_in_limit_w, + interval_h, free_capacity_wh, max_capacity_wh, + headroom=1.0, slot0_hours=None): + """Compute the solar-cap rule output (floor, cap) for the current slot. + + Args: + production_wh: forecast PV energy per slot (Wh), index 0 = now. + consumption_wh: forecast consumption per slot (Wh). + feed_in_limit_w: grid feed-in power limit in W. <= 0 or None makes + the rule neutral (no effect). + interval_h: slot length in hours (e.g. 0.25 or 1.0). + free_capacity_wh: battery free capacity (Wh). + max_capacity_wh: battery max capacity (Wh). + headroom: safety factor >= 1.0 applied to the forecast surplus + before the clip is computed (reservation and floor sizing). + Forecasts systematically underestimate PV peaks; headroom + reconstructs the higher real curve. Default 1.0 (neutral). + slot0_hours: remaining hours in the current (partial) slot. + Defaults to ``interval_h``. + + Returns: + (floor_w, cap_w): both ints. ``floor_w`` of 0 means no floor. + ``cap_w`` of -1 means no cap, 0 blocks PV charging. + """ + if feed_in_limit_w is None or feed_in_limit_w <= 0: + return 0, -1 + if slot0_hours is None: + slot0_hours = interval_h + + n = min(len(production_wh), len(consumption_wh)) + # Production window ends at the first slot with zero production (same + # convention as the existing time/price peak-shaving rules). + prod_end = n + for i in range(n): + if float(production_wh[i]) == 0: + prod_end = i + break + if prod_end == 0: + return 0, -1 + + slot_h = np.full(prod_end, interval_h, dtype=float) + slot_h[0] = slot0_hours + + surplus_wh = np.clip( + np.asarray(production_wh[:prod_end], dtype=float) + - np.asarray(consumption_wh[:prod_end], dtype=float), + 0, None) + surplus_hr_wh = surplus_wh * headroom + feed_allow_wh = feed_in_limit_w * slot_h + clip_wh = np.clip(surplus_hr_wh - feed_allow_wh, 0, None) + + clip_slots = np.nonzero(clip_wh > 0)[0] + if len(clip_slots) == 0: + return 0, -1 + + first_clip = int(clip_slots[0]) + + # -- Case A: before the clip window -> reservation cap ---------------- # + # Free capacity minus the predicted clip energy is spread evenly over + # the slots until the window starts. This prevents exportable energy + # from displacing clip energy in the battery 1:1. + if first_clip > 0: + total_clip_wh = min(float(np.sum(clip_wh)), max_capacity_wh) + allowed_wh = free_capacity_wh - total_clip_wh + if allowed_wh <= 0: + return 0, 0 # block PV charging, keep all capacity for the clip + hours_before = slot0_hours + (first_clip - 1) * interval_h + return 0, int(allowed_wh / hours_before) + + # -- Case B: inside a clip slot -> floor + capacity-preserving cap ---- # + # The floor is computed from the headroom-adjusted clip: with a + # greedy-charging inverter this only ever raises the allowed cap, it + # never forces charging energy that does not actually exist. + floor_w = clip_wh[0] / slot0_hours + + # The "everything fits, no cap needed" check uses the RAW (not + # headroom-adjusted) surplus -- this is a physical check, not a safety + # margin. + total_surplus_wh = float(np.sum(surplus_wh)) + if total_surplus_wh <= free_capacity_wh: + return int(floor_w), -1 # everything fits, no cap needed + + remaining_clip_wh = float(np.sum(clip_wh)) + extra_wh = max(0.0, free_capacity_wh - remaining_clip_wh) + remaining_prod_h = float(np.sum(slot_h)) + # When clip energy alone exceeds free capacity (extra == 0) the cap + # equals the floor: the battery absorbs ONLY otherwise-curtailed + # energy, exportable surplus goes to the grid instead of displacing + # clip energy. + cap_w = int(floor_w + extra_wh / remaining_prod_h) + return int(floor_w), cap_w + + +def merge_limits(floor_w, caps): + """Merge the solar floor with a list of caps: ``final = max(floor, min(caps))``. + + ``caps`` entries: ``None`` or a negative value other than the sentinel + means "no opinion" and is ignored; ``-1`` explicitly means "no cap"; + ``0`` blocks charging. Rationale (see the "Priorities between the rule + flavors" section of docs/development/solar-limit-evaluation.md): caps + optimize economics (shift charging in time), the floor prevents + physical loss (curtailment) and therefore overrides every cap. An + unlimited cap (``-1``) automatically satisfies any floor because the + inverter then charges PV surplus greedily anyway. + + Returns: + int: -1 (no limit), 0 (block), or a positive charge rate in W. + """ + active = [c for c in caps if c is not None and c >= 0] + if not active: + return -1 + return max(int(floor_w), min(active)) From 8e5e59fbf77d721197d1b5e4b4fcc639b3fce07d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 05:14:49 +0000 Subject: [PATCH 09/13] test: add coverage for the solar_cap peak-shaving rule 50 new tests: pure-function coverage for compute_solar_limit (neutral cases, reservation case A, floor/cap case B, headroom, partial slot 0, 15-min interval, reference-day spot check) and merge_limits priority semantics; NextLogic integration (floor overrides time and blocking price caps, neutral by default, active at high SoC and past the target hour, force-charge and no-discharge skips, 500 W minimum); config parsing (mode deprecation mapping, switches win over mode, defaults, validation errors). helpers.make_logic gains peak_shaving and interval_minutes passthrough kwargs. Full suite: 837 passed. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- tests/batcontrol/logic/helpers.py | 16 +- tests/batcontrol/logic/test_peak_shaving.py | 238 ++++++++++++++++++- tests/batcontrol/logic/test_solar_limit.py | 229 ++++++++++++++++++ tests/batcontrol/test_peak_shaving_config.py | 164 +++++++++++++ 4 files changed, 643 insertions(+), 4 deletions(-) create mode 100644 tests/batcontrol/logic/test_solar_limit.py diff --git a/tests/batcontrol/logic/helpers.py b/tests/batcontrol/logic/helpers.py index 15f9a40d..7920dd91 100644 --- a/tests/batcontrol/logic/helpers.py +++ b/tests/batcontrol/logic/helpers.py @@ -32,11 +32,18 @@ def make_logic(logic_cls, *, always_allow_discharge_limit=0.90, min_charge_energy=100, peak_shaving_enabled=False, - grid_charge_target=None): + peak_shaving=None, + grid_charge_target=None, + interval_minutes=60): """Create a logic instance with common scenario defaults. The CommonLogic singleton is reset so each helper call applies the requested singleton-backed tuning values independently. + + ``peak_shaving``, if given, is used as-is (a full ``PeakShavingConfig`` + instance) and takes priority over ``peak_shaving_enabled`` -- pass it + when a test needs to configure switches/feed_in_limit_w/etc. beyond the + simple enabled/disabled toggle. """ CommonLogic._instance = None CommonLogic.get_instance( @@ -45,7 +52,7 @@ def make_logic(logic_cls, *, max_capacity=capacity_wh, min_charge_energy=min_charge_energy, ) - logic = logic_cls(timezone=timezone, interval_minutes=60) + logic = logic_cls(timezone=timezone, interval_minutes=interval_minutes) logic.set_calculation_parameters(CalculationParameters( max_charging_from_grid_limit=max_charging_from_grid_limit, min_price_difference=min_price_difference, @@ -53,7 +60,10 @@ def make_logic(logic_cls, *, max_capacity=capacity_wh, min_grid_charge_soc=min_grid_charge_soc, preserve_min_grid_charge_soc=preserve_min_grid_charge_soc, - peak_shaving=PeakShavingConfig(enabled=peak_shaving_enabled), + peak_shaving=( + peak_shaving if peak_shaving is not None + else PeakShavingConfig(enabled=peak_shaving_enabled) + ), grid_charge_target=grid_charge_target or GridChargeTargetConfig(), )) return logic diff --git a/tests/batcontrol/logic/test_peak_shaving.py b/tests/batcontrol/logic/test_peak_shaving.py index 5a705620..f8a45204 100644 --- a/tests/batcontrol/logic/test_peak_shaving.py +++ b/tests/batcontrol/logic/test_peak_shaving.py @@ -21,6 +21,7 @@ PeakShavingConfig, ) from batcontrol.logic.common import CommonLogic +from .helpers import make_logic logging.basicConfig(level=logging.DEBUG) @@ -841,7 +842,7 @@ def _make_input(self, production, consumption, free_capacity, to isolate the charge-limit computation from the guard check. """ if stored_energy is None: - stored_energy = self._MAX_CAPACITY * 0.5 # 5 000 Wh – below gate + stored_energy = self._MAX_CAPACITY * 0.5 # 5 000 Wh - below gate n = len(production) return CalculationInput( production=np.array(production, dtype=float), @@ -1045,3 +1046,238 @@ def test_min_grid_charge_soc_does_not_block_discharge_at_high_price(self): self.assertTrue(result.allow_discharge) self.assertFalse(result.charge_from_grid) + + +class TestSolarLimitIntegration(unittest.TestCase): + """Integration tests for the solar_cap rule via NextLogic. + + Logic instances are built with tests/batcontrol/logic/helpers.py's + make_logic(), extended to accept a full PeakShavingConfig directly. + max_capacity=10000 throughout (matches the CommonLogic setup used by + the rest of this file). + + Shared clip scenario (headroom=1.0, interval=1h, slot0_hours=1.0, + i.e. calc_timestamp on the hour): + production = [9000, 9000, 9000, 0], consumption = [400]*4 + surplus = 8600 W/slot for slots 0..2 (prod_end=3) + feed_allow = 6000 Wh/slot (feed_in_limit_w=6000) + clip = 2600 Wh/slot -> currently clipping (first_clip=0) + floor = clip_wh[0] / 1.0 = 2600 W + total_surplus (raw) = 3 * 8600 = 25800 Wh + remaining_clip = 3 * 2600 = 7800 Wh + """ + + PRODUCTION = [9000, 9000, 9000, 0] + CONSUMPTION = [400, 400, 400, 400] + FEED_IN_LIMIT_W = 6000 + MAX_CAPACITY = 10000 + TS = datetime.datetime(2025, 6, 20, 11, 0, tzinfo=datetime.timezone.utc) + + def _make_settings(self, allow_discharge=True, charge_from_grid=False, + charge_rate=0, limit_battery_charge_rate=-1): + return InverterControlSettings( + allow_discharge=allow_discharge, + charge_from_grid=charge_from_grid, + charge_rate=charge_rate, + limit_battery_charge_rate=limit_battery_charge_rate, + ) + + def _make_calc_input(self, free_capacity, production=None, + consumption=None, prices=None): + production = production if production is not None else self.PRODUCTION + consumption = consumption if consumption is not None else self.CONSUMPTION + stored_energy = self.MAX_CAPACITY - free_capacity + if prices is None: + prices = np.zeros(len(production)) + return CalculationInput( + production=np.array(production, dtype=float), + consumption=np.array(consumption, dtype=float), + prices=np.array(prices, dtype=float), + stored_energy=stored_energy, + stored_usable_energy=stored_energy, + free_capacity=free_capacity, + ) + + def _make_logic(self, peak_shaving): + return make_logic(NextLogic, capacity_wh=self.MAX_CAPACITY, + peak_shaving=peak_shaving) + + def test_floor_overrides_time_cap(self): + """Floor (2600 W) overrides the time-ramp cap while clipping now. + + free_capacity=9500 Wh (battery mostly empty), time_active only + (price_active off). + Time ramp: n=3 slots to 14:00 (target hour), free=9500 Wh + expected_surplus = 3*8600 = 25800 Wh > free -> ramp applies + wh_current = 2*9500/(3*4) = 1583.3 -> 1583 W + Solar rule: scarcity (25800 > 9500 free), and free(9500) > + remaining_clip(7800) -> extra=1700, remaining_prod_h=3 + cap = 2600 + 1700/3 = 3166 W; floor stays 2600 W. + merge_limits(2600, [1583, 3166]) = max(2600, 1583) = 2600. + """ + peak_shaving = PeakShavingConfig( + enabled=True, time_active=True, price_active=False, + solar_cap_active=True, feed_in_limit_w=self.FEED_IN_LIMIT_W, + allow_full_battery_after=14) + logic = self._make_logic(peak_shaving) + calc_input = self._make_calc_input(free_capacity=9500) + settings = self._make_settings() + + result = logic._apply_peak_shaving(settings, calc_input, self.TS) + self.assertEqual(result.limit_battery_charge_rate, 1583) + + result = logic._apply_solar_limit(result, calc_input, self.TS) + self.assertEqual(result.limit_battery_charge_rate, 2600) + self.assertGreater(result.limit_battery_charge_rate, 1583) + + def test_floor_overrides_price_cap_zero(self): + """Floor overrides a blocking (0 W) price cap while clipping now. + + free_capacity=5000 Wh. Price rule (price_active, price_limit=0.05): + the only cheap slot is index 2 (price 0), first_cheap_slot=2 > 0 + -> reserve = surplus[2] = 8600 Wh, additional_allowed = 5000-8600 + < 0 -> price cap = 0 (block PV charging). + Solar rule: scarcity, free(5000) <= remaining_clip(7800) + -> cap == floor == 2600 W. + merge_limits(2600, [0, 2600]) = max(2600, min(0, 2600)) = 2600. + """ + peak_shaving = PeakShavingConfig( + enabled=True, time_active=False, price_active=True, + price_limit=0.05, solar_cap_active=True, + feed_in_limit_w=self.FEED_IN_LIMIT_W, allow_full_battery_after=14) + logic = self._make_logic(peak_shaving) + prices = [10.0, 10.0, 0.0, 10.0] + calc_input = self._make_calc_input(free_capacity=5000, prices=prices) + settings = self._make_settings() + + result = logic._apply_peak_shaving(settings, calc_input, self.TS) + self.assertEqual(result.limit_battery_charge_rate, 0) + + result = logic._apply_solar_limit(result, calc_input, self.TS) + self.assertEqual(result.limit_battery_charge_rate, 2600) + + def test_neutral_by_default(self): + """solar_cap_active=False -> _apply_solar_limit is a no-op. + + Same setup as test_floor_overrides_time_cap, but with the solar + switch off: the settings after _apply_solar_limit must be + bit-identical to the settings right after _apply_peak_shaving. + """ + peak_shaving = PeakShavingConfig( + enabled=True, time_active=True, price_active=False, + solar_cap_active=False, feed_in_limit_w=self.FEED_IN_LIMIT_W, + allow_full_battery_after=14) + logic = self._make_logic(peak_shaving) + calc_input = self._make_calc_input(free_capacity=9500) + settings = self._make_settings() + + before = logic._apply_peak_shaving(settings, calc_input, self.TS) + before_snapshot = InverterControlSettings( + allow_discharge=before.allow_discharge, + charge_from_grid=before.charge_from_grid, + charge_rate=before.charge_rate, + limit_battery_charge_rate=before.limit_battery_charge_rate, + ) + + after = logic._apply_solar_limit(before, calc_input, self.TS) + self.assertEqual(after, before_snapshot) + + def test_high_soc_solar_floor_still_applies(self): + """always_allow_discharge region: peak shaving skips, solar acts. + + free_capacity=500 Wh -> stored=9500/10000=95% >= 90% threshold + -> _apply_peak_shaving skips (limit stays -1). + Solar rule: scarcity, free(500) <= remaining_clip(7800) + -> cap == floor == 2600 W. merge_limits(2600, [-1, 2600]) = 2600. + """ + peak_shaving = PeakShavingConfig( + enabled=True, time_active=True, price_active=False, + solar_cap_active=True, feed_in_limit_w=self.FEED_IN_LIMIT_W, + allow_full_battery_after=14) + logic = self._make_logic(peak_shaving) + calc_input = self._make_calc_input(free_capacity=500) + settings = self._make_settings() + + result = logic._apply_peak_shaving(settings, calc_input, self.TS) + self.assertEqual(result.limit_battery_charge_rate, -1) + + result = logic._apply_solar_limit(result, calc_input, self.TS) + self.assertEqual(result.limit_battery_charge_rate, 2600) + + def test_past_allow_full_battery_after_solar_floor_still_applies(self): + """Past the target hour: peak shaving skips, solar floor still acts. + + ts hour=15 >= allow_full_battery_after=14 -> _apply_peak_shaving + skips (limit stays -1). free_capacity=5000 Wh gives the same + scarcity math as test_floor_overrides_price_cap_zero: cap == floor + == 2600 W. + """ + peak_shaving = PeakShavingConfig( + enabled=True, time_active=True, price_active=False, + solar_cap_active=True, feed_in_limit_w=self.FEED_IN_LIMIT_W, + allow_full_battery_after=14) + logic = self._make_logic(peak_shaving) + calc_input = self._make_calc_input(free_capacity=5000) + settings = self._make_settings() + ts = datetime.datetime(2025, 6, 20, 15, 0, + tzinfo=datetime.timezone.utc) + + result = logic._apply_peak_shaving(settings, calc_input, ts) + self.assertEqual(result.limit_battery_charge_rate, -1) + + result = logic._apply_solar_limit(result, calc_input, ts) + self.assertEqual(result.limit_battery_charge_rate, 2600) + + def test_force_charge_skips_solar_limit(self): + """charge_from_grid active -> _apply_solar_limit leaves settings unchanged.""" + peak_shaving = PeakShavingConfig( + enabled=True, solar_cap_active=True, + feed_in_limit_w=self.FEED_IN_LIMIT_W) + logic = self._make_logic(peak_shaving) + calc_input = self._make_calc_input(free_capacity=5000) + settings = self._make_settings( + allow_discharge=False, charge_from_grid=True, charge_rate=3000) + + result = logic._apply_solar_limit(settings, calc_input, self.TS) + + self.assertEqual(result.limit_battery_charge_rate, -1) + self.assertTrue(result.charge_from_grid) + + def test_allow_discharge_false_skips_solar_limit(self): + """allow_discharge=False -> _apply_solar_limit leaves settings unchanged.""" + peak_shaving = PeakShavingConfig( + enabled=True, solar_cap_active=True, + feed_in_limit_w=self.FEED_IN_LIMIT_W) + logic = self._make_logic(peak_shaving) + calc_input = self._make_calc_input(free_capacity=5000) + settings = self._make_settings(allow_discharge=False) + + result = logic._apply_solar_limit(settings, calc_input, self.TS) + + self.assertEqual(result.limit_battery_charge_rate, -1) + self.assertFalse(result.allow_discharge) + + def test_enforce_min_pv_charge_rate_on_solar_limit(self): + """A small positive solar cap (<500 W) is raised to 500 W. + + Case A reservation: production=[3000,3000,9000,9000,0], + consumption=400/slot, feed_in_limit_w=6000 -> clip slots 2,3, + clip=2600 Wh each, total=5200 Wh. free_capacity=5400 + -> allowed=200, hours_before=2 -> cap=int(200/2)=100 W, floor=0. + merge_limits(0, [-1, 100]) = 100 -> enforced up to 500 W. + """ + peak_shaving = PeakShavingConfig( + enabled=True, solar_cap_active=True, + feed_in_limit_w=self.FEED_IN_LIMIT_W) + logic = self._make_logic(peak_shaving) + production = [3000, 3000, 9000, 9000, 0] + consumption = [400] * 5 + calc_input = self._make_calc_input( + free_capacity=5400, production=production, consumption=consumption) + settings = self._make_settings() + ts = datetime.datetime(2025, 6, 20, 8, 0, + tzinfo=datetime.timezone.utc) + + result = logic._apply_solar_limit(settings, calc_input, ts) + + self.assertEqual(result.limit_battery_charge_rate, 500) diff --git a/tests/batcontrol/logic/test_solar_limit.py b/tests/batcontrol/logic/test_solar_limit.py new file mode 100644 index 00000000..c04917a6 --- /dev/null +++ b/tests/batcontrol/logic/test_solar_limit.py @@ -0,0 +1,229 @@ +"""Tests for the pure solar_cap rule functions in logic/solar_limit.py. + +Covers compute_solar_limit() (Case A reservation, Case B floor/cap, +headroom, partial slot 0, 15-minute intervals) and merge_limits() (the +floor-overrides-every-cap priority rule). See +docs/development/solar-limit-evaluation.md for the algorithm spec and the +reference-day numbers reproduced in test_reference_day_first_clip_floor. +""" +import unittest + +from batcontrol.logic.solar_limit import compute_solar_limit, merge_limits + + +class TestComputeSolarLimitNeutral(unittest.TestCase): + """Cases where the rule must have no effect.""" + + def test_feed_in_limit_zero_is_neutral(self): + """feed_in_limit_w=0 -> (0, -1) regardless of the arrays.""" + floor, cap = compute_solar_limit( + production_wh=[9000, 9000], consumption_wh=[400, 400], + feed_in_limit_w=0, interval_h=1.0, + free_capacity_wh=1000, max_capacity_wh=10000) + self.assertEqual((floor, cap), (0, -1)) + + def test_feed_in_limit_none_is_neutral(self): + """feed_in_limit_w=None -> (0, -1).""" + floor, cap = compute_solar_limit( + production_wh=[9000, 9000], consumption_wh=[400, 400], + feed_in_limit_w=None, interval_h=1.0, + free_capacity_wh=1000, max_capacity_wh=10000) + self.assertEqual((floor, cap), (0, -1)) + + def test_no_production_now_is_neutral(self): + """production[0] == 0 (nighttime) -> window length 0 -> (0, -1).""" + floor, cap = compute_solar_limit( + production_wh=[0, 5000], consumption_wh=[100, 100], + feed_in_limit_w=1000, interval_h=1.0, + free_capacity_wh=5000, max_capacity_wh=10000) + self.assertEqual((floor, cap), (0, -1)) + + def test_surplus_below_limit_no_clip(self): + """Surplus stays below the feed-in limit everywhere -> (0, -1).""" + floor, cap = compute_solar_limit( + production_wh=[2000, 2000], consumption_wh=[500, 500], + feed_in_limit_w=3000, interval_h=1.0, + free_capacity_wh=5000, max_capacity_wh=10000) + self.assertEqual((floor, cap), (0, -1)) + + +class TestComputeSolarLimitCaseA(unittest.TestCase): + """Case A: before the clip window -> reservation cap.""" + + # Shared scenario: clip window starts at slot 2 (clip 2600 Wh/slot at + # slots 2 and 3), total clip 5200 Wh. + # production = [3000, 3000, 9000, 9000, 0], consumption = [400]*5 + # surplus = [2600, 2600, 8600, 8600] (prod_end=4) + # feed_allow = 6000 Wh/slot -> clip = [0, 0, 2600, 2600] + PRODUCTION = [3000, 3000, 9000, 9000, 0] + CONSUMPTION = [400] * 5 + FEED_IN_LIMIT_W = 6000 + + def test_reservation_cap(self): + """free=8000, max=10000 -> allowed=8000-5200=2800, hours_before=2 + -> cap = int(2800/2) = 1400, floor = 0.""" + floor, cap = compute_solar_limit( + self.PRODUCTION, self.CONSUMPTION, self.FEED_IN_LIMIT_W, + interval_h=1.0, free_capacity_wh=8000, max_capacity_wh=10000) + self.assertEqual((floor, cap), (0, 1400)) + + def test_reservation_blocks_when_free_capacity_too_small(self): + """free=5000 <= total_clip(5200) -> (0, 0), PV charging blocked.""" + floor, cap = compute_solar_limit( + self.PRODUCTION, self.CONSUMPTION, self.FEED_IN_LIMIT_W, + interval_h=1.0, free_capacity_wh=5000, max_capacity_wh=10000) + self.assertEqual((floor, cap), (0, 0)) + + +class TestComputeSolarLimitCaseB(unittest.TestCase): + """Case B: inside the clip window -> floor + capacity-preserving cap.""" + + # Currently clipping: production=[8000,8000,0], consumption=[400,400,0] + # surplus = [7600, 7600] (prod_end=2), feed_allow = 6000/slot + # clip = [1600, 1600] -> floor = clip[0]/1.0 = 1600 + # total_surplus (raw) = 15200, remaining_clip = 3200 + PRODUCTION = [8000, 8000, 0] + CONSUMPTION = [400, 400, 0] + FEED_IN_LIMIT_W = 6000 + + def test_scarcity_cap_equals_floor(self): + """free=2000 <= remaining_clip(3200) -> extra=0 -> cap == floor.""" + floor, cap = compute_solar_limit( + self.PRODUCTION, self.CONSUMPTION, self.FEED_IN_LIMIT_W, + interval_h=1.0, free_capacity_wh=2000, max_capacity_wh=10000) + self.assertEqual(floor, 1600) + self.assertEqual(cap, floor) + + def test_abundance_no_cap_needed(self): + """free=20000 >= total raw surplus(15200) -> (floor, -1).""" + floor, cap = compute_solar_limit( + self.PRODUCTION, self.CONSUMPTION, self.FEED_IN_LIMIT_W, + interval_h=1.0, free_capacity_wh=20000, max_capacity_wh=30000) + self.assertEqual((floor, cap), (1600, -1)) + + def test_extra_spread_over_remaining_slots(self): + """free=5000: between remaining_clip(3200) and total surplus(15200). + extra = 5000-3200 = 1800, remaining_prod_h = 2 + -> cap = int(1600 + 1800/2) = 2500.""" + floor, cap = compute_solar_limit( + self.PRODUCTION, self.CONSUMPTION, self.FEED_IN_LIMIT_W, + interval_h=1.0, free_capacity_wh=5000, max_capacity_wh=10000) + self.assertEqual((floor, cap), (1600, 2500)) + + +class TestComputeSolarLimitHeadroom(unittest.TestCase): + """Headroom applied to the forecast surplus before clip computation.""" + + def test_headroom_creates_a_clip_slot_that_raw_surplus_would_miss(self): + """production=5500, consumption=500 -> raw surplus=5000 (< limit + 6000, no clip with headroom=1.0). With headroom=1.25 the + headroom-adjusted surplus is 5000*1.25=6250 > 6000 -> clip=250, + floor=250 (Case B). free_capacity is large enough that the raw + total surplus (5000) still fits -> cap stays -1. + """ + floor_neutral, cap_neutral = compute_solar_limit( + [5500], [500], feed_in_limit_w=6000, interval_h=1.0, + free_capacity_wh=10000, max_capacity_wh=10000, headroom=1.0) + self.assertEqual((floor_neutral, cap_neutral), (0, -1)) + + floor_headroom, cap_headroom = compute_solar_limit( + [5500], [500], feed_in_limit_w=6000, interval_h=1.0, + free_capacity_wh=10000, max_capacity_wh=10000, headroom=1.25) + self.assertEqual((floor_headroom, cap_headroom), (250, -1)) + + +class TestComputeSolarLimitPartialSlot(unittest.TestCase): + """slot0_hours: remaining hours in the current (partial) interval.""" + + def test_slot0_hours_halved_doubles_the_slot0_floor(self): + """production=5000, consumption=500 -> surplus=4500. + slot0_hours=0.5 -> feed_allow = 2000*0.5 = 1000 + -> clip_wh[0] = 4500-1000 = 3500 + -> floor = clip_wh[0] / 0.5 = 2 * clip_wh[0] = 7000 + free_capacity (20000) covers the raw total surplus (4500) -> cap=-1. + """ + floor, cap = compute_solar_limit( + [5000], [500], feed_in_limit_w=2000, interval_h=1.0, + free_capacity_wh=20000, max_capacity_wh=20000, + headroom=1.0, slot0_hours=0.5) + clip_wh_slot0 = 3500 + self.assertEqual(floor, 2 * clip_wh_slot0) + self.assertEqual((floor, cap), (7000, -1)) + + +class TestComputeSolarLimit15MinInterval(unittest.TestCase): + """15-minute resolution variant of a simple Case B scenario.""" + + def test_quarter_hour_interval(self): + """3 slots of 15 min: 1200 Wh production (4800 W), 100 Wh + consumption (400 W) per slot; feed_in_limit_w=3000. + surplus_wh = 1100/slot, feed_allow_wh = 3000*0.25 = 750/slot + clip_wh = 350/slot -> floor = 350/0.25 = 1400 + total_surplus = 3300 Wh <= free_capacity(10000) -> cap = -1. + """ + floor, cap = compute_solar_limit( + [1200, 1200, 1200, 0], [100, 100, 100, 100], + feed_in_limit_w=3000, interval_h=0.25, + free_capacity_wh=10000, max_capacity_wh=15000) + self.assertEqual((floor, cap), (1400, -1)) + + +class TestComputeSolarLimitReferenceDay(unittest.TestCase): + """Spot check against docs/development/solar-limit-evaluation.md scenario 1. + + At 11:00 the documented floor sequence is 1200 -> 2500 -> 2400 -> 1400 W + as the window progresses; this test reproduces only the first (1200 W) + value for the slot evaluated at 11:00, per the task's "keep it simple" + guidance. + """ + + def test_reference_day_first_clip_floor(self): + """production[0]=7600, consumption=400/slot, limit=6000 (1h slots): + surplus[0] = 7200, feed_allow[0] = 6000 -> clip[0] = 1200 + -> floor = 1200 W. free_capacity is large enough that the whole + window's raw surplus (37400 Wh) fits -> cap = -1. + """ + production = [7600, 8900, 8800, 7800, 6300, 0] + consumption = [400] * 6 + floor, cap = compute_solar_limit( + production, consumption, feed_in_limit_w=6000, interval_h=1.0, + free_capacity_wh=50000, max_capacity_wh=50000) + self.assertEqual(floor, 1200) + self.assertEqual(cap, -1) + + +class TestMergeLimits(unittest.TestCase): + """merge_limits: final = max(floor, min(active caps)).""" + + def test_no_caps_returns_no_limit(self): + """Empty caps list -> -1, regardless of the floor.""" + self.assertEqual(merge_limits(1500, []), -1) + + def test_all_caps_none_or_sentinel_returns_no_limit(self): + """None and -1 both mean 'no opinion' -> -1, floor irrelevant.""" + self.assertEqual(merge_limits(0, [None, -1]), -1) + + def test_positive_floor_with_unlimited_cap_returns_no_limit(self): + """A single -1 ('no cap') satisfies any floor -> -1.""" + self.assertEqual(merge_limits(1500, [-1]), -1) + + def test_strictest_cap_wins_when_floor_is_zero(self): + """floor=0, caps=[500, 300] -> 300 (the strictest cap).""" + self.assertEqual(merge_limits(0, [500, 300]), 300) + + def test_floor_overrides_a_looser_cap(self): + """floor=1500, caps=[500] -> 1500 (floor > cap).""" + self.assertEqual(merge_limits(1500, [500]), 1500) + + def test_floor_overrides_a_blocking_cap(self): + """floor=1500, caps=[0] -> 1500: the floor overrides even a cap + that would otherwise block charging entirely.""" + self.assertEqual(merge_limits(1500, [0]), 1500) + + def test_zero_floor_with_blocking_cap_blocks(self): + """floor=0, caps=[0] -> 0 (nothing to override with).""" + self.assertEqual(merge_limits(0, [0]), 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/batcontrol/test_peak_shaving_config.py b/tests/batcontrol/test_peak_shaving_config.py index 6d27a49c..abafd162 100644 --- a/tests/batcontrol/test_peak_shaving_config.py +++ b/tests/batcontrol/test_peak_shaving_config.py @@ -197,3 +197,167 @@ def test_replace_does_not_re_emit_warning(self, caplog): dataclasses.replace(cfg, enabled=True) warnings = [r for r in caplog.records if r.levelname == 'WARNING'] assert warnings == [] + + +class TestPeakShavingConfigModeDeprecationMapping: + """Test the deprecated `mode` -> explicit switches mapping in from_config. + + See docs/development/solar-limit-evaluation.md ("Configuration design: + one switch per rule") for the mapping rules: mode='time' -> + time_active=True, price_active=False; mode='price' -> the reverse; + mode='combined' -> both True. + """ + + def test_mode_time_maps_to_time_only(self): + cfg = PeakShavingConfig.from_config({ + 'peak_shaving': {'mode': 'time'} + }) + assert cfg.time_active is True + assert cfg.price_active is False + + def test_mode_price_maps_to_price_only(self): + cfg = PeakShavingConfig.from_config({ + 'peak_shaving': {'mode': 'price', 'price_limit': 0.05} + }) + assert cfg.time_active is False + assert cfg.price_active is True + + def test_mode_combined_maps_to_both_active(self): + cfg = PeakShavingConfig.from_config({ + 'peak_shaving': {'mode': 'combined', 'price_limit': 0.05} + }) + assert cfg.time_active is True + assert cfg.price_active is True + + def test_switches_win_over_mode_with_warning(self, caplog): + """An explicit switch key present alongside `mode` wins; `mode` is + ignored entirely and a warning is logged.""" + with caplog.at_level('WARNING', logger=TestPeakShavingConfigFallbackWarning.LOGGER): + cfg = PeakShavingConfig.from_config({ + 'peak_shaving': {'mode': 'time', 'price_active': True} + }) + assert cfg.price_active is True + messages = [r.getMessage() for r in caplog.records + if r.levelname == 'WARNING'] + assert any('mode' in m and 'deprecated' in m for m in messages) + + def test_solar_cap_active_switch_present_ignores_mode(self, caplog): + """solar_cap_active alone (without time_active/price_active keys) + also counts as 'switches present' and triggers the mode-ignored + warning; the unspecified switches default to True.""" + with caplog.at_level('WARNING', logger=TestPeakShavingConfigFallbackWarning.LOGGER): + cfg = PeakShavingConfig.from_config({ + 'peak_shaving': { + 'mode': 'price', 'solar_cap_active': True, + 'feed_in_limit_w': 6000}, + }) + assert cfg.solar_cap_active is True + assert cfg.time_active is True + assert cfg.price_active is True + messages = [r.getMessage() for r in caplog.records + if r.levelname == 'WARNING'] + assert any('mode' in m and 'deprecated' in m for m in messages) + + +class TestPeakShavingConfigDefaults: + """Test the default values of the new solar_cap fields.""" + + def test_empty_dict_defaults(self): + cfg = PeakShavingConfig.from_config({}) + assert cfg.time_active is True + assert cfg.price_active is True + assert cfg.solar_cap_active is False + assert cfg.feed_in_limit_w == 0.0 + assert cfg.feed_in_limit_headroom == 1.0 + + def test_empty_peak_shaving_section_defaults(self): + cfg = PeakShavingConfig.from_config({'peak_shaving': {}}) + assert cfg.time_active is True + assert cfg.price_active is True + assert cfg.solar_cap_active is False + assert cfg.feed_in_limit_w == 0.0 + assert cfg.feed_in_limit_headroom == 1.0 + + def test_dataclass_defaults_match(self): + cfg = PeakShavingConfig() + assert cfg.time_active is True + assert cfg.price_active is True + assert cfg.solar_cap_active is False + assert cfg.feed_in_limit_w == 0.0 + assert cfg.feed_in_limit_headroom == 1.0 + + +class TestPeakShavingConfigSolarCapValidation: + """Validation of feed_in_limit_w and feed_in_limit_headroom.""" + + def test_feed_in_limit_w_negative_raises(self): + with pytest.raises(ValueError, match='peak_shaving.feed_in_limit_w'): + PeakShavingConfig(feed_in_limit_w=-1) + + def test_feed_in_limit_w_bool_rejected(self): + with pytest.raises(ValueError, match='peak_shaving.feed_in_limit_w'): + PeakShavingConfig(feed_in_limit_w=True) + + def test_feed_in_limit_w_zero_accepted(self): + cfg = PeakShavingConfig(feed_in_limit_w=0) + assert cfg.feed_in_limit_w == 0 + + def test_feed_in_limit_w_positive_accepted(self): + cfg = PeakShavingConfig(feed_in_limit_w=6000) + assert cfg.feed_in_limit_w == 6000 + + def test_feed_in_limit_w_string_rejected(self): + with pytest.raises(ValueError, match='peak_shaving.feed_in_limit_w'): + PeakShavingConfig(feed_in_limit_w='6000') + + def test_feed_in_limit_headroom_below_one_raises(self): + with pytest.raises(ValueError, + match='peak_shaving.feed_in_limit_headroom'): + PeakShavingConfig(feed_in_limit_headroom=0.9) + + def test_feed_in_limit_headroom_bool_rejected(self): + with pytest.raises(ValueError, + match='peak_shaving.feed_in_limit_headroom'): + PeakShavingConfig(feed_in_limit_headroom=False) + + def test_feed_in_limit_headroom_one_accepted(self): + cfg = PeakShavingConfig(feed_in_limit_headroom=1.0) + assert cfg.feed_in_limit_headroom == 1.0 + + def test_feed_in_limit_headroom_above_one_accepted(self): + cfg = PeakShavingConfig(feed_in_limit_headroom=1.25) + assert cfg.feed_in_limit_headroom == 1.25 + + def test_feed_in_limit_headroom_string_rejected(self): + with pytest.raises(ValueError, + match='peak_shaving.feed_in_limit_headroom'): + PeakShavingConfig(feed_in_limit_headroom='1.1') + + +class TestPeakShavingConfigDirectConstruction: + """Direct dataclass construction (no from_config) resolves switches + from `mode` via __post_init__ when the switches are left at their + None sentinel -- used by code/tests that still construct with `mode=`.""" + + def test_mode_price_resolves_switches(self): + cfg = PeakShavingConfig(mode='price') + assert cfg.time_active is False + assert cfg.price_active is True + + def test_mode_time_resolves_switches(self): + cfg = PeakShavingConfig(mode='time') + assert cfg.time_active is True + assert cfg.price_active is False + + def test_mode_combined_resolves_switches(self): + cfg = PeakShavingConfig(mode='combined') + assert cfg.time_active is True + assert cfg.price_active is True + + def test_explicit_switches_are_not_overridden_by_mode(self): + """When switches are passed explicitly they win over `mode`, + matching the from_config precedence rule.""" + cfg = PeakShavingConfig( + mode='time', time_active=False, price_active=True) + assert cfg.time_active is False + assert cfg.price_active is True From c110a4978831a464cb501a558c0231be68c64ff5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 05:23:02 +0000 Subject: [PATCH 10/13] fix: address Copilot review round 1 - Use %.0f instead of %d for feed_in_limit_w log placeholders (float-typed config value) in core.py and next.py. - docs/features/peak-shaving.md: correct the MQTT runtime-control section (price_limit and the deprecated mode setter exist), fix the price_limit default in the parameter table (None, not 0.05), and correct the skip table row for the discharge-not-allowed state (solar floor is not applied there). - config dummy yaml: same MQTT runtime-control correction. - solar-limit-evaluation.md: update the stale "not yet integrated" status header, the rule now ships in logic/solar_limit.py. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- config/batcontrol_config_dummy.yaml | 5 +++-- docs/development/solar-limit-evaluation.md | 4 +++- docs/features/peak-shaving.md | 10 ++++++---- src/batcontrol/core.py | 4 ++-- src/batcontrol/logic/next.py | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/config/batcontrol_config_dummy.yaml b/config/batcontrol_config_dummy.yaml index 95d5035a..acd1217f 100644 --- a/config/batcontrol_config_dummy.yaml +++ b/config/batcontrol_config_dummy.yaml @@ -61,8 +61,9 @@ battery_control_expert: # still accepted: time->time_active, price->price_active, combined->both. # See docs for full details. # -# Runtime control via MQTT: limited to 'enabled' and 'allow_full_battery_after'. -# All other parameters require a restart. +# Runtime control via MQTT: 'enabled', 'allow_full_battery_after', 'price_limit' +# and the deprecated 'mode' (mapped onto the switches). The rule switches and +# the feed_in_limit_* parameters require a restart. #-------------------------- peak_shaving: enabled: false diff --git a/docs/development/solar-limit-evaluation.md b/docs/development/solar-limit-evaluation.md index 17a2d4e7..47f9b6a0 100644 --- a/docs/development/solar-limit-evaluation.md +++ b/docs/development/solar-limit-evaluation.md @@ -1,6 +1,8 @@ # Evaluation: Solar feed-in limit (Solarspitzengesetz) in peak shaving -Status: **evaluation and simulation phase** — not yet integrated into `logic/next.py`. +Status: **implemented** — the rule described here ships as `src/batcontrol/logic/solar_limit.py` +plus `_apply_solar_limit()` in `src/batcontrol/logic/next.py`. This page documents the +evaluation that preceded the implementation; its numbers are reproduced by the test suite. Simulation script: [`scripts/simulate_solar_limit_day.py`](https://github.com/MaStr/batcontrol/blob/main/scripts/simulate_solar_limit_day.py), figures generated by `scripts/plot_solar_limit_day.py`. diff --git a/docs/features/peak-shaving.md b/docs/features/peak-shaving.md index 126ced53..89394e47 100644 --- a/docs/features/peak-shaving.md +++ b/docs/features/peak-shaving.md @@ -46,7 +46,7 @@ peak_shaving: | `price_active` | bool | `true` | Enable price rule (reserve capacity for cheap slots) | | `solar_cap_active` | bool | `false` | Enable solar feed-in limit rule (absorb PV above `feed_in_limit_w`) | | `allow_full_battery_after` | int | `14` | Target hour (0-23) for the time rule | -| `price_limit` | float | `0.05` | Price threshold in Euro/kWh. Set `-1` to disable the price rule. | +| `price_limit` | float | unset (`None`) | Price threshold in Euro/kWh. The price rule only acts when a value is configured (e.g. `0.05`). Set `-1` to disable the price component explicitly. | | `feed_in_limit_w` | int | `0` | Absolute feed-in power limit in watts (solar rule). Formula: `0.6 * kWp * 1000`. Set to `0` to disable. | | `feed_in_limit_headroom` | float | `1.0` | Safety factor (>= 1.0) on the forecast surplus (solar rule). Recommended: `1.1` if clipping is observed. | @@ -54,14 +54,16 @@ peak_shaving: ### MQTT Runtime Control -Only `enabled` and `allow_full_battery_after` can be changed at runtime via MQTT without restarting batcontrol: +The following parameters can be changed at runtime via MQTT without restarting batcontrol: | Topic | Accepts | Description | |-------|---------|-------------| | `{base}/peak_shaving/enabled/set` | `true` / `false` | Enable or disable peak shaving | | `{base}/peak_shaving/allow_full_battery_after/set` | int 0-23 | Change the target hour for the time rule | +| `{base}/peak_shaving/price_limit/set` | float | Change the price threshold for the price rule | +| `{base}/peak_shaving/mode/set` | `time` / `price` / `combined` | Deprecated: kept for backward compatibility, mapped onto `time_active`/`price_active` | -All other parameters (`time_active`, `price_active`, `solar_cap_active`, `price_limit`, `feed_in_limit_w`, `feed_in_limit_headroom`) require restarting batcontrol to take effect. +The rule switches themselves (`time_active`, `price_active`, `solar_cap_active`) and the solar parameters (`feed_in_limit_w`, `feed_in_limit_headroom`) have no MQTT setters and require restarting batcontrol to take effect. Runtime changes are temporary and are not written back to the configuration file. @@ -206,7 +208,7 @@ Peak shaving cap rules (time and price) are automatically bypassed in the follow | Past `allow_full_battery_after` hour | Bypassed | Still applies (if clipping predicted) | | Battery in `always_allow_discharge` region (high SOC) | Bypassed | Still applies (if clipping predicted) | | Force-charge from grid active (Mode -1) | Bypassed | Not applied | -| Discharge not allowed | Bypassed | Still applies (if clipping predicted) | +| Discharge not allowed (battery preserved) | Bypassed | Not applied (no charge cap is active in this state, the inverter charges all surplus anyway) | | evcc is actively charging the EV | Bypassed | Not applied | | EV connected in PV mode (evcc) | Bypassed | Not applied | | `price_limit` not configured | Price rule inactive | Not affected | diff --git a/src/batcontrol/core.py b/src/batcontrol/core.py index 63730bbd..23093ccb 100644 --- a/src/batcontrol/core.py +++ b/src/batcontrol/core.py @@ -254,8 +254,8 @@ def __init__(self, configdict: dict): if (self.peak_shaving_config.feed_in_limit_w > 0 and self.max_pv_charge_rate > 0): logger.warning( - 'peak_shaving.feed_in_limit_w (%d W) is configured together ' - 'with a static max_pv_charge_rate (%d W): if the solar_cap ' + 'peak_shaving.feed_in_limit_w (%.0f W) is configured together ' + 'with a static max_pv_charge_rate (%.0f W): if the solar_cap ' 'clip absorption needs a higher charge rate than this static ' 'cap allows, curtailment cannot be fully avoided. Consider ' 'raising max_pv_charge_rate or removing it.', diff --git a/src/batcontrol/logic/next.py b/src/batcontrol/logic/next.py index ca526ec9..263544f8 100644 --- a/src/batcontrol/logic/next.py +++ b/src/batcontrol/logic/next.py @@ -433,7 +433,7 @@ def _apply_solar_limit(self, settings: InverterControlSettings, settings.limit_battery_charge_rate = final_w logger.info('[SolarLimit] floor=%d W, cap=%s W, final PV limit=%s W ' - '(feed_in_limit=%d W)', + '(feed_in_limit=%.0f W)', floor_w, cap_w if cap_w >= 0 else 'off', final_w if final_w >= 0 else 'off', From e0b5ffac56ff12024e1b03a954155dc21c6a90fa Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 05:32:06 +0000 Subject: [PATCH 11/13] fix: address Copilot review round 2 - Gate the max_pv_charge_rate startup warning on solar_cap_active so configs with feed_in_limit_w set but the rule disabled do not get a misleading warning. - scripts: stop calling the simulation's algorithm copy a "candidate" for src/ -- the authoritative implementation now lives in logic/solar_limit.py; the script keeps a standalone reference copy. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- scripts/README.md | 7 ++++--- scripts/simulate_solar_limit_day.py | 8 +++++--- src/batcontrol/core.py | 3 ++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/README.md b/scripts/README.md index f3c8509e..1b17f432 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -31,8 +31,9 @@ python scripts/simulate_solar_limit_day.py forecast error with headroom sweep, midday consumption spike, 15-min interval - Compares baseline, legacy time-based peak shaving, and the new rule - Prints curtailed/feed-in energy, end SoC and clip-recovery percentage -- Contains the candidate algorithm (`compute_solar_limit`, `merge_limits`) - intended to move to `src/batcontrol/logic/solar_limit.py` +- Contains a standalone reference copy of the algorithm (`compute_solar_limit`, + `merge_limits`); the authoritative production implementation lives in + `src/batcontrol/logic/solar_limit.py` See `docs/development/solar-limit-evaluation.md` for results and design. @@ -40,7 +41,7 @@ See `docs/development/solar-limit-evaluation.md` for results and design. Generates the figures for `docs/development/solar-limit-evaluation.md` into `docs/assets/` (clipping concept, algorithm behaviour on the reference day, -headroom explainer). Imports profiles and the candidate algorithm from +headroom explainer). Imports profiles and the reference algorithm from `simulate_solar_limit_day.py`. **Usage:** diff --git a/scripts/simulate_solar_limit_day.py b/scripts/simulate_solar_limit_day.py index 294183b0..984d6004 100644 --- a/scripts/simulate_solar_limit_day.py +++ b/scripts/simulate_solar_limit_day.py @@ -24,8 +24,10 @@ -1 = no cap. The floor overrides every cap because a cap below the floor burns energy (curtailment); caps only optimize economics. -The candidate algorithm lives in the "proposed algorithm" section below and -is cut so it can move to src/batcontrol/logic/solar_limit.py unchanged. +The "reference algorithm" section below is the standalone copy this +evaluation was run with; the authoritative production implementation now +lives in src/batcontrol/logic/solar_limit.py (ported from here, with the +surplus-headroom and headroom-floor variants baked in). Usage: python scripts/simulate_solar_limit_day.py @@ -75,7 +77,7 @@ # --------------------------------------------------------------------------- -# Proposed algorithm (candidate for src/batcontrol/logic/solar_limit.py) +# Reference algorithm (production version: src/batcontrol/logic/solar_limit.py) # --------------------------------------------------------------------------- def compute_solar_limit(production_wh, consumption_wh, feed_in_limit_w, interval_h, free_capacity_wh, max_capacity_wh, diff --git a/src/batcontrol/core.py b/src/batcontrol/core.py index 23093ccb..1a05a2e2 100644 --- a/src/batcontrol/core.py +++ b/src/batcontrol/core.py @@ -251,7 +251,8 @@ def __init__(self, configdict: dict): self.time_at_forecast_error = -1 self.peak_shaving_config = PeakShavingConfig.from_config(config) - if (self.peak_shaving_config.feed_in_limit_w > 0 + if (self.peak_shaving_config.solar_cap_active + and self.peak_shaving_config.feed_in_limit_w > 0 and self.max_pv_charge_rate > 0): logger.warning( 'peak_shaving.feed_in_limit_w (%.0f W) is configured together ' From 482c53f737bebc0c95634ea928577a6ca3c0aca4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 06:09:36 +0000 Subject: [PATCH 12/13] docs: clarify from_config docstring on mode validation and log level Copilot round 3 flagged two docstring inaccuracies; the behavior itself is correct and stays unchanged: the mode-only deprecation notice is deliberately logged at debug level so unmigrated configs keep loading quietly, and an invalid mode value raises ValueError even when explicit switches are present (fail-fast on config typos). The docstring now says both explicitly. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- src/batcontrol/logic/logic_interface.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/batcontrol/logic/logic_interface.py b/src/batcontrol/logic/logic_interface.py index a4e366d5..de785ddc 100644 --- a/src/batcontrol/logic/logic_interface.py +++ b/src/batcontrol/logic/logic_interface.py @@ -119,12 +119,16 @@ def from_config(cls, config: dict) -> 'PeakShavingConfig': ``mode`` is deprecated in favour of the explicit switches ``time_active``/``price_active``/``solar_cap_active``. If any switch key is present in the config, the switches win; a ``mode`` key - present alongside them is ignored (warning logged). If only ``mode`` - is present, it is mapped onto the switches (``time`` -> - ``time_active=True, price_active=False``; ``price`` -> - ``price_active=True, time_active=False``; ``combined`` -> both - True) and a one-time deprecation warning is logged. If neither is - present, the defaults apply (equivalent to ``combined``). + present alongside them has no effect on the switches (warning + logged), but its value is still validated -- an invalid ``mode`` + raises ValueError so configuration typos fail fast instead of being + silently swallowed. If only ``mode`` is present, it is mapped onto + the switches (``time`` -> ``time_active=True, price_active=False``; + ``price`` -> ``price_active=True, time_active=False``; + ``combined`` -> both True) and a debug-level deprecation notice is + logged (deliberately below WARNING so unmigrated configs keep + loading quietly). If neither is present, the defaults apply + (equivalent to ``combined``). """ ps = config.get('peak_shaving', {}) price_limit_raw = ps.get('price_limit', None) From 8bfec07c4e187f2295ca989febe844ca2dcf1e54 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 06:13:18 +0000 Subject: [PATCH 13/13] feat: log the mode deprecation notice at warning level Raise the mode-only deprecation notice in PeakShavingConfig.from_config from debug to warning (maintainer decision): users still on the deprecated mode key should see the migration hint at config load. The warning fires once at startup; the per-cycle dataclasses.replace path does not go through from_config. The three fallback-warning tests now filter out the deprecation notice (they guard the price_limit fallback warning only), and a new test asserts the deprecation warning explicitly. Docs and dummy config mention the warning. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_011McoUchh4HNguDbWWmDqd4 --- config/batcontrol_config_dummy.yaml | 6 ++--- docs/features/peak-shaving.md | 2 +- src/batcontrol/logic/logic_interface.py | 14 +++++----- tests/batcontrol/test_peak_shaving_config.py | 28 +++++++++++++++----- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/config/batcontrol_config_dummy.yaml b/config/batcontrol_config_dummy.yaml index acd1217f..8e7ae865 100644 --- a/config/batcontrol_config_dummy.yaml +++ b/config/batcontrol_config_dummy.yaml @@ -57,9 +57,9 @@ battery_control_expert: # applies after allow_full_battery_after and at high SoC, so the battery may reach # 100% later than the target hour on clipping days. # -# Deprecated: 'mode' parameter (maps to switches at load time). Old syntax is -# still accepted: time->time_active, price->price_active, combined->both. -# See docs for full details. +# Deprecated: 'mode' parameter (maps to switches at load time, logs a +# deprecation warning). Old syntax is still accepted: time->time_active, +# price->price_active, combined->both. See docs for full details. # # Runtime control via MQTT: 'enabled', 'allow_full_battery_after', 'price_limit' # and the deprecated 'mode' (mapped onto the switches). The rule switches and diff --git a/docs/features/peak-shaving.md b/docs/features/peak-shaving.md index 89394e47..ccdee91c 100644 --- a/docs/features/peak-shaving.md +++ b/docs/features/peak-shaving.md @@ -50,7 +50,7 @@ peak_shaving: | `feed_in_limit_w` | int | `0` | Absolute feed-in power limit in watts (solar rule). Formula: `0.6 * kWp * 1000`. Set to `0` to disable. | | `feed_in_limit_headroom` | float | `1.0` | Safety factor (>= 1.0) on the forecast surplus (solar rule). Recommended: `1.1` if clipping is observed. | -**Deprecated:** The old `mode` parameter (`time` / `price` / `combined`) is still accepted for backward compatibility and mapped to the switches at startup. New configurations should use the switch-based design above. +**Deprecated:** The old `mode` parameter (`time` / `price` / `combined`) is still accepted for backward compatibility and mapped to the switches at startup; a deprecation warning is logged. New configurations should use the switch-based design above. ### MQTT Runtime Control diff --git a/src/batcontrol/logic/logic_interface.py b/src/batcontrol/logic/logic_interface.py index de785ddc..8941b8fe 100644 --- a/src/batcontrol/logic/logic_interface.py +++ b/src/batcontrol/logic/logic_interface.py @@ -125,9 +125,8 @@ def from_config(cls, config: dict) -> 'PeakShavingConfig': silently swallowed. If only ``mode`` is present, it is mapped onto the switches (``time`` -> ``time_active=True, price_active=False``; ``price`` -> ``price_active=True, time_active=False``; - ``combined`` -> both True) and a debug-level deprecation notice is - logged (deliberately below WARNING so unmigrated configs keep - loading quietly). If neither is present, the defaults apply + ``combined`` -> both True) and a one-time deprecation warning is + logged at config load. If neither is present, the defaults apply (equivalent to ``combined``). """ ps = config.get('peak_shaving', {}) @@ -161,11 +160,10 @@ def from_config(cls, config: dict) -> 'PeakShavingConfig': time_active = ps.get('time_active', True) price_active = ps.get('price_active', True) elif mode_present: - # Deprecation notice at debug level: the existing test suite - # (and users who have not yet migrated) expect a plain mode= - # config to load silently at WARNING level; the combined+missing - # price_limit fallback below still warns as before. - logger.debug( + # One-time deprecation warning at config load; the per-cycle + # dataclasses.replace path does not go through from_config, so + # this does not repeat on every evaluation. + logger.warning( "peak_shaving.mode is deprecated; use the explicit switches " "time_active/price_active/solar_cap_active instead. Mapping " "mode='%s' onto the switches for now.", mode diff --git a/tests/batcontrol/test_peak_shaving_config.py b/tests/batcontrol/test_peak_shaving_config.py index abafd162..a83fa742 100644 --- a/tests/batcontrol/test_peak_shaving_config.py +++ b/tests/batcontrol/test_peak_shaving_config.py @@ -160,14 +160,22 @@ def test_combined_without_price_limit_logs_warning(self, caplog): if r.levelname == 'WARNING'] assert any("combined" in m and "price_limit" in m for m in messages) + @staticmethod + def _non_deprecation_warnings(caplog): + # A config that still uses `mode` now always gets the one-time + # deprecation warning; these tests only guard the price_limit + # fallback warning, so the deprecation notice is filtered out. + return [r for r in caplog.records + if r.levelname == 'WARNING' + and 'deprecated' not in r.getMessage()] + def test_disabled_combined_without_price_limit_does_not_warn(self, caplog): # When peak shaving is disabled there is no user-visible problem. with caplog.at_level('WARNING', logger=self.LOGGER): PeakShavingConfig.from_config({ 'peak_shaving': {'enabled': False, 'mode': 'combined'}, }) - warnings = [r for r in caplog.records if r.levelname == 'WARNING'] - assert warnings == [] + assert self._non_deprecation_warnings(caplog) == [] def test_combined_with_price_limit_does_not_warn(self, caplog): with caplog.at_level('WARNING', logger=self.LOGGER): @@ -175,16 +183,14 @@ def test_combined_with_price_limit_does_not_warn(self, caplog): 'peak_shaving': { 'enabled': True, 'mode': 'combined', 'price_limit': 0.05}, }) - warnings = [r for r in caplog.records if r.levelname == 'WARNING'] - assert warnings == [] + assert self._non_deprecation_warnings(caplog) == [] def test_time_mode_without_price_limit_does_not_warn(self, caplog): with caplog.at_level('WARNING', logger=self.LOGGER): PeakShavingConfig.from_config({ 'peak_shaving': {'enabled': True, 'mode': 'time'}, }) - warnings = [r for r in caplog.records if r.levelname == 'WARNING'] - assert warnings == [] + assert self._non_deprecation_warnings(caplog) == [] def test_replace_does_not_re_emit_warning(self, caplog): # dataclasses.replace re-runs __post_init__ but must not trigger @@ -215,6 +221,16 @@ def test_mode_time_maps_to_time_only(self): assert cfg.time_active is True assert cfg.price_active is False + def test_mode_only_config_logs_deprecation_warning(self, caplog): + """A config still using `mode` gets a one-time deprecation warning.""" + with caplog.at_level('WARNING', logger=TestPeakShavingConfigFallbackWarning.LOGGER): + PeakShavingConfig.from_config({ + 'peak_shaving': {'mode': 'time'} + }) + messages = [r.getMessage() for r in caplog.records + if r.levelname == 'WARNING'] + assert any('mode' in m and 'deprecated' in m for m in messages) + def test_mode_price_maps_to_price_only(self): cfg = PeakShavingConfig.from_config({ 'peak_shaving': {'mode': 'price', 'price_limit': 0.05}