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