nano.rust agrees with ROOT — on ROOT's own NanoAOD tutorial file

2026-06-21 — a screencast + an interop check: read the file from ROOT's df102 dimuon tutorial with pure-Rust nano.rust, reproduce the dimuon spectrum (the Z peak shows up), and confirm the values match ROOT itself, bit for bit.

ROOT ships a canonical NanoAOD tutorial, df102_NanoAODDimuonAnalysis, whose input is Run2012BC_DoubleMuParked_Muons.root — the famous CMS dimuon-spectrum file (J/ψ, Υ, Z). It's a great interop target: if nano.rust reads that file and agrees with ROOT, the pure-Rust I/O has nothing to hide.

(No player? Raw cast: demo-root-interop.cast.)

1. Inspect ROOT's file over HTTPS

nano inspect now takes a URL — no download, just byte-range metadata reads:

$ nano inspect "https://eospublic.cern.ch//eos/opendata/cms/derived-data/AOD2NanoAODOutreachTool/Run2012BC_DoubleMuParked_Muons.root" --insecure
TTree Events entries=61540413
  nMuon u32
  Muon_pt f32
  Muon_eta f32
  Muon_phi f32
  Muon_mass f32
  Muon_charge i32

61.5 M events; the muon branches ROOT's df102 uses.

2. Reproduce the df102 dimuon spectrum

dimuon_opendata streams events, pairs opposite-charge muons, and computes the invariant mass — the same analysis as the tutorial:

$ dimuon_opendata "<that url>" 20000 --insecure
opposite_charge_pairs: 13743
z_window_60_120_gev: 2465
mass_histogram_gev:
    0-20     6861 ################################
   20-40     3155 ###############
   40-60     1154 ######
   60-80      445 ###
   80-100    1867 #########   <-- the Z
  100-120     153 #
bytes_fetched: 768178 / 2244449133

The Z peak stands out in the 80–100 GeV bin, and reading 20 000 events fetched 768 KB of the 2.2 GB file (0.034 %) — on-demand, nothing stored.

Dimuon invariant-mass spectrum from CMS Open Data, reconstructed by nano.rust

The dimuon spectrum, plotted in-process with kuva via --plot (--features plot).

3. The interop check: nano.rust vs ROOT, same file

The point of the exercise. nano.rust reads over HTTPS (pure Rust, no ROOT); the locally-installed ROOT reads the same file over xrootd. First five events:

entry nano.rust (HTTPS) ROOT (xrootd)
0 nMuon=2 [10.7637, 15.7365] nMuon=2 [10.7637, 15.7365]
1 nMuon=2 [10.5385, 16.3271] nMuon=2 [10.5385, 16.3271]
2 nMuon=1 [3.2753] nMuon=1 [3.27533]
3 nMuon=4 [11.4292, 17.6340, 9.6247, 3.5022] nMuon=4 [11.4292, 17.634, 9.62473, 3.50223]
4 nMuon=4 [3.2834, 3.6440, 32.9112, 23.7218] nMuon=4 [3.28344, 3.64401, 32.9112, 23.7218]

Identical (modulo display rounding). A pure-Rust reader over an HTTPS byte-range and ROOT over xrootd return the same bytes decoded the same way, on ROOT's own canonical example file. (Reproduce locally with scripts/root_crosscheck.sh.)

Why it matters

This is the I/O layer earning trust the only way that counts: against the reference implementation, on its own data. The same nano-io reader powers the spec → kernel → workflow pipeline, so everything above it inherits a read path that ROOT itself agrees with — while staying pure Rust, remote-on-demand, and ROOT-free at runtime.