Skip to content

fix(car-charging): hold battery discharge during car charging regardless of car_energy_reported_load (#4246)#4247

Closed
chalfontchubby wants to merge 4 commits into
springfall2008:mainfrom
chalfontchubby:fix/car-charging-discharge-hold
Closed

fix(car-charging): hold battery discharge during car charging regardless of car_energy_reported_load (#4246)#4247
chalfontchubby wants to merge 4 commits into
springfall2008:mainfrom
chalfontchubby:fix/car-charging-discharge-hold

Conversation

@chalfontchubby

@chalfontchubby chalfontchubby commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

🤖 Drafted with Claude Code assistance.

Fixes #4246. Contains two commits: a regression test that reproduces the bug against main, followed by a fix.

The fix

Removes the and self.car_energy_reported_load condition from the discharge-hold gate in execute.py and prediction.py (with the matching change mirrored in prediction_kernel.cpp to keep C++/Python parity), so car_charging_from_battery = Off always holds discharge during car-charging windows, regardless of CT-clamp visibility.

This is "option 1" from the fix options discussed in #4246 - the simplest of a few possible approaches, chosen because:

  • car_energy_reported_load only ever gated CT-clamp load-history/export-accounting logic elsewhere in the codebase (confirmed by auditing every remaining use) - conditioning the discharge-hold decision on it as well looks like a category error rather than intentional design, since that switch says nothing about whether the battery can physically reach the car.
  • Charging and discharge-holding are controlled by independent code paths (adjust_charge_window/charge-rate vs. adjust_pause_mode/discharge-rate), so this doesn't risk interfering with a legitimate "charge now, rate is cheap" decision - verified both by code inspection and the included regression test.

It is not the only way to close this gap - #4246 also discusses:

  • Option 2: an opt-in switch, defaulting Off, for shared-busbar topologies specifically - preserves current behaviour for any (largely theoretical) genuinely-isolated-circuit population, at the cost of more config surface.
  • Option 3: a proper cost comparison (round-trip battery cost vs. import rate) instead of an unconditional hold - the more "correct" long-term answer, but meaningfully larger in scope (also touches the compiled kernel's cost model, not just this one gate) and out of scope for this PR.

Went with option 1 here as the smallest, most surgical change that fixes the reported behaviour; happy to rework this PR toward option 2 or 3 instead if a maintainer prefers a different tradeoff.

Verification

  • New/updated regression test (no_discharge_car_demand1b in test_execute.py) passes against the fix, fails against main.
  • Full local suite (unit_test.py, no --quick) passes, including kernel_parity and model_kernel (C++/Python engines stay in sync).
  • run_pre_commit passes cleanly (ruff, black, cspell, etc.).

…rted_load is Off

Adds a regression case mirroring no_discharge_car_demand1 but with
car_energy_reported_load=False, asserting discharge should still be
held (status "Hold for car", pause_discharge=True). Currently fails,
reproducing the reported behaviour where the discharge-hold protection
is silently skipped for CT-clamp-blind-spot topologies where the
battery and car still share a busbar. See issue springfall2008#4246.
…ess of car_energy_reported_load

car_energy_reported_load only controls whether car load is visible to the
CT clamp for house-load/export accounting purposes - it says nothing about
whether the battery can physically reach the car. Previously the discharge-hold
gate for car_charging_from_battery=Off was also conditioned on
car_energy_reported_load being On, so CT-clamp-blind-spot topologies (e.g. a
Henley block tapped ahead of the clamp) where the battery and car still share
a busbar got no discharge protection at all during car charging.

Removes that condition in execute.py and prediction.py (with the matching
change mirrored in prediction_kernel.cpp to keep C++/Python parity), so
discharge is held whenever car_charging_from_battery is Off, independent of
CT-clamp visibility. The charge decision itself is unaffected - charge
window/rate and discharge-hold are controlled by separate code paths, so a
window that's genuinely charging from grid still charges as planned.

Updates no_discharge_car_demand1b to assert the corrected behaviour and folds
in the reproduction case added previously. See issue springfall2008#4246.
@chalfontchubby chalfontchubby changed the title test(car-charging): reproduce discharge-hold gap when car_energy_reported_load is Off (#4246) fix(car-charging): hold battery discharge during car charging regardless of car_energy_reported_load (#4246) Jul 16, 2026
@chalfontchubby
chalfontchubby marked this pull request as ready for review July 16, 2026 08:56
@springfall2008
springfall2008 requested a review from Copilot July 17, 2026 08:21

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.

Pull request overview

Fixes a car-charging regression where battery discharge-hold was incorrectly gated by car_energy_reported_load, causing the battery to discharge during car-charging windows when EV load is outside the CT clamp. This aligns runtime execution (execute.py) and both prediction engines (Python + C++ kernel) so car_charging_from_battery = Off consistently prevents discharge during car-charging windows.

Changes:

  • Update execute_plan() discharge-hold gating to apply regardless of car_energy_reported_load.
  • Update Python prediction model and C++ prediction kernel to mirror the same discharge-hold behavior.
  • Adjust/extend regression coverage in test_execute.py for the car_energy_reported_load=False case.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
apps/predbat/tests/test_execute.py Updates regression expectations to assert discharge-hold during car charging even when EV load is outside CT clamp.
apps/predbat/prediction.py Ensures the Python prediction model holds discharge during car-charging windows regardless of CT-clamp inclusion.
apps/predbat/prediction_kernel.cpp Mirrors the Python prediction change in the compiled kernel for parity.
apps/predbat/execute.py Removes car_energy_reported_load from the discharge-hold gate so runtime behavior matches intent and tests.

Comment thread apps/predbat/execute.py Outdated
Comment thread apps/predbat/prediction.py
Comment thread apps/predbat/prediction_kernel.cpp
Comment thread apps/predbat/tests/test_execute.py Outdated
chalfontchubby and others added 2 commits July 17, 2026 09:28
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot review flagged that "the battery shares the same busbar either
way" is a claim Predbat can't actually verify from car_energy_reported_load
alone - that switch only reflects whether EV energy is counted in the
CT-clamp house-load model. Reworded the remaining comments (execute.py,
prediction.py, prediction_kernel.cpp) to match the wording already applied
to the test_execute.py comment, stating only what the setting controls
rather than asserting a specific wiring topology.
@springfall2008

Copy link
Copy Markdown
Owner

Thanks for this, @chalfontchubby — the fix itself is good. The red CI here isn't the change; it's that the pre-built kernel binaries (prediction_kernel_lib_*.so) need regenerating after the prediction_kernel.cpp edit, and the kernel-binaries auto-commit job can't run on fork PRs (it needs contents: write on the head branch), so the checked-in binary stays stale and fails the parity verify step.

I've brought your commits into a branch in the main repo — fix/car-charging-discharge-hold — so the kernel binaries can be rebuilt and committed automatically, and I'll merge from there. Your original commits (and authorship) are preserved on that branch.

Closing this in favour of that branch — full credit to you for the fix. Superseded by the work carried over from this PR (#4247).

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.

Battery discharges during car charging when car_energy_reported_load: Off, despite predbat.status showing 'Charging' (shared-busbar topology)

3 participants