PWR-HYBRID-3/plant-model/controllers/ctrl_operation_lqr.m
Dane Sabo d2997c2861 plant-model: add shutdown/heatup/scram controllers and LQR, linearize
Fill out the DRC mode set with ctrl_shutdown (u = -5*beta), ctrl_scram
(u = -8*beta), and ctrl_heatup (feedback-linearizing P on ramped T_avg
reference, saturated u, no integrator). Add ctrl_operation_lqr as a
full-state-feedback counterpart to ctrl_operation — K cached, closed-loop
essentially perfect under the 100%->80% Q_sg step where plain P has ~5F
overshoot.

Add pke_linearize for numerical (A, B, B_w) Jacobians at any operating
point; test_linearize confirms ~4e-4 rel err vs nonlinear sim for a
5% Q_sg step. Extend pke_solver with an optional x0 argument so each
mode can start from a plausible IC.

main_mode_sweep.m exercises all five modes back-to-back and saves the
4-panel plots. CLAUDE.md updated with model-validity-range note (trust
region is ~+/-50C around operating point; true cold shutdown is out of
scope for the linear feedback coefficients).

Hacker-Split: build out control layer end-to-end for reachability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:52:03 -04:00

54 lines
1.9 KiB
Matlab

function u = ctrl_operation_lqr(t, x, plant, ref)
% CTRL_OPERATION_LQR Full-state feedback for operation mode.
%
% u = -K * (x - x_op), K from LQR on (A, B) at x_op.
%
% Advantages over ctrl_operation (plain P on T_avg):
% - Damps the fast neutronics and the slow thermal response jointly.
% - Adds NO state (K is 1x10, feedback is memoryless).
% - Closed-loop A_cl = A - B*K is trivially propagable for reachability.
%
% Q/R are persistently cached on the first call. If the caller mutates
% plant after the first invocation, gains become stale — clear `persistent`
% by restarting MATLAB. That's a reach-analysis bookkeeping issue, not a
% runtime one.
%
% Weighting rationale:
% Q diagonal:
% n -> 1 (power tracking matters)
% C1..C6 -> 1e-3 (precursor deviations are informational)
% T_f -> 1e-2 (care about it but not as much as coolant)
% T_c -> 1e2 (primary objective)
% T_cold -> 1 (secondary but consequential)
% R = 1e6 (discourage large rod movements — one Kp of |u| ~ 1e-3)
%
% Inputs:
% t, x, plant, ref - standard signature. ref is unused; the setpoint
% is baked in as x_op. If you want to track a
% different operating point, regenerate K there.
persistent K x_op_cached
if isempty(K)
x_op_cached = pke_initial_conditions(plant);
[A, B, ~] = pke_linearize(plant, x_op_cached, 0, plant.P0);
Q = diag([1, ...
1e-3, 1e-3, 1e-3, 1e-3, 1e-3, 1e-3, ...
1e-2, 1e2, 1]);
R = 1e6;
% MATLAB's lqr requires Control System Toolbox. Fall back to
% icare (in base MATLAB from R2019a) if lqr is unavailable.
try
K = lqr(A, B, Q, R);
catch
[~, ~, K_ric] = icare(A, B, Q, R);
K = K_ric;
end
end
u = -K * (x - x_op_cached);
end