A reactor's temperature is held near a setpoint by a proportional thermostat, a recursive feedback loop sampled once per tick:
u(t) = Kp · (Tset − T(t)) (controller output)
T(t+1) = T(t) + [ −kloss·(T(t) − Tamb) + u(t) ] · Δt + noise
(recursive plant equation y(t+1)=f(y(t),u(t)))
Pressure is physically coupled to temperature (a sealed vessel builds pressure as it heats), so under normal operation:
P(t) = Pbase + α·(T(t) − Tamb) + noise
Each new sample is pushed into a sliding window of the last N points (N = correlation window). From that window the simulator computes the rate of change, the Pearson correlation coefficient between the two streams, and an anomaly score from the regression residual:
ΔT/Δt = (T(t) − T(t−1)) / Δt
R = Cov(T,P) / (σ_T · σ_P) (Pearson correlation, window)
ê = P − (slope·T + intercept) (least-squares residual)
z = (ê − mean(ê)) / std(ê) (anomaly score)
Trigger Malfunction forces a brief decoupling — temperature drops while pressure spikes independently, exactly the "pressure up, temperature down" fault pattern real plant operators watch for. R collapses toward zero (or negative) and |z| crosses the alert threshold, flagging the correlated cloud's outliers in red.
This 2D view plots the same sliding window directly as a T–P scatter plot (X = temperature, Y = pressure) instead of a 3D point cloud with a recency depth axis — each dot fades as it ages out of the window, and the freshest sample pulses so it's easy to track. A rolling strip chart along the bottom shows both raw streams over time. Drag to pan the scatter plot and scroll/pinch to zoom.