What's Changed
Planning performance
This release is mostly about speed. Three independent rounds of optimisation land here, and every one
of them is gated on the 20-scenario random benchmark producing byte-identical plans - same metric,
cost, both PV futures, final SoC, battery cycles and carbon. Nothing about how Predbat plans has
changed, only how long it takes.
| change | 20-scenario benchmark |
|---|---|
| Batch the prediction fan-out into one kernel call (#4540) | 39.53s → 24.82s (37% faster) |
| Remove redundant scans and copies (#4549) | 26.13s → 20.15s (23% faster) |
| Cache window bounds, drop deepcopy on window lists (#4536) | 20.27s → 16.64s (18% faster) |
Each step was measured separately against its own baseline, so the percentages don't simply multiply -
but end to end the same suite went from roughly 40 seconds to under 17 seconds. Heavy plans gain the
most, which is the point: the two slowest scenarios were 42% of the suite's runtime and both are around
25% faster on the last round alone.
CAUTION: threads in apps.yaml has changed meaning. It used to size a Python pool of worker
processes; it now sizes the C++ kernel's thread pool. The plan is identical at any setting, 0 and 1
both mean serial, and auto remains the default - so if you are running the shipped kernel binary,
there is nothing to do.
CAUTION: if you are not running the C++ kernel, planning is now single-threaded. The Python
multiprocessing pool has been removed rather than kept alongside the kernel, so whenever Predbat falls
back to the Python engine - no binary for your architecture, a binary rejected at load time, or
debug_enable set - the fan-out runs serially and threads has no effect at all. Predbat ships
binaries for x86_64, i686, aarch64, armv7l and both macOS architectures, which covers effectively every
normal install, and those are unaffected. If you are on an architecture with no binary, expect planning
to be slower than v8.48.3 on top of the Python engine's existing cost. Predbat logs
Warn: Prediction kernel ... - using Python engine when this happens, so check the log if planning
suddenly takes longer after upgrading.
Removing the pool also removes a failure mode of its own: its workers rebuilt state from a module global
populated only in the parent, so on any platform defaulting to spawn rather than fork (macOS since
Python 3.8) every worker started empty and every scenario died. That path is gone.
CAUTION: the C++ kernel ABI is bumped to 4 and the parity revision to 6. The binaries shipped in this
release are already rebuilt, but if you build the kernel yourself you must rebuild it. A stale
binary is now correctly rejected at load time and Predbat falls back to the Python engine - which, per
the note above, now also means serial planning, so a forgotten rebuild costs you twice. Previously an
ABI-3 binary would be accepted and then segfault on its first prediction, so the rejection is still very
much the better outcome.
- perf: batch the prediction fan-out into one kernel call (35% faster planning) by @springfall2008 in #4540
- perf: cut ~23% off planning by removing redundant scans and copies by @springfall2008 in #4549
- perf: cache window bounds and replace deepcopy on window lists (-21% planning) by @springfall2008 in #4536
New features
Up to 8 cars are now supported, for people running several EVs or Octopus Intelligent devices on one
account. Cars 4-7 get their own car_charging_rate_* and car_charging_manual_soc_* entities, matching
cars 0-3. This follows on from the v8.48.3 fix that stopped a 5th car crashing Predbat outright.
- feat(car): raise the maximum supported cars from 4 to 8 by @springfall2008 in #4545
Bug fixes
On multi-inverter systems the dashboard status was whichever inverter happened to be processed last,
which hid genuine disagreement between inverters. Real cross-charging - one inverter charging while
another discharges - is now surfaced explicitly instead of arbitrarily picking a side.
Solis users with high-voltage batteries were badly affected by a hard-coded 48V nominal voltage: every
derived power and capacity value was wrong by the ratio of the real pack voltage to 48V, around 11x on
a 533V pack (960W computed against ~10.7kW actual).
- fix(execute): resolve multi-inverter status from all inverters, not just the last one processed by @chalfontchubby in #4466
- fix(solis): derive nominal_voltage from live battery data instead of hard-coded 48V by @chalfontchubby in #4502
- fix(inverter): skip the discharge target write on AC Coupled GivTCP inverters by @chalfontchubby in #4543
- fix(sigenergy): map Sigenergy ESS max discharge limit to battery rate max by @mike-oakley in #4535
- fix(load-ml): stop a training run that crosses midnight inflating the forecast by @mbuhansen in #4537
- fix: add timeout to requests.get() in futurerate.py and octopus.py by @springfall2008 with @Copilot in #4539
Notes on two of those: the AC Coupled GivTCP fix completes the #4517/#4518 work for inverters where
Control.Discharge_Target_SOC_1 is absent entirely - GivTCP reported every write as successful but the
value never persisted, so Predbat rewrote it every cycle forever. The load ML fix addresses a fine-tune
that starts before midnight and finishes after it, where a stale load baseline was added on top of every
published forecast point - seen in the wild as a ~30 kWh spike against a 25 kWh actual day.
The requests.get() timeouts matter more than they look: a silent TCP stall on the Nord Pool fetch
raised no exception, so the existing handler never fired and the main prediction loop hung until manual
restart, leaving the inverter latched in its last commanded state.
Testing and documentation
Three feature flags were pinned off across all 20 benchmark scenarios, so anything behind them could be
broken without the gate noticing. set_export_low_power and iboost_enable are now varied across the
suite - and this was not theoretical: the iboost arm of export_window_allowed added in #4549 could not
be reached by any scenario in the repo, and is now exercised 126 times.
- test: vary iboost and low power export across the random scenarios by @springfall2008 in #4550
- docs: report on the planning performance work by @springfall2008 in #4547
New Contributors
- @mike-oakley made their first contribution in #4535
Full Changelog: v8.48.3...v8.48.4