scan
Project columns of a single bank across an event range — the ROOT
TTree::Scan of HIPO. It's the command you'll use most.
hipoq scan rec.hipo --bank REC::Particle --cols pid,px,py,pz --events 0..100
event tag row pid px py pz
0 0 0 11 0.51 -0.02 2.31
0 0 1 2212 0.30 0.11 0.98
1 0 0 11 0.44 0.05 1.87
...
Each output row is one bank row, prefixed with its event index, the event's
tag, and the row number within the event.
tag is always there, even when every event has zeroIt sits with event because both describe the event rather than the row, and it
repeats down a bank's rows exactly as event does.
Emitting it only when some event happened to be tagged would make the CSV header
depend on the file's contents — a script reading column 3 would get row on
one run and pid on another. A constant column is the lesser problem. ⚠️ This
did shift the column positions after event.
Options
| Flag | Meaning |
|---|---|
--bank B | (required) bank to project (repeatable, comma-ok) |
--cols a,b,… | columns to show (repeatable, comma-ok). Empty = every column of the bank |
--events A..B | event span, end-exclusive (0..100). Omit for from the start |
--limit N | maximum output rows (default 100) |
--where E | keep only events passing a cut (repeatable, ANDed) |
--expr N=E | add a computed column (repeatable) |
--format F | table (default), csv, ndjson, or json |
Global --pdg decodes pid to symbols in table output (data formats stay
numeric).
Choosing columns
Omit --cols to get every column of the bank — array columns included,
rendered as a JSON array per row; name them to control order and width:
hipoq scan rec.hipo --bank REC::Particle # every column
hipoq scan rec.hipo --bank REC::Particle --cols pid,px,py,pz # just these, in order
Several banks at once
--bank is repeatable, and comma-ok. The rows of each bank are stacked under
their event, in the order you named them, and the output gains a bank column:
hipoq scan rec.hipo --bank REC::Particle,REC::Calorimeter --cols pid,energy
event,tag,bank,row,pid,energy
0,0,REC::Particle,0,11,
0,0,REC::Particle,1,2212,
0,0,REC::Calorimeter,0,,0.8
0,0,REC::Calorimeter,1,,0.9
The value columns are the union across the banks, in order of first appearance.
A column a bank does not have is blank for that bank's rows — blank rather
than zero, because zero is a value energy could legitimately take. row
restarts at 0 for each bank, so it still pairs with pindex directly.
Two banks have different row counts in the same event — four particles, seven
calorimeter hits — so there is no row correspondence to print, and inventing one
would be a guess. For the pindex join use a cut across
banks, dump for the whole event as one
object, or the TUI's x panel.
The bank column appears only when you name more than one bank, so a
single-bank scan is byte-identical to what it always was. That depends on the
arguments — which you wrote — never on how many banks the file happens to hold.
--expr is refused with several banks: a computed column is evaluated against
one driver bank, and p (which needs px,py,pz) means nothing for
REC::Calorimeter. Scan one bank at a time when computing columns.
Column names not present in the schema are silently skipped, so you can keep a
generous --cols list across files with slightly different banks.
Event range and limit
--events is end-exclusive; --limit caps the number of output rows (not
events), which matters because one event yields several rows:
hipoq scan rec.hipo --bank REC::Particle --events 500..600 --limit 1000
Filtering with --where
Keep only events that pass a cut — the same expression language used everywhere:
hipoq scan rec.hipo --bank REC::Particle --cols pid,px,py,pz \
--where "REC::Particle.pid == 11"
The cut selects events; all rows of the bank in a passing event are shown.
Computed columns with --expr
Add derived columns — a NAME=EXPRESSION, or a bare preset name:
# momentum, polar angle, sampling fraction
hipoq scan rec.hipo --bank REC::Particle --cols pid \
--expr p --expr theta_deg --expr sampling_fraction
# a custom transverse mass
hipoq scan rec.hipo --bank REC::Particle --expr "mt=sqrt(px*px+py*py)"
Computed columns are evaluated per row of the scanned bank (which is the expression's driver, so cross-bank references resolve by pindex). They appear in every output format. See computed columns for the full list of presets and the reverse-gather trick.
Formats
ndjson is the one to pipe into jq or a dataframe:
hipoq scan rec.hipo --bank REC::Particle --cols pid,px,py,pz --format ndjson | jq .
Array columns (like a covariance cov) come out as arrays in every format; in
ndjson/json they are real nested JSON arrays. For the full comparison of
table / csv / ndjson / json, see output formats.
scan projects the banks you name into one flat table, with a union of
their columns. To get every bank of an event, use dump: as NDJSON
for one object per event, or dump --format table for one table per bank per
event, each keeping its own columns.
Exporting more than the default 100 rows
--limit caps the output; raise it for a real export:
hipoq scan run.hipo --bank REC::Particle --limit 2000000 --format csv > particles.csv
The data formats (csv, ndjson, json) stream — each row is written and
dropped, so peak memory does not grow with the row count. --format table holds
every row instead, because a column is as wide as its widest cell, and above
100,000 rows it says so and points at the cheaper formats. See
Output formats for measured
numbers.