Folds three previously-separate pieces into one preliminary-example repo for the HAHACS thesis: - thesis/ (submodule) → gitea Thesis.git — the PhD proposal - fret-pipeline/ — FRET requirements to AIGER controller (was ~/Documents/fret_processing/; prior single-commit history abandoned per user decision) - plant-model/ — 10-state PKE + lumped T/H PWR model (was ~/Documents/PKE_Playground/; never version-controlled before) - presentations/2026DICE/ (submodule) → gitea 2026DICE.git - reachability/, hardware/ — empty placeholders for Thrust 3 and HIL - docs/architecture.md — how the discrete and continuous layers compose - claude_memory/ — session notes and scratch knowledge pattern Plant model refactored to thesis naming (x, plant, u, ref); pke_th_rhs now takes u as an explicit arg instead of reading rho_ext from the params struct. First two controllers built to the contract u = ctrl_<mode>(t, x, plant, ref): ctrl_null (baseline) and ctrl_operation (stabilizing, proportional on T_avg). Validated under a 100% -> 80% Q_sg step: ctrl_operation reduces steady-state T_avg drift ~47% vs. the unforced plant. Root CLAUDE.md emphasizes that CLAUDE.md files are living documents and that any knowledge not captured before a session ends is lost forever; claude_memory/ holds the session-level notes that haven't stabilized enough to graduate into a CLAUDE.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
113 lines
4.9 KiB
Markdown
113 lines
4.9 KiB
Markdown
# 2026-04-16 — controllers scaffold and ctrl_operation rough-out
|
||
|
||
First continuous controller. Established the controller contract, refactored
|
||
the plant to thesis naming, and got a closed-loop sim running end-to-end
|
||
with a meaningful comparison against the unforced baseline.
|
||
|
||
## Naming convention (locked in)
|
||
|
||
Thesis-aligned. Applied to plant and controllers:
|
||
|
||
- `t` — time
|
||
- `x` — continuous state vector `[n; C1..C6; T_f; T_c; T_cold]`
|
||
- `plant` — params struct (was `p`; renamed to avoid collision with `p_i`
|
||
predicates from the thesis)
|
||
- `u` — control input: external rod reactivity (scalar, `dk/k`)
|
||
- `ref` — reference/setpoint struct (e.g. `ref.T_avg`)
|
||
- `Q_sg` — bounded disturbance (SG heat removal, `W`), a function handle
|
||
of time
|
||
|
||
Captured in `plant-model/CLAUDE.md` under "Naming convention" — any future
|
||
file in `plant-model/` or `controllers/` uses these names.
|
||
|
||
## Controller contract
|
||
|
||
```matlab
|
||
u = ctrl_<mode>(t, x, plant, ref)
|
||
```
|
||
|
||
Pure function. Returns scalar `u`. All four args required even if unused
|
||
(so `pke_solver` can swap controllers by function handle without branching).
|
||
|
||
## Plant refactor
|
||
|
||
Four files touched. Non-cosmetic changes:
|
||
|
||
- `pke_th_rhs.m` now takes `u` as an **explicit argument** instead of
|
||
reading `p.rho_ext(t)` from the params struct. This decouples plant from
|
||
controller and matches the thesis's `f(x, u)` form. Old `rho_ext` field
|
||
on the params struct is gone.
|
||
- `pke_solver.m` signature changed:
|
||
`[t, X, U] = pke_solver(plant, Q_sg, ctrl_fn, ref, tspan)`.
|
||
Returns `U` (the control trajectory), reconstructed post-hoc by
|
||
re-evaluating `ctrl_fn` at each returned time step.
|
||
- `plot_pke_results.m` takes `(t, X, U, plant, Q_sg, title)` — reactivity
|
||
panel now shows `u`, feedback, and total separately rather than relying on
|
||
a `p.rho_ext` handle.
|
||
- `pke_initial_conditions.m` trivial rename `p → plant`, output `x0`.
|
||
|
||
## ctrl_operation
|
||
|
||
Proportional-only for now. `Kp = 1e-4 /K`, chosen to roughly double the
|
||
effective moderator coefficient (`alpha_c ≈ -1e-4 /K`). Not tuned for
|
||
precision — this is rough-out. PI upgrade would require augmenting state
|
||
with integral error; deferred until we actually need tighter tracking.
|
||
|
||
**Validation run** (100% → 80% Q_sg step at t=30s, 600s horizon):
|
||
|
||
| Metric | ctrl_null | ctrl_operation |
|
||
|---|---|---|
|
||
| Final T_avg drift from setpoint | +1.5 °F | +0.8 °F |
|
||
| Final `u` | 0 $ | -0.0068 $ |
|
||
| Final power | 800 MW ✓ | 800 MW ✓ |
|
||
|
||
Sign polarity: T_avg > ref → e < 0 → u < 0 (rods in). Correct for a PWR.
|
||
Controller reduces steady-state offset by ~47%. Expected result — gain is
|
||
modest; the point is the plumbing works, not that the tuning is final.
|
||
|
||
## What's deferred (on purpose)
|
||
|
||
- **Integral action in ctrl_operation.** P-only for now. If we want to hit
|
||
`ref.T_avg` exactly (zero steady-state error), augment state with
|
||
`int_e` and upgrade to PI.
|
||
- **Gain config.** Gains hardcoded in the controller file. Fine for one
|
||
mode; once we tune 3–4 modes we should lift to a `ctrl_params.m` or
|
||
similar.
|
||
- **Mode dispatcher.** No `ctrl_dispatch.m` yet because there's only one
|
||
real controller. Will introduce when we add the second mode and need to
|
||
switch based on discrete state.
|
||
- **ctrl_heatup, ctrl_scram, ctrl_shutdown.** Next. Each is a separate
|
||
design problem with its own verification method (reachability, parametric
|
||
reach, trivial).
|
||
- **Invariants and `X_safe` regions.** User explicitly said: rough out
|
||
controllers first, then come back to invariants. Per the thesis, we're
|
||
not trying to verify the whole hybrid system at once — one mode's reach
|
||
set at a time.
|
||
|
||
## Things worth remembering
|
||
|
||
- `addpath('controllers')` is required in `main.m` — MATLAB won't find
|
||
`@ctrl_operation` otherwise. Putting controllers in a subfolder is
|
||
ergonomically clean but the path has to be set.
|
||
- `U` is reconstructed post-hoc in `pke_solver`. If a future controller
|
||
has hysteresis or switching behavior, this reconstruction may not match
|
||
the ODE's internal trajectory (the solver might have evaluated `ctrl_fn`
|
||
at interior points that don't appear in the returned `t`). For smooth
|
||
controllers (P, PI) this is fine; for event-driven / hybrid controllers
|
||
we'll need to log `u` during the solve instead.
|
||
- MATLAB `-batch` mode runs `main.m` cleanly without a display — figures
|
||
are created off-screen but the script completes. Good for CI or sanity
|
||
checks without opening the IDE.
|
||
- `T_avg` is literally `T_c` in the state vector (`x(9)`). Don't let the
|
||
two names confuse future debugging — `T_avg` is the nuclear-industry term,
|
||
`T_c` is the code variable, they're the same quantity.
|
||
|
||
## Paths touched
|
||
|
||
- Modified: `plant-model/pke_th_rhs.m`, `pke_solver.m`,
|
||
`pke_initial_conditions.m`, `plot_pke_results.m`, `main.m`, `README.md`,
|
||
`CLAUDE.md`
|
||
- Created: `plant-model/controllers/ctrl_null.m`,
|
||
`plant-model/controllers/ctrl_operation.m`
|
||
- Nothing outside `plant-model/` was touched this session.
|