"The alarm told the operator that the plant was unhappy. It did not tell her which of two hundred sensors was lying, and at 3 a.m. that difference is the whole job."
A Blame-Assigning AI Agent
Prerequisites
This section takes the residual detector of Section 38.3 as given: a model of normality that emits a per-sensor error vector \(\mathbf{r}_t\), collapsed into an alarm. Localization decomposes that alarm rather than raising it. We lean on the process graph of Section 38.4 (which sensor talks to which) and the physical invariants of Section 38.1 (which conservation law was broken). The attribution machinery generalizes the time-series root-cause methods of Chapter 67, and the ranking-quality metrics reuse the evaluation discipline of Chapter 65. The attack-versus-fault question of Section 38.2 is settled here: localization answers where, not whose fault.
The Big Picture
Detection is a scalar; response is a place. When a residual detector fires on a water-treatment plant, the security operator does not need to know that "the process is anomalous", a fact the flashing screen already conveys. She needs to know that the pressure transmitter on stage three has been spoofed, or that valve MV-201 is stuck, so she can isolate that loop, switch to a manual setpoint, and keep the plant running while forensics proceeds. Root-cause localization is the bridge from "something is wrong" to "here, this component, right now". It is a ranking problem: given the anomaly, order the sensors and actuators by how likely each is to be the origin, and put the true culprit near the top of a short list a human can actually check. The temptation is to read the localization straight off the residual, blaming whichever sensor has the largest error. That naive reflex is wrong often enough to be dangerous, because a physical process propagates a disturbance across every coupled variable within seconds, and the loudest residual is frequently a downstream victim, not the source.
Attribution: which channel carries the anomaly
The cheapest localizer is already sitting inside the detector. If the alarm score is a Mahalanobis distance over the residual vector, the per-channel contribution to that distance is a natural attribution: the term \(z_{i,t} = \big((\mathbf{r}_t-\boldsymbol{\mu})\odot(\boldsymbol{\Sigma}^{-1}(\mathbf{r}_t-\boldsymbol{\mu}))\big)_i\) tells you how much sensor \(i\) drove the flagged step, and sorting the sensors by \(z_{i,t}\) produces a ranked suspect list at zero extra cost. For a deep detector whose score is not a clean quadratic form, the same idea generalizes through gradient attribution: the sensitivity \(\partial s_t / \partial x_{i,t}\), or a cleaner integrated-gradients or SHAP decomposition of the score onto its inputs, distributes the anomaly across channels. These are the interpretability tools of Chapter 67 pointed at the anomaly score instead of a class label.
Attribution answers "which channel is inconsistent", which is necessary but not sufficient. A whitened residual is honest about magnitude but blind to causality: it cannot distinguish the tampered sensor from the three neighbours whose readings drifted because the physics coupled them. That is why a good localizer treats attribution as a candidate generator, a short high-recall list, and then applies structure to sort origin from consequence.
Key Insight
The largest residual is a suspect, not a verdict. In a coupled physical plant a single manipulated variable perturbs its entire neighbourhood within one or two control cycles, so the sensor with the biggest error is often the most sensitive downstream measurement, not the point of attack. Localization that ranks by residual magnitude alone systematically blames the victim. The fix is to add the one thing a residual lacks: the direction of physical influence. Whichever channel deviated first, and sits upstream in the process graph of its co-anomalous siblings, is the origin; the rest are its shadow. Time-of-onset plus graph direction beats magnitude almost every time.
From magnitude to origin: time, graph, and invariants
Three structural signals turn a suspect list into a localization. The first is onset time. Estimate, per channel, the first sample at which its residual crossed a per-channel threshold and stayed above it; the earliest-onset channel among a cluster of co-firing sensors is the leading candidate, because effects cannot precede causes. Propagation delay in process plants is real and measurable (seconds to minutes as a disturbance travels down a pipe), so onset ordering carries genuine information.
The second is the process graph from Section 38.4. A learned or engineered adjacency, whether the attention weights of a graph neural network or a hand-drawn P&ID, encodes which variable can physically influence which. Restrict the origin search to nodes that are graph-ancestors of the anomalous cluster, and propagate blame along edges: a node whose anomaly is fully explained by an anomalous parent is a consequence, while a node that is anomalous with no anomalous parent is a source. This is graph message passing used for diagnosis rather than prediction; the sensor-network view of Chapter 54 supplies the same machinery.
The third, and the most discriminating for security, is the violated invariant. Section 38.1 gave you a library of physical relations that must hold, mass balance around a tank, a valve state consistent with the flow it gates, a level that can only rise when inflow exceeds outflow. When the detector fires, evaluate each invariant and report which ones are broken. An invariant names its variables explicitly, so a broken mass balance around tank T-401 localizes to exactly the meters and actuators in that equation, and it does something attribution cannot: it distinguishes a spoofed sensor (the reading contradicts a physical law the actuators still obey) from a compromised actuator (the physics genuinely changed, and every honest sensor agrees on the wrong new state). That sensor-versus-actuator split is often the single most useful bit the operator receives.
Practical Example: a spoofed level transmitter on SWaT
On the Secure Water Treatment (SWaT) testbed, an attacker holds level transmitter LIT-101 at a comfortable mid-tank value while the real tank drains. The residual detector fires, but its loudest channel is not LIT-101: it is the flow meter FIT-201 downstream and the pump P-101, whose behaviour has gone wildly off-manifold because the control loop, trusting the frozen level, keeps commanding inflow. Magnitude ranking blames the pump. The structural localizer disagrees on all three counts. Onset time shows FIT-201 and P-101 deviating after LIT-101's residual first ticks up. The process graph places LIT-101 upstream: it is the control input that drives P-101, not the reverse. And the mass-balance invariant around the tank is violated in a specific way, the measured level is flat while the integrated net inflow says it should be falling, which is the signature of a frozen sensor rather than a stuck valve. All three agree: origin is LIT-101, a spoofed sensor. The operator switches that loop to a redundant level estimate and the plant recovers, while the pump, correctly, is exonerated.
Ranking, and how to score a localizer fairly
Because the operator can only check a handful of components per alarm, localization quality is a top-\(k\) ranking question, not a hit-or-miss classification. The standard metrics reuse information retrieval directly. HitRate@\(k\) (also called HR@\(k\) or Hit@\(k\)) is the fraction of anomaly events whose ground-truth root cause appears in the top \(k\) ranked channels; on SWaT, WADI, and HAI, where each attack is annotated with the exact variables it manipulated, HR@1 and HR@3 are the numbers to report. Normalized discounted cumulative gain (NDCG@\(k\)) rewards putting the culprit higher within that list. Averaging must be per-event, not per-sample: a single ten-minute attack that dominates the timeline should count once, or a localizer that nails only the longest attack looks deceptively good.
The leakage traps of Chapter 65 reappear with a localization-specific twist. Tuning the onset threshold, the graph, or the attribution weights on the same attacks you score against turns the reported HR@\(k\) into memorization of that attack set; hold out whole attack events, never windows within an event, exactly as the contiguous-split rule of Chapter 5 demands. The snippet below scores a residual-magnitude localizer against annotated root causes and returns per-event HitRate@\(k\).
import numpy as np
def hit_rate_at_k(residuals, event_spans, true_causes, k=3):
"""
residuals : (T, C) per-sensor anomaly attribution (already whitened)
event_spans : list of (start, end) sample ranges, one per attack event
true_causes : list of sets of ground-truth channel indices, per event
Returns HR@k averaged over events (each event counts once).
"""
hits = 0
for (s, e), cause in zip(event_spans, true_causes):
# aggregate attribution over the event, then rank channels
score = residuals[s:e].mean(axis=0) # (C,)
ranked = np.argsort(score)[::-1][:k] # top-k suspects
if cause & set(ranked.tolist()): # culprit in the short list?
hits += 1
return hits / len(event_spans)
rng = np.random.default_rng(0)
T, C = 2000, 12
res = np.abs(rng.standard_normal((T, C)))
res[500:650, 4] += 6.0 # channel 4 is the true root cause of event 1
res[1200:1300, 9] += 6.0 # channel 9 is the true root cause of event 2
spans = [(500, 650), (1200, 1300)]
causes = [{4}, {9}]
for k in (1, 3, 5):
print(f"HR@{k} = {hit_rate_at_k(res, spans, causes, k):.2f}")
Right Tool: attribution without hand-rolling gradients
The gradient, integrated-gradients, and SHAP attributions that turn a deep detector's score into a per-channel blame vector are roughly 40 to 80 lines each to implement correctly (baseline handling, path integration, and Shapley sampling are all easy to get subtly wrong). captum and shap collapse each to two or three lines: wrap the detector's scalar score, call IntegratedGradients(model).attribute(x, baselines=x_clean) or shap.DeepExplainer, and get a signed per-sensor attribution back. The library handles the numerical integration, convergence checks, and batching; you keep the physics-aware post-processing (onset ordering and the graph walk) that no library knows about.
Research Frontier
The state of the art has moved from magnitude attribution toward explicit causal localization. Graph-deviation networks (GDN, 2021) learn a sensor graph and localize by per-node forecasting deviation, and remain a strong ICS baseline on SWaT and WADI. More recent work reframes root-cause localization as counterfactual reasoning: intervene on a candidate variable in a learned structural model or digital twin (Chapter 55) and ask whether resetting it to normal would explain away the whole anomaly; the smallest such intervention is the root cause. Causal-discovery methods (PCMCI and neural Granger variants) estimate the influence graph directly from the telemetry so localization no longer depends on a hand-drawn P&ID. The open problems are stealthy multi-point attacks that violate no single invariant, propagation delays long enough to scramble onset ordering, and honest evaluation: the field is still converging on whether HR@\(k\) on the same three public testbeds (SWaT, WADI, HAI) measures localization skill or overfitting to three particular plants.
Exercise
Extend the hit_rate_at_k baseline with an onset-time tie-breaker. For each event, instead of ranking by mean attribution, (a) compute each channel's first-crossing sample above its own healthy 99th-percentile residual, and (b) among channels whose aggregated attribution exceeds a recall threshold, rank the earliest-onset channel first. Construct a two-channel scenario where channel A is the true cause but channel B has the larger residual (B is a downstream amplifier), and show that magnitude ranking gives HR@1 = 0 while onset ranking gives HR@1 = 1. What happens to your onset ranker when the propagation delay is shorter than one sampling interval, and how would you detect that failure mode?
Self-Check
- Why does ranking suspects by residual magnitude alone tend to blame downstream victims rather than the true origin, and which two structural signals correct it?
- A broken mass-balance invariant around a tank shows a flat measured level while integrated net inflow says the level should be falling. Does this indicate a spoofed sensor or a compromised actuator, and why?
- Why must HitRate@\(k\) be averaged per attack event rather than per sample, and what leakage-safe rule governs how attack events are split between tuning and scoring?
What's Next
In Section 38.6, we turn the localizer and detector against a thinking adversary. A root-cause report is a target: an attacker who knows you localize by onset time and graph structure can craft a slow, distributed manipulation that keeps every invariant nominally satisfied and every residual below its threshold. We examine adversarial robustness of cyber-physical detectors and the transductive-leakage trap that makes many published ICS results collapse the moment the attack is genuinely unseen.