Skip to content

fix: settle controller judges a step-up on the same reading as a step-down - #41

Merged
zebraengine merged 1 commit into
mainfrom
fix/settle-step-up-live-reading
Oct 16, 2026
Merged

zebraengine merged 1 commit into
mainfrom
fix/settle-step-up-live-reading

Conversation

@zebraengine

Copy link
Copy Markdown
Owner

Problem

Found while writing the closed-loop fixture in #40. With the ambient prior well under the cable's real temperature, the settle controller (experimental, not wired in) steps up and takes the amp back on the next tick. In the fixture, with the prior 8 C low (25 C against an effective 33 C): 48 -> 42 A at minute 12.5, then 42 -> 43 A at minute 24 and 43 -> 42 A at minute 24.5.

The two directions were judged on different numbers:

  • Step-up used the fused estimate, which the cool prior pulls down (30.5 C).
  • Step-down used the higher of the fused estimate and the live stretch's own reading (33 C) once the stretch is half a time constant old.

A step-up needs a stretch a full time constant old, so the live reading always qualifies for the down rule by then. A 1 A step is too small to start a new stretch, so the down rule sees the same reading on the next tick.

The whole-history backtest shows it was not a fixture artifact: 23 such flaps undone within a minute on the fitted plant, 61 reversals in all.

Code touched

  • contrib/settle_amp_control.py: i_up is worked from theta_down (higher of fused and live) instead of the fused theta. With k_up >= k_down, I* up can never exceed I* down. Comment and module docstring updated.
  • tests/test_settle_amp_control.py: test_a_cool_prior_does_not_buy_an_amp_the_live_reading_takes_back, the 25 C prior scenario on the closed-loop fixture: exactly one move, to 42 A, and no reversal back-off.
  • Left alone: the step-down rule, front-load, emergency cut, reversal widening, derate_amp_control.py, the backtest.

Risk

  • None to a running install: nothing imports the settle controller.
  • Step-ups nearly vanish: 32 -> 0 on the fitted plant, 40-50 -> 2-3 on the hotter plants. Amp-hours did not drop, so on this history nearly every step-up was later undone. But the backtest's plant holds ambient constant through a session, so it cannot reward a step-up that a cooling evening would justify. The controller still steps up when the live reading supports it (test_step_up_waits_for_a_mature_stretch_and_a_rested_controller passes unchanged); whether it does so often enough is a question for live sessions.

Verification

  • uv run pytest: 180 passed. The new test fails without the fix (1 failed, 9 passed in the file).
  • Closed-loop backtest, --stress, on a production snapshot taken 2026-10-14 (113 sessions; 93 with a usable plant, 16 short and 4 poor fits skipped). Settle at a 1.5 C soak margin, deployed controller for scale:
Fitted plant settle before settle after deployed
Trips 0 0 0
Worst peak 63.7 C 63.7 C 64.5 C
Amp changes 315 250 453
Reversals 61 0 194
Flaps undone within a minute 23 0 8
Sessions with no reversal 66 93 30
Delivered 3981.2 Ah 3982.1 Ah 3964.6 Ah
  • Hotter plants (ambient and cable soak each +1 and +2 C): no trips before or after on any; reversals 88-97 -> 3-5; amp-hours within 1.3 Ah of before.
  • No session got worse in changes or reversals. 09-06, the one session where settle was worse than deployed, goes from 12 changes and 6 reversals to 6 changes and none.

Deploy: nothing to deploy.

🤖 Generated with Claude Code

…-down

With the ambient prior well under the cable's real temperature, the settle
controller stepped up on the fused estimate (which the cool prior pulls
down) and the step-down rule, on the live stretch's own reading, took the
amp back on the next tick: a 1 A step does not start a new stretch. In a
closed-loop fixture with the prior 8 C low: 48 -> 42 -> 43 -> 42 A, the last
two 30 s apart.

I* up is now worked from the same higher-of-fused-and-live reading as I*
down. With k_up >= k_down it can never exceed I* down, so a step-up is
never to a current the down rule would refuse.

Whole-history closed-loop backtest (production snapshot 2026-10-14, 93
sessions with a usable plant), settle at a 1.5 C soak margin, before /
after, fitted plant: trips 0 / 0, worst peak 63.7 / 63.7 C, amp changes
315 / 250, reversals 61 / 0, flaps undone within a minute 23 / 0, 3981.2 /
3982.1 Ah. On the four hotter plants: no trips either way, reversals 88-97
-> 3-5. No session got worse; 09-06 goes from 12 changes and 6 reversals
to 6 and none.

Step-ups nearly vanish (32 -> 0 on the fitted plant, 40-50 -> 2-3 on the
hotter ones) with no loss of amp-hours: on this history nearly every
step-up was later undone. The plant holds ambient constant through a
session, so the backtest cannot reward a step-up that a cooling evening
would justify; that needs live sessions.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@zebraengine
zebraengine merged commit 4bb3e94 into main Oct 16, 2026
1 check passed
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.

1 participant