Commit Graph
3 Commits
Author SHA1 Message Date
jensandClaude Sonnet 5 4d59e11c1e sud: fail fast on malformed sud.json documents, one test per fixture
Sud.load() now calls utils/sud_validate.py's validate_sud() before
parsing: requires a non-empty 'steps' list, and for every resolved
step that sets its own 'temperature', a numeric ramp.rate - the one
genuinely unguarded crash site (ramp['rate'], no .get() fallback) hit
in tasks/sud.py, components/sud_forecast.py and demo_sud.py the moment
that step is reached, which for SudForecastEstimator can be as early
as the Load itself. A refused load is now logged with the specific
reason (previously silent). validate_sud() reuses Sud._parse_data()
directly rather than reimplementing default-merging/step-building.

tests/utils/test_sud_validate_fixtures.py covers 8 fixture docs plus
all 6 real sude/*.json files; tests/components/sud/test_sud_load.py
exercises the Sud.load() wiring itself (rejects, keeps prior schedule).

Also reworked both this and test_config_fixtures.py to generate one
dedicated test method per fixture file (dynamic setattr) instead of
looping with subTest inside a single method - subTest iterations don't
show up individually in `unittest -v`/IDE test runners, which made it
look like far fewer fixtures were covered than actually are.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4oEE99rkKVU8L8tkzXWdG
2026-07-11 13:27:20 +02:00
jensandClaude Sonnet 5 2d032bf9b2 fix: allow gentle negative HOLD floor to fix steady-state overshoot
Testing the windup-fix rework live against sude/sud_0030.json surfaced a
distinct bug: a HOLD overshoot from grain-fill-in cooling never decayed -
process_pid()'s pid_outer_y floor clamped to exactly 0.0, so pid_inner
fought the pot's own ambient loss to hold the overshot temperature flat
instead of declining back to setpoint (see docs/overshoot2.png).

Replace the hardcoded 0.0 floor with a configurable Outer.y_hold_min
(default 0.0, backward compatible), set to -0.1 in config.json, both
.tpl templates, and the demo scripts - small enough to avoid
reintroducing the bb5af3c limit cycle while letting HOLD request a
gentle decline matching passive ambient cooling.

Adds TestHoldOvershootRecoversToSetpoint and documents the finding in
docs/overshoot_hold_windup.md's Follow-up section and
components/pid/TODO.md. Confirmed against a live sud_0030 re-run, not
just the unit test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M2ierBoxW3v7nUbDw3M2pE
2026-07-06 08:19:10 +02:00
jensandClaude Sonnet 5 f69d63c31b fix: split inner-loop yi_max clamp to fix HOLD-state windup overshoot
Renames pid_hold/pid_heat/pid_cool to pid_outer/pid_inner/pid_inner_cool
to match what actually runs when, and splits inner-loop config into
Inner.Heat/Inner.Hold/Inner.Cool so the same PID instance gets a tight
yi_max ceiling only while HOLD drives it, without capping legitimate
1.5 K/min ramps. Fixes the overshoot from docs/overshoot_hold_windup.md
where a cold-water disturbance during HOLD wound up pid_heat's integral
term with no anti-windup engagement, taking ~35s+ to unwind naturally.

Breaking config change: Hold/Heat/Cool -> Outer/Inner.{Heat,Hold,Cool}
in config.json, both .tpl templates, the pid/sud demo scripts, and
replay_sim.py's CLI flags. Adds tests/components/pid/ (stdlib unittest)
covering the Pid clamp/recovery behavior and closed-loop disturbance,
ramp, and HOLD<->HEAT transition cases.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGQhVQ2Y3yXAQTXhrxVd5u
2026-07-05 21:23:55 +02:00