Skip to content

Your First CLAS12 Analysis

A complete, compilable starter: open a CLAS12 HIPO file, iterate events, keep charged particles, and histogram the vertex-z distribution. Once this runs on your machine, the rest of CLAS12 analysis is just swapping in better cuts and adding banks.

What you need

  • A CLAS12 HIPO file: any reconstructed output works, e.g. rec_clas_*.evio.*.hipo.
  • A working hipo4 install (see Installation).

The program

The program uses the sequential chain — the recommended way to read HIPO files. It works for one file or many, and event.getBank("...") looks each bank up by name, so there is no separate dictionary or schema setup.

#include "chain.h"
#include "twig.h"
#include <cstdio>

int main(int argc, char** argv) {
    if (argc < 2) { std::fprintf(stderr, "usage: %s <file.hipo>\n", argv[0]); return 1; }

    // Build the chain — one file here; ch.add_pattern("dir/*.hipo") for many.
    hipo::chain ch;
    ch.add(argv[1]);

    twig::h1d hvz(120, -15.0, 5.0);        // vertex-z histogram, −15 cm … +5 cm

    long events = 0, charged = 0;
    for (auto& [event, file_idx, event_idx] : ch) {
        ++events;
        auto parts = event.getBank("REC::Particle");
        for (int row = 0; row < parts.getRows(); row++) {
            if (parts.getInt("charge", row) == 0) continue;   // keep charged tracks
            hvz.fill(parts.getFloat("vz", row));
            ++charged;
        }
    }

    std::printf("processed %ld events, %ld charged tracks\n", events, charged);
    hvz.print();                           // ASCII histogram to the terminal
    return 0;
}

Build it against hipo4 via pkg-config (see Installation — pkg-config):

g++ -std=c++17 first_analysis.cc \
    $(pkg-config --cflags --libs hipo4) -o first_analysis
./first_analysis path/to/rec_clas.hipo

What the code is doing

  1. Build the chain. ch.add() registers a file; the chain opens it and reads its schema dictionary lazily when iteration starts. Add more files with repeated ch.add() calls or a glob via ch.add_pattern().
  2. Iterate every event. The range-based for loop walks every event across every file in the chain. file_idx is the current file; event_idx is the running event count.
  3. Get the bank. event.getBank("REC::Particle") returns the bank for the current event, looked up by name — no pre-built schema needed.
  4. Filter charged tracks. parts.getInt("charge", row) == 0 skips neutrals (photons, neutrons). Flip the comparison for a neutrals-only selection.
  5. Fill the histogram. twig::h1d is a tiny built-in histogram class with an ASCII print(). For anything beyond eyeballing, see Integration — ROOT RDataFrame.

Caching column indices

getInt("charge", row) looks the column up by name. In a hot loop you can resolve the column's integer index once and reuse it, which avoids a name lookup on every access — see Performance — Optimization Tips.

Common next steps

Replace the charge check with a CLAS12-style selection. Three representative cuts:

int pid    = parts.getInt("pid", row);
int status = std::abs(parts.getInt("status", row));
if (pid == 11 && status >= 2000 && status < 4000) {
    // electron detected by the Forward Detector
}
int   pid     = parts.getInt("pid", row);
float chi2pid = parts.getFloat("chi2pid", row);
if (std::abs(pid) == 211 && std::abs(chi2pid) < 3.0f) {
    // pi+ or pi- passing the timing-based PID cut
}
if (parts.getInt("pid", row) == 2212 && parts.getFloat("beta", row) > 0.0f) {
    // proton with a valid TOF-derived beta
}

The CLAS12 status field encodes the detector combination that assigned the particle: |status| in [2000, 4000) is the Forward Detector and [1000, 2000) is the Forward Tagger. The sign distinguishes negative/positive detector assignment in some reconstruction versions — check the CLAS12 analysis note for your pass if the exact sign matters.

Where to go next