Skip to content

Python Examples

Writing and Reading a File

Create a HIPO file with particle data and read it back.

import hipopy
import numpy as np

# --- Write ---
schema = hipopy.Schema("REC::Particle", 300, 1)
schema.parse("pid/I,px/F,py/F,pz/F,charge/B")

d = hipopy.Dictionary()
d.add_schema(schema)

writer = hipopy.Writer()
writer.add_dictionary(d)
writer.open("particles.hipo")

np.random.seed(42)
for evt in range(100):
    event = hipopy.Event()
    n = np.random.randint(1, 6)
    bank = hipopy.Bank(schema, n)
    for i in range(n):
        bank.put_int("pid", i, np.random.choice([11, -11, 2212, 211, -211]))
        bank.put_float("px", i, np.random.normal(0, 1))
        bank.put_float("py", i, np.random.normal(0, 1))
        bank.put_float("pz", i, np.random.uniform(0.5, 5.0))
        bank.put_byte("charge", i, -1 if i % 2 == 0 else 1)
    event.add_structure(bank)
    writer.add_event(event)

writer.close()
print("Wrote 100 events")

# --- Read ---
with hipopy.open("particles.hipo", banks=["REC::Particle"]) as f:
    print(f"File has {f.entries} events")
    for i, banks in enumerate(f):
        bank = banks["REC::Particle"]
        pid = bank["pid"]
        px  = bank["px"]
        py  = bank["py"]
        pt  = np.sqrt(px**2 + py**2)
        if i < 3:
            print(f"  Event {i}: {len(pid)} particles, pids={pid}, max pT={pt.max():.3f}")

Output:

Wrote 100 events
File has 100 events
  Event 0: 4 particles, pids=[  11 2212 -211  211], max pT=1.234
  Event 1: 3 particles, pids=[ -11   11  211], max pT=0.987
  Event 2: 5 particles, pids=[2212  -11 -211   11  211], max pT=1.567

NumPy Batch Analysis

Read all data at once into flat NumPy arrays for fast analysis.

import hipopy
import numpy as np

# Read all particles from all events as flat arrays
data = hipopy.read_columns("particles.hipo", "REC::Particle",
                            columns=["pid", "px", "py", "pz"])

print(f"Total particles: {len(data['pid'])}")

# Compute derived quantities
pt = np.sqrt(data["px"]**2 + data["py"]**2)
p  = np.sqrt(data["px"]**2 + data["py"]**2 + data["pz"]**2)

print(f"pT: mean={pt.mean():.3f}, max={pt.max():.3f}")
print(f"|p|: mean={p.mean():.3f}, max={p.max():.3f}")

# Filter by particle type
for name, pid_val in [("electrons", 11), ("protons", 2212), ("pions+", 211)]:
    mask = np.abs(data["pid"]) == pid_val
    n = mask.sum()
    print(f"{name}: {n} ({100*n/len(data['pid']):.1f}%), "
          f"mean pT={pt[mask].mean():.3f}")

Awkward Array Ragged Analysis

Preserve per-event structure for event-level operations.

import hipopy
import numpy as np
import awkward as ak

particles = hipopy.read_bank("particles.hipo", "REC::Particle",
                              columns=["pid", "px", "py", "pz"])

print(f"Type: {particles.type}")
print(f"Events: {len(particles)}")

# Particles per event
n_per_event = ak.num(particles.pid)
print(f"Particles/event: min={ak.min(n_per_event)}, "
      f"max={ak.max(n_per_event)}, mean={ak.mean(n_per_event):.1f}")

# pT for all particles (vectorized, ragged-aware)
pt = np.sqrt(particles.px**2 + particles.py**2)

# Max pT per event
max_pt = ak.max(pt, axis=1)
print(f"Max pT per event: mean={ak.mean(max_pt):.3f}")

# Select electrons per event
is_electron = np.abs(particles.pid) == 11
electrons = particles[is_electron]
n_with_electron = ak.sum(ak.num(electrons.pid) > 0)
print(f"Events with electrons: {n_with_electron}/{len(particles)}")

# Read only first 10 events with column subset
subset = hipopy.read_bank("particles.hipo", "REC::Particle",
                           columns=["px", "py"], max_events=10)
print(f"Subset: {len(subset)} events, fields={subset.fields}")

Low-Level Read/Write

Direct C++ API style for maximum control.

import hipopy

# Define two bank types
part_schema = hipopy.Schema("event::particle", 100, 1)
part_schema.parse("pid/I,px/F,py/F,pz/F")

det_schema = hipopy.Schema("event::detector", 100, 2)
det_schema.parse("id/I,energy/F,time/F")

# Write file with two banks per event
d = hipopy.Dictionary()
d.add_schema(part_schema)
d.add_schema(det_schema)

writer = hipopy.Writer()
writer.add_dictionary(d)
writer.open("multi_bank.hipo")

for i in range(50):
    event = hipopy.Event()

    pbank = hipopy.Bank(part_schema, 3)
    for row in range(3):
        pbank.put_int("pid", row, 11 + row * 100)
        pbank.put_float("px", row, 0.1 * (row + 1))
        pbank.put_float("py", row, 0.2 * (row + 1))
        pbank.put_float("pz", row, 1.0 + row)
    event.add_structure(pbank)

    dbank = hipopy.Bank(det_schema, 2)
    for row in range(2):
        dbank.put_int("id", row, 10 + row)
        dbank.put_float("energy", row, 0.5 * (row + 1))
        dbank.put_float("time", row, 100.0 + row * 10)
    event.add_structure(dbank)

    writer.add_event(event)

writer.close()

# Read back with low-level API
reader = hipopy.Reader("multi_bank.hipo")
d2 = hipopy.Dictionary()
reader.read_dictionary(d2)

print(f"Banks in file: {d2.get_schema_list()}")
print(f"Events: {reader.get_entries()}")

event = hipopy.Event()
pbank = hipopy.Bank(d2["event::particle"])
dbank = hipopy.Bank(d2["event::detector"])

reader.next()
reader.read(event)
event.read(pbank)
event.read(dbank)

print(f"\nFirst event:")
print(f"  Particles: {pbank.get_rows()} rows")
for row in range(pbank.get_rows()):
    print(f"    pid={pbank.get_int('pid', row)}, "
          f"px={pbank.get_float('px', row):.2f}, "
          f"py={pbank.get_float('py', row):.2f}")

print(f"  Detectors: {dbank.get_rows()} rows")
for row in range(dbank.get_rows()):
    print(f"    id={dbank.get_int('id', row)}, "
          f"energy={dbank.get_float('energy', row):.2f}")

Expression Parsing

Use the built-in expression parser for calculations.

import hipopy

# Basic arithmetic
p = hipopy.Parser("2 * x + y")
p["x"] = 3.0
p["y"] = 1.0
print(f"2*x + y = {p.evaluate()}")  # 7.0

# Physics: compute transverse momentum
pt_calc = hipopy.Parser("sqrt(px*px + py*py)")
pt_calc["px"] = 3.0
pt_calc["py"] = 4.0
print(f"pT = {pt_calc.evaluate()}")  # 5.0

# Evaluate different expressions with same parser
p2 = hipopy.Parser("1")
print(f"sin(3.14159/2) = {p2.evaluate('sin(3.14159/2)'):.4f}")  # ~1.0
print(f"exp(1) = {p2.evaluate('exp(1)'):.4f}")                  # ~2.7183

Source Files

The full example scripts are available in the repository at extensions/pybind/examples/:

  • write_read.py -- Create and read back a HIPO file
  • numpy_analysis.py -- NumPy-based particle analysis
  • awkward_analysis.py -- Awkward Array ragged batch reading
  • event_filtering.py -- Row filtering with NumPy masks and Parser
  • multi_file.py -- Multi-file processing with InputSource, Fusion, and batch reading