Skip to content

Compare: scale the export rate with the hardware overrides - #4897

Open
springfall2008 wants to merge 3 commits into
mainfrom
fix/compare-export-rate-override-4895
Open

Compare: scale the export rate with the hardware overrides#4897
springfall2008 wants to merge 3 commits into
mainfrom
fix/compare-export-rate-override-4895

Conversation

@springfall2008

Copy link
Copy Markdown
Owner

This is an automated draft PR generated from issue #4895 — a maintainer should review it before merging.

Fixes #4895

Summary

Compare's apply_hardware_overrides() never touched battery_rate_max_export, but the prediction engine uses that variable — not battery_rate_max_discharge — once an export window is active (prediction.py:906). A tariff modelling a 30 kW inverter therefore discharged at 30 kW everywhere except the force-export slots, which stayed pinned at the real hardware's export rate, materially understating export earnings for upgrade scenarios.

  • override_battery_rate_max_discharge_kw now carries the export rate with it, so the common "model a bigger inverter" case just works.
  • Added an explicit override_battery_rate_max_export_kw key for export-limited hardware, which takes precedence. A non-numeric value there falls back to the discharge override rather than silently reinstating the real hardware rate.
  • run_all() now snapshots and restores battery_rate_max_export per tariff, alongside the other four hardware attributes, so an override can't bleed into the next tariff.
  • Documented the new key in docs/compare.md, including a note that the grid export_limit is deliberately not overridden — the property's grid connection doesn't change when you swap the inverter, so a grid-capped user won't see more export from raising these keys.

inverter_limit_export itself is left alone: it is only an input to the derived battery_rate_max_export, which is now set directly.

Testing

  • cd coverage && ./run_pre_commit — all hooks Passed (file contents sorter, trailing whitespace, end of files, check yaml/json/json5, cspell dictionary sorter, triage daemon unit tests, ruff, black, markdownlint-fix, cspell), and the bundled quick suite reported All tests passed (4 slow tests skipped, total time: 105.02s).
  • tools/triage_test.sh compare — exit 0, T1–T19 all pass. Five new cases cover the fix: export follows the discharge override (T15), explicit export override wins (T16), export override alone leaves discharge untouched (T17), a bad export value falls back to the discharge override (T18), and run_all() genuinely snapshots/restores the attribute (T19). The existing T6/T7/T11 cases were extended to include the new attribute, and the test stub now starts with an export rate below its discharge rate, as real hardware often does — without the fix T15 fails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@springfall2008 springfall2008 self-assigned this Sep 1, 2026
@springfall2008 springfall2008 added the BOT_REVIEW Trigger an autotriage label Sep 1, 2026

@springfall2008 springfall2008 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review — 10 finder angles, all findings re-verified against the branch

Overall the approach is sound: the export-follows-discharge coupling mirrors both the real hardware derivation (inverter.py:431) and the existing annual tool (annual.py:733), the save/restore coverage is complete (snapshot + two restores, soc_kw clamp included), and the docs correctly leave the grid export_limit alone.

The one real code bug is inline at compare.py:229: a numeric-string override (e.g. a quoted "7.5") passes float() but the {:.2f} log on the raw value raises, so the value is applied while the flag stays False — which silently skips the #4895 coupling for string inputs and publishes a false "not numeric" warning. Parse once, format the float; that fixes both branches.

Findings not anchorable to diff lines:

  • annual.py:685 documents apply_hardware() as "the sole owner of battery_rate_max_export". This PR makes Compare a second writer of the same attribute, and annual.py:733 implements the identical export-follows-discharge rule. Worth updating that docstring (and/or cross-linking the two sites) in this PR so the ownership contract and the coupling rule don't drift apart.
  • plan.py:200 and plan.py:3055 size/threshold export windows from battery_rate_max_discharge, while prediction.py:906 simulates export windows at battery_rate_max_export. That seam is pre-existing (real hardware can have export ≠ discharge), but with an explicit export-only override the optimiser and the simulation now deliberately diverge — worth a sentence in the PR description.
  • Freeze-only export windows (set_export_freeze_only or export_limit_now >= EXPORT_LIMIT_FREEZE, prediction.py:900) never use battery_rate_max_export, so the fix reaches force-export slots only. One doc sentence would set expectations for freeze-heavy users.

Raised by finders and refuted (don't act on these): a claimed NameError on inspect in T19 is wrong — import inspect exists as a pre-existing function-local import in the T12 block (test_compare.py:369) which executes before T19. Also verified clean: line lengths within the 256/250 limits; the override reaches both the Python and C++ prediction engines (prediction.py:140, prediction_kernel.py:671); fetch_config_options does not clobber the override; the discharge power curve cannot cap the override (it uses the passed rate as the cap); no stale sensor publish occurs (compare's publish_data emits only compare entities, and the main loop re-derives rates each tick); no DC discharge attribute exists, so the charge-override's DC scaling asymmetry is correct.

Comment thread apps/predbat/compare.py Outdated
Comment thread apps/predbat/compare.py
Comment thread apps/predbat/compare.py Outdated
Comment thread apps/predbat/compare.py Outdated
Comment thread apps/predbat/tests/test_compare.py
Comment thread apps/predbat/tests/test_compare.py
Comment thread docs/compare.md Outdated
@springfall2008 springfall2008 removed the BOT_REVIEW Trigger an autotriage label Sep 1, 2026
@springfall2008 springfall2008 added the BOT_CLEANUP Trigger: bot should address PR review feedback and CI failures, then commit and push label Sep 2, 2026
@springfall2008 springfall2008 removed the BOT_CLEANUP Trigger: bot should address PR review feedback and CI failures, then commit and push label Sep 2, 2026
@springfall2008
springfall2008 marked this pull request as ready for review September 3, 2026 17:22
Copilot AI lite review requested due to automatic review settings September 3, 2026 17:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The changes directly address the reported bug, add explicit override semantics, and include targeted regression tests and documentation updates.

Pull request overview

This PR fixes Compare’s hardware override handling so export windows scale correctly when modelling different inverter/battery hardware, aligning Compare’s behaviour with how the prediction engine uses battery_rate_max_export during force-export slots.

Changes:

  • Extend apply_hardware_overrides() to keep battery_rate_max_export in sync with override_battery_rate_max_discharge_kw by default, and add a dedicated override_battery_rate_max_export_kw that takes precedence when provided.
  • Harden override parsing by rejecting non-finite values (nan/inf) and ensuring bad export overrides fall back to the discharge override when available.
  • Ensure run_all() snapshots/restores battery_rate_max_export per-tariff (and restores hardware/config in finally blocks) to prevent override bleed between tariffs; add/extend unit tests and update docs.
File summaries
File Description
docs/compare.md Documents the new export-rate override key and clarifies how export-rate modelling interacts with grid export limits and inverter limits.
apps/predbat/tests/test_compare.py Adds regression tests covering export-rate override semantics and verifies run_all() snapshots/restores battery_rate_max_export.
apps/predbat/compare.py Implements export-rate override behaviour, rejects non-finite override inputs, and snapshots/restores battery_rate_max_export to prevent bleed.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compare: hardware overrides don't scale export rate — export windows stay capped at real hardware battery_rate_max_export

2 participants