SPIRAL2 LLRF fault-diagnosis pipeline
A working companion to the pipeline behind the LLRF fault-diagnosis paper: how raw superconducting-cavity postmortem buffers become labeled events, features, and models. Built up together, step by step — each section below fills in with real plots and notes as we explore it in conversation, rather than all at once.
Pipeline at a glance
One trunk — extraction through feature engineering — then five independent
consumers of the same feature matrix, converging into a shared explainability and
reporting layer. This is an orientation diagram for our own exploration, distinct from
the paper's own fig:pipeline_overview (still a hand-drawn placeholder there).
PyPostMortem, labeled by the
deterministic ground-truth classifier Classify_PostMortemFile into seven
fault categories, quality-filtered, then preprocessed (high-pass filter, per-file
trigger alignment, Z-score normalization) into a 777-feature matrix. Five modeling
tasks each consume that matrix independently — root cause's target is the same
fault-label vector multi-label classification uses, not multi-label's model output, so
the two are siblings rather than chained. Diagram drawn from
report/sections/03_system_description.tex and
04_methodology.tex as of the 2026-09-07 revision; no fabricated numbers.
Data foundation
prepare_data_cluster_v7.py Confirmed 2026-09-09
Every event starts as a raw postmortem buffer captured by the LLRF system's trigger
logic across 27 monitored signals. Labels come from Classify_PostMortemFile,
a deterministic classifier built on the first hardware fault-register bit to trip
chronologically — not the older, less reliable summary ALM header field this pipeline
used to rely on. Four of the seven categories get a further physics-threshold refinement on
top of that hardware decode: a cavity-voltage fall of at least 50% within a bounded time
window separates a genuine quench from a false alarm; the reflected-power/cavity-voltage
ratio Ucav/Ucat > 8.5 marks an RF safety threshold exceedance; oscillation, gain,
and beam-anomaly subtypes of RF regulation out of tolerance come from Z-score+FFT
analysis of the drive and reflected signals. The remaining three categories are the hardware
decode alone.
Of 14,232 raw files scanned (2019–2025), 102 fail to parse and 20 are zero-byte, leaving 14,109 ground-truth rows. Before any of the seven fault categories or Normal is even considered, each file must pass six validity checks — in order, so a file failing an earlier check is never evaluated against a later one. Chart below reflects the corrected classifier (re-run 2026-09-09, see the callout further down) — the check order and the four reliability/state checks are unchanged, only the feed-forward check's scope changed:
AMPT/KPI are per-file acquisition-quality metrics, not state
flags; Ucav/mask are direct signal/metadata validity checks.
Counts from the re-run LLRF_result.csv, 2026-09-09.
BEAM=NON in the header doesn't mean "no beam present" — it means feed-forward
disabled, and the field has only been operationally meaningful since 2021 (before that it
defaults to NON because feed-forward didn't exist yet: 2019 100.0%, 2020 96.4% of
files header-flagged NON, vs. a genuine 50.3%→9.3% decline from 2021→2025).
Classify_PostMortemFile's _check_filters() has been fixed to scope the
check to 2021+ (gated on the acquisition's own header DATE field) and re-run against
the full 2019–2025 corpus.
BEAM=NON (a raw header statistic, unaffected
by the fix itself). 2019–2020 (faded bars) sit at a constant ~100%, consistent with
feed-forward not existing yet; 2021 on is a genuine, declining operational choice.
Fixing the scope recovers real events — not an estimate this time, the classifier has actually been re-run against the full corpus and the before/after is a direct diff:
Is the header trustworthy, or could a stuck bit be discarding files that were actually
running? — the coherence check from before the correction, still valid as a read of
what the header field itself behaves like (the sample below was drawn from the old, unscoped
Beam NON grouping, which mixes pre-2021 defaulted-NON files with genuine post-2021
feed-forward-off files — useful for checking the flag's physical meaning, not a current
rejection-count figure; see the corrected counts above for that). For a random sample of files
in each rejected group (n=100, plus a 150-file passing baseline mixing Normal and
every fault category), we read the real cavity-voltage (Ucav) and drive (Uci) waveform
amplitude via PyPostMortem, independent of the header flags that got the file
rejected.
Loop OFF is unambiguous: 95% collapse near zero, fully
coherent with an open control loop. Feed-forward off splits: median
0.39 MV/m (vs. 3.16 baseline), but ~35% of the sample sits inside the baseline's own
operating range, loop still closed — feed-forward is a control-loop compensation
feature, unrelated to beam presence, so a live-looking file with it disabled needs no
special explanation. The dead-looking majority lines up with the recoverable-data check
above: most of that group fails a real check (low KPI, open loop) once you look past the
feed-forward flag. AMPT and KPI are acquisition-quality metrics,
not state flags, so their large overlap with the baseline range was always expected.
n=100 per rejected group, n=150 baseline, single random sample
(seed 20260908), one pass — a first look, not a hardened estimate.
One more thing tested along the way: is there an independent beam-presence signal in
this data at all, now that the header field turned out not to be one? The classifier's own code
carries a field literally called Presence faisceau, but it's built by renaming a
per-sample état bit labeled Temp RF int on the way out — a mismatch between
the variable name and its documented source. Tested against 250 of the same sampled files: even
in the passing baseline, this bit reads "on" for essentially none of the trace (median 0.09% of
samples). That's not what a real, sustained beam-presence signal should look like, so this field
looks mislabeled or misassigned rather than trustworthy — worth a bug report to whoever
owns Classify_PostMortemFile, separately from the feed-forward correction above.
courant pickup (pickup-probe current) was the other candidate; it also failed,
pinning at a near-constant ~33 µA for the large majority of files in every group.
Net: there is currently no independently-verified beam-presence signal in this dataset at all.
Status: V7 extracted and validated. The classifier fix is applied, re-run, and the
recovery confirmed (+756 events, entirely 2019–2020, everything else untouched).
prepare_data_cluster_v7.py (forked from V6, with the
beam_present→feed_forward_enabled feature rename applied) has
run against the corrected ground truth: 2,865 events (2,866 ground-truth-passing minus
the same single feature-extraction failure V6 also hit), 1,478 Normal / 1,387 fault,
810 features (up from 777 — expected: the same 0.95 correlation-dedup threshold
keeps a different subset once the dataset's composition changes, not a bug), 246 PCA
components. Validated directly: NaN rate in the newly-recovered 2019–2020 events
(0.20% / 0.31%) is lower than the full corpus's own average (0.45%), and their
fault-category mix (majority Normal, RF regulation out of tolerance the largest fault
category) matches the rest of the corpus — no sign the recovered events are degenerate.
Steps 01–08 below still describe V6 — re-running them against V7 (Phase 2)
hasn't started yet.
Preprocessing
preprocess_signals() Explored 2026-09-09
Three operations, in order, applied to every one of the 27 signals independently: a
4th-order Butterworth high-pass filter (cutoff 0.01, normalized to each file's own Nyquist
frequency — i.e. relative to that file's own sample rate, since decimation varies
~250× across the corpus) removes DC offset and slow drift while keeping fast
transients; each event is aligned to its own true trigger sample, read directly off the
acquisition's real time axis (zero_idx = argmin(|time|)) rather than a fixed
offset; then the filtered signal is Z-score normalized over its own event window, with the
filtered-but-unnormalized version kept alongside it (the 2026-09-07 fix from step 02, so
real-amplitude statistics have real amplitude to work from).
The old bug, with a real number attached: for a representative NDEC=200 event
(2022 10 12 CMA01_0001, an Oscillation Ucav subtype of RF regulation
out of tolerance), the true trigger sits at sample 50,047 of 100,096. The pipeline's
old hardcoded trigger_idx=3000 would have pointed 534 ms early
— 47,047 samples off, 47% of the entire file — treating what was actually
mid-acquisition steady-state as if it were the pre-trigger precursor window. Every
"pre-trigger" and "post-trigger" feature in the old pipeline was computed from the wrong
slice of the trace for a file like this one.
Two more things worth being precise about, since they're easy to gloss over. First, the
precursor window used for early-fault-onset detection isn't "the first 3,000 samples" or
"34 ms" in some fixed sense — it's scale_samples(3000, dt_us),
which converts a sample count tuned at the dominant NDEC=200 spacing
(dt ≈ 11.4 µs) into whatever sample count gives the same
physical duration for this file's own dt_us. A file with NDEC=100 (like
several of the recovered 2019 events above) needs about twice as many samples for the same
34 ms. Second, one fault category — RF regulation out of tolerance — gets a
further onset refinement on top of the informatic trigger: Alexandre's AMPT-based physical
onset detection (find_true_t0_with_ampt), applied only to that category since
it's the one his validation actually covers, per the earlier user decision documented in the
methodology.
Feature engineering
engineer_features() Explored 2026-09-09Three families, computed for every event: 17 statistics × 27 signals over the full window (459 candidates); seven physics-informed families targeting specific LLRF dynamics (39 candidates: effective decay, detuning, microphonics, RF mismatch, modulator command, phase stability, RF power flow); and temporal/derivative features — per-segment slopes, finite differences, CUSUM change points, early/late-window ratios — the largest group by far. Before correlation filtering that's 1,200+ raw candidates; after the >0.95 dedup pass, 810 features survive in V7 (up from 777 in V6). Verified directly on V7 with the same family classifier the paper's SHAP analysis uses (100% coverage, zero unclassified features, both versions):
One family worth a close look because it's the most physically interpretive: effective system decay. Post-trigger, the pipeline fits cavity voltage amplitude (|Ucav|, in MV/m) to V(t) = V0·e−t/τeff over a fixed 500-sample-equivalent window (≈5.7 ms), by linear regression on ln|Ucav|. The report is explicit that τeff is not the cavity's intrinsic QL — with the LLRF loop still active and protection logic responding, what's measured is the whole system's response. First pass at this (below, corrected) picked one clean-fitting and one poor-fitting quench event and left it at "an active loop makes the decay non-exponential" — too glib, and the log-scale plot hid the actual units and made a genuinely important detail invisible. Redone properly: measured how long each of the 31 real quench events takes to drop below 20% of its pre-trigger amplitude, across the whole category, not two cherry-picked examples.
Checked whether the pipeline's own subtype-clustering step (09, k=2 search per category) already found this: it gives 19/9 for this category on V6's 28 events, and that smaller cluster (n=9) is disproportionately the group with no valid decay window (56% vs 26% in the larger cluster) — correlated with the fast/slow split, but not identical to it. Checked why: the real top-differentiating features between those two clusters (same Cohen's-d method the report's own subtype figure uses) turn out to be modulator-command and reference-frequency statistics, not the decay features directly — the existing clustering runs over the full 810-feature space and lands on whatever axis separates best overall, which isn't necessarily collapse-timescale. The collapse-time gap shown above is a cleaner, more directly physical signal than what the current full-feature clustering surfaces — worth flagging as a concrete, real methodology improvement: either add collapse-timescale as an explicit feature, or run the subtype search on the physics-informed decay features specifically rather than the full feature space, before trusting the current quench subtype split as physically meaningful.
Last check: the whole-signal-statistic normalization-order bug (Sec. 00/01, fixed 2026-09-07)
reduced near-constant features from 65 to 15 on V6. Re-ran the same check on V7:
16 near-constant features (std<1e-6 of 810) — same story, one more. The two
previously-disclosed non-functional features (reflected_saturation_fraction,
control_saturation_fraction, still-miscalibrated legacy thresholds) are still in
the list, unchanged. The one addition, llrf_loop_closed, is easily explained: V7's
recovered 2019–2020 events skew the corpus slightly more toward closed-loop operation,
pushing that flag's variance just under the threshold — a composition effect, not a new
bug.
Precursor detection
prepare_05_phase0_precursor.py Explored 2026-09-08Early fault-onset detection, restricted to the pre-trigger-only 386-feature subset so the task can't cheat off the post-trigger transient. Five unsupervised baselines (Isolation Forest, LOF, Mahalanobis distance on a PCA-reduced Ledoit–Wolf covariance, PCA reconstruction error, DBSCAN) establish how much separability exists without labels, then a class-weighted Random Forest is trained supervised on the same subset. An ensemble score combines all of the unsupervised methods with the RF's own probability output.
step_05_v6/precursor_detection.pkl — V6, the last dataset with a full
downstream re-run, not V7 (see the "Data foundation" step above and the sidebar
footer for the V6/V7 dataset status). Step 05 is one of the steps Phase 2 (re-running
everything against V7) would need to redo, and Phase 2 hasn't happened yet. Concretely:
none of the ~756 events
recovered by the feed-forward fix (mostly 2019–2020) were in this model's
training/test data, and the mislabeled beam_present field (only correctly
meaningful post-2021 on V6) may itself have been a feature the model leaned on. The one
finding below that's unaffected by this: the buffer-length numbers (568.4ms/1136.8ms),
which come from reading raw postmortem files directly, independent of dataset version.
The large supervised/unsupervised gap (0.999 vs 0.58–0.79) is a real, learnable decision boundary, not a window-length leak: a follow-up check restricting to only the already-fixed-window columns (382 of 766, same conclusion as the 386-feature production subset above) still gets RF AUC 0.9998 while Isolation Forest/LOF on the identical columns stay at 0.65/0.59 — unsupervised methods can't see what the label-trained model sees, which window length alone can't explain.
step_05_v6/precursor_curves_exploration.json.
A model really keying on an approaching fault should look uncertain far from the trigger and grow confident as it nears — instead, confidence is already saturated at the furthest offset tested and never moves. Taken alone, that pattern would suggest the model is picking up a stable per-capture characteristic unrelated to the fault itself, rather than genuine fault-onset proximity — the open caveat the report already carries (Sec. 6/7/8).
Net reading: there is a real, fault-specific signal present far earlier than the interlock trip — genuine early separability, not a spurious correlate. What it is not, per the flat curves above, is a signal that ramps up as the fault approaches; it appears (at least within the tested window) present at roughly constant strength well in advance, on fault-destined recordings specifically. Whether that constant early signature reflects a genuine slow-developing physical precursor or an early fault-associated operating-condition change that isn't itself the cause ramping toward the trigger is still open — this test rules out the “meaningless artifact” reading, it doesn't yet distinguish between those two remaining explanations.
Binary classification
prepare_06_phase1_binary.py Not yet exploredFault-vs-Normal on the full event window (post-trigger included), using all 777 features, compared across Logistic Regression, Random Forest, XGBoost, and an RBF-kernel SVM. This task is expected to sit near ceiling performance — the full window includes the interlock-trip transient itself, which is close to trivially separable on amplitude alone — a materially easier task than precursor detection above by design, not evidence of a leak on its own.
Multi-label classification
phaseA/B/C/D_*.py Not yet exploredFour competing approaches to the same seven-category task, each class-weighted appropriately for its own structure: Binary Relevance, Classifier Chains, and Label Powerset (all with RF or XGBoost base estimators), plus four deep multi-head architectures (CNN, Transformer, CNN-LSTM, MLP) sharing a per-label weighted BCE loss. Every fault event in this dataset carries exactly one active label by construction of the ground-truth classifier, so the genuinely multi-label case (several simultaneous faults) hasn't been exercised on real data yet. Label Powerset is the best-performing approach on the current class-weighted, leakage-safe rerun; all four deep architectures underperformed the classical baselines on the comparison run of record.
Root cause identification
prepare_08_phase3a_rootcause.py Not yet explored
A class-weighted Random Forest predicts the primary fault category among the seven, on
the full 777-feature set. The training target is argmax over the multi-label
vector — a deterministic first-flagged-category rule, not an independently labeled
physical root cause — and because the current dataset has zero genuinely multi-fault
events, that argmax currently has one unambiguous answer per event rather than exercising
any real tie-breaking. A real limitation of the task's current formulation, disclosed as
such rather than presented as a solved comparison.
Fault subtype clustering
prepare_09c_enhanced_subclass_discovery.py Not yet exploredWithin each fault category, K-Means, Agglomerative, and Gaussian Mixture clustering are compared by silhouette score to look for subtypes. Two corrections were needed before this was trustworthy: a near-zero-denominator division bug that let a handful of events take extreme outlier values, and a subtler bias where unconstrained silhouette maximization reliably chose whichever split isolated the single tightest minority subgroup — as small as one event — over a genuine population split. Fixed by winsorizing features at the 5th/95th percentile per category and requiring every cluster hold at least 15% of that category's events. Every one of the five analyzable categories converges to a balanced k=2 split under that constraint.
Explainability & reporting
generate_macros.py Not yet exploredSHAP explains the classification models per physics-informed feature family, reported as per-feature-mean importance rather than a naive per-group sum — the statistical and temporal families outnumber the seven physics families combined roughly 20:1, so a group sum would structurally favor them regardless of how informative any single physics feature is. Every number in the paper, including every figure and table above, traces to a versioned, git-tracked results manifest rather than being hand-typed: each pipeline script writes its own manifest, a generator converts it into LaTeX macros, and a checker flags any bare numeric literal in the report text that isn't backed by one.
Session log
A running narrative of what we've looked at and found, in the order we looked at it — kept here rather than scattered across chat turns.
Set up this explorer. Pipeline overview diagram and all nine step sections drawn
directly from the current report (report/sections/03_system_description.tex,
04_methodology.tex, 2026-09-07 revision) and the V6/777-feature dataset —
no plots yet, no numbers invented. Next: pick a step to open up with real plots.
00 Data foundation, done. Pulled the exact quality-filter breakdown from
Classify_PostMortemFile's ground-truth CSV (11,999 of 14,109 files rejected:
68% Beam NON, 13% Loop OFF, 9% AMPT>10%, 7%
KPI<10, plus small Ucav/mask checks) — the filter runs as an
early-exit cascade, so these aren't independent counts. Then ran a real signal-coherence
check requested by the user: read actual cavity-voltage/drive amplitude via
PyPostMortem for a 550-file random sample across the passing baseline and
every rejected reason, independent of the header flags that got each file rejected.
Loop OFF is cleanly coherent (95% dead). Beam NON is genuinely
heterogeneous — roughly a third of the sample shows a fully live cavity (loop still
closed, no fault bit tripped for many) — not a broken header bit, but real RF-only
conditioning/standby operation without a particle beam, a legitimate state the header is
reporting correctly. AMPT/KPI are acquisition-quality metrics,
not state flags, so their high live-fraction is expected, not a red flag. Side finding:
courant pickup pins near a constant ~33 µA for most files regardless
of state — not a reliable solo diagnostic. Next: pick step 01 or 02, or dig
further into the Beam-NON heterogeneity (e.g. is it worth splitting into a separate
no-beam/RF-conditioning cohort for a future study?).
Correction from the project lead: BEAM (OUI/NON) does not mean
particle-beam presence — it means feed-forward enabled/disabled in the LLRF
control loop, and the field has only been operationally meaningful since 2021. Checked
directly against the ground-truth CSV: 2019 is 100% BEAM=NON, 2020 is 96.4%
— a constant default, not a real signal — then a genuine, declining rate from
2021 (50.3%) to 2025 (9.3%) as feed-forward adoption grew. Re-derived the whole coherence
story on the corrected premise (the "RF-only, no beam" reading from yesterday is retracted
— feed-forward being off never implied anything about RF/beam state, so the live-looking
minority needed no special story). More importantly, this surfaces a real, actionable bug:
the classifier applies this check the same way in every year, so it's likely discarding
~700 legitimate 2019–2020 events (tested 200 pre-2021 BEAM=NON-only
rejects against every other real check: 29/200, 14.5%, pass everything else — mostly
Normal, some genuine faults). Also checked whether any independent beam-presence signal
exists in the data at all (the classifier's own Presence faisceau field) —
it doesn't hold up either (built from a mismatched état bit, reads "off" almost everywhere
even in beam-present baseline files). Next: user to decide whether to (a) recommend
Classify_PostMortemFile scope the feed-forward check to 2021+ and re-run to
recover the ~700 events, (b) fix the "beam-present" wording in
report/sections/03_system_description.tex now, or (c) file both as future work
and move on to step 01.
Fix applied, re-run, confirmed — Phase 1 (V7 extraction) started.
Classify_PostMortemFile's _check_filters() now scopes the
feed-forward check to acquisitions from 2021 onward (parses the year from the header's own
DATE field), config key renamed check_feed_forward +
feed_forward_min_year: 2021, rejection label renamed Feed-forward NON.
Re-ran the classifier across the full 2019–2025 corpus (backed up the old CSV first):
ground-truth-passing rows went from 2,110 to 2,866 (+756, +35.8%), a real diff not an
estimate — entirely from 2019 (+73) and 2020 (+683), every year 2021–2025
byte-for-byte unchanged. Rewrote this section's charts/numbers to match (the old "8,205
Beam NON" framing this page used to show is now clearly marked as the pre-fix count; current
corrected count is 3,229, no longer the single dominant reason). Also fixed the same stale
beam_present feature name in prepare_data_cluster_v6.py (forked as
prepare_data_cluster_v7.py, renamed to feed_forward_enabled) and
in the two downstream scripts that hardcode it for SHAP-taxonomy grouping. Report: Sec. 3
rewritten with the real numbers, verified compiling clean locally (0 fatal errors); Abstract
and Sections 4–9 + Appendices A–C gray-banner-marked "pending V7 re-run" until
the corrected dataset propagates through. V7 extraction is running now (SLURM job
58226540, launched today, forked from V6 with the same corrected ground truth) — V6
(2,109 events) is still what every step below this one describes until that finishes.
Next: once V7 lands, validate it, then Phase 2 (re-run steps 01–08 against it).
V7 extraction complete and validated. SLURM job 58226540 finished (54 min,
2019–2025 full corpus): 2,865 events, 1,478 Normal / 1,387 fault, 810 features
(up from 777 — expected correlation-dedup threshold sensitivity to the new dataset
composition, checked directly: 57 features newly survive, 24 previously-survivors no longer
do, beam_present correctly among the latter since it's the same field renamed).
Reconciles exactly (2,866 ground-truth-passing minus the same single extraction failure V6
also hit). Validated the recovered 2019–2020 events aren't degenerate: NaN rate
0.20%/0.31% vs. 0.45% corpus-wide (lower, not higher), fault-category mix matches the rest
of the corpus. Also caught and fixed a real gap: the coherence-check strip plot's x-axis
still literally rendered Beam NON as a chart label (the caption explained it,
but the chart itself didn't) — relabeled to Feed-forward off directly,
per direct user feedback. Next: Phase 2 (re-run steps 01–08 against V7) —
checking in with the user before launching, per the plan (multi-hour, many SLURM jobs).
Data foundation re-verified, then 01 Preprocessing explored. Extra pass on V7
before moving on: every fault category grew and none shrank (+756 splits as
Normal +412, RF regulation +199, Absence autorisation RF +20, Pickup threshold +82,
…), class balance held (50.5%→51.6% Normal), and pulled three real raw 2019
events directly (not just aggregate stats) — genuine LLRF traces, sensible Ucav
ranges, LOOP=ON/closed-loop operation, exactly the "feed-forward off but
otherwise normal" story the correction predicts. Then read the real
preprocess_signals() code and pulled one real event
(2022 10 12 CMA01_0001, NDEC=200, an Oscillation-Ucav fault) all the way
through: high-pass filter → Z-score, with the true trigger at sample 50,047 —
concretely showing the old hardcoded trigger_idx=3000 would have been 534ms
(47% of the file) early. Also verified directly that this file's single most extreme
Z-score value (93.6σ) sits at literal sample 0, the filter's own startup transient
— confirms the report's stated reason for excluding early-file samples from
analysis, not just an assertion. Next: continue exchanging on preprocessing, or
move to 02 Feature engineering.
02 Feature engineering explored. Ran the real family classifier the paper's own
SHAP analysis uses (100% coverage on both V6 and V7, zero unclassified) against V7's 810
features: physics-informed families are byte-identical in count to V6 (39 features, all
seven families unchanged) — only Statistical (262→260) and Temporal
(466→501) shifted, which is exactly what the >0.95 correlation-dedup threshold does
when the dataset's composition changes, not a sign anything computational changed.
Confirms the report's "~20:1" physics-vs-generic imbalance claim still holds (19.8:1 on
V7). Then pulled two real quench events all the way through the effective-decay
τeff fit: one fits cleanly (R²=0.596), the other barely fits at all
(R²=0.011) despite both being genuine Claquage events — checked across
more quench events and most look like the poor-fit case, which is a real, honest
confirmation of the report's own claim that an actively-regulated collapse isn't a clean
passive exponential (that's why R²/non-exponentiality are tracked as features, not an
afterthought). Re-ran the near-constant-feature check on V7: 16 (was 15 on V6, one new
addition traced to the 2019–2020 recovery shifting llrf_loop_closed's
variance, not a new bug); the two previously-disclosed non-functional threshold features
are unchanged. Next: continue exchanging, or move to 03 Precursor detection.
Correction to the decay-fit figure, from user feedback. Three real problems with the first pass: no units on the y-axis, the "poor fit" panel's window was mismatched to what actually happens physically (the collapse is over in 1–2 samples, so a 5.7 ms log-linear fit is regressing mostly noise), and jumping to "active loop makes it non-exponential" from two examples was underdetermined. Redid it properly: measured collapse time (to 20% of pre-trigger amplitude) for all 31 real quench events, not two. Real result, not two cherry-picked examples: a clean bimodal split, 23/31 (74%) collapse within 0.17 ms, the other 8 (26%) take 4.0–5.2 ms, zero events in between. Cross-checked against the pipeline's own step-09 subtype clustering for this category (19/9 split on V6): correlated with the fast/slow split (the smaller cluster is disproportionately the no-valid-decay-window group) but not identical — the real top-differentiating features between those two existing clusters (same Cohen's-d method the report's own subtype figure uses) are modulator-command/reference-frequency stats, not the decay features. Net: the user's hypothesis holds up as a real, sharp physical distinction, and it's a cleaner signal than what the current full-feature-space clustering finds — flagged as a concrete methodology improvement (cluster on decay-timescale features directly for this category) rather than something to just note and move past. Rebuilt the figure in linear MV/m units instead of unlabeled log scale.
03 Precursor detection, explored. Ran the step 05 unsupervised-vs-supervised comparison (RF 0.9991 AUC vs. 0.58–0.79 for five unsupervised baselines) and the window-leak follow-up (RF still 0.9998 on a properly fixed-window 382-column subset, unsupervised methods unchanged at 0.65/0.59 on the same columns — rules out a window-length leak as the explanation). Then went further: computed real probability-vs-time curves for 20 sampled true-positive fault events (12 offsets each, out to 477 ms before the trigger, SLURM job 58250756 after fixing a path bug from an earlier attempt — see below). 19 of 20 curves are essentially flat, already saturated at ≥0.85 confidence at the furthest offset tested, no ramp-up toward the trigger. Combined with the existing lead-time finding (39/40 sampled events right-censored, never dropping below 0.5 within ~477 ms), this is direct evidence against a genuine near-trigger precursor signal and for the report's already-flagged alternative: a stable per-capture characteristic of eventually-faulting files, not fault-onset proximity. The report's own proposed disambiguating test (score the same features on a window from the middle of a Normal event, away from any boundary) is still open, not run here.
Infrastructure note. The probability-curve job initially failed (SLURM job
58240255, 1s, exit 2): the driver script had been written to a Claude session's own
/scratch/... path, invisible to SLURM worker nodes once that session ended.
Rewrote it to the shared filesystem
(pipeline/00_scripts/investigate_precursor_curves.py) and resubmitted via a
proper sbatch wrapper — completed cleanly in 3 minutes.
Disambiguating test run — result complicates the earlier "stable per-capture characteristic" reading. Ran the report's own proposed follow-up: score the same 386-feature set on a window from the middle of a Normal event's own recording, far from any boundary (SLURM job 58257135, 20 confidently-true-negative Normal events). 0 of 20 scored above the 0.5 fault threshold (0.00–0.05 for 18 of 20; two at 0.34/0.44, still below). If the model were keying on a generic, label-blind acquisition characteristic, these should have scored elevated too — they didn't. Revised step 03's prose accordingly: this rules out "meaningless artifact," but doesn't resolve whether the flat, early, fault-specific signal reflects a genuine slow-developing physical precursor or an early fault-associated condition change that isn't itself ramping toward the trigger. Left as open, now with one wrong branch (generic artifact) eliminated rather than three explanations still on the table.