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 from the CLAS12 validation dataset.
  • A working hipo4 install (see Installation).

The program

#include "reader.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; }

    hipo::reader reader;
    reader.open(argv[1]);

    // Pull REC::Particle as a banklist so reader.next(list) fills it each event.
    auto list     = reader.getBanks({"REC::Particle"});
    auto& parts   = list[0];

    // Cache column indices once — ~100x faster than repeated name lookups in the
    // inner loop. See Reference / Performance for details.
    const int charge_col = parts.getSchema().getEntryOrder("charge");
    const int vz_col     = parts.getSchema().getEntryOrder("vz");

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

    long events = 0, charged = 0;
    while (reader.next(list)) {
        ++events;
        for (int row = 0; row < parts.getRows(); row++) {
            if (parts.getInt(charge_col, row) == 0) continue;   // keep charged tracks
            hvz.fill(parts.getFloat(vz_col, 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. Open the file. hipo::reader::open() reads the 72-byte file header and the dictionary, so every subsequent reader.next() knows what banks to expect.
  2. Ask for a banklist. reader.getBanks({"REC::Particle"}) returns a hipo::banklist — a vector of hipo::bank objects. Inside the loop, reader.next(list) refills every bank in list from the current event.
  3. Cache column indices. parts.getSchema().getEntryOrder("charge") turns the string name into an integer index once; reusing the integer in the hot loop is roughly 100× faster than passing the string every call. See Performance — Optimization Tips.
  4. Filter charged tracks. parts.getInt(charge_col, 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.

Common next steps

Replace the charge check with a CLAS12-style selection. Three representative cuts, each using cached column indices for the columns they touch:

const int pid_col    = parts.getSchema().getEntryOrder("pid");
const int status_col = parts.getSchema().getEntryOrder("status");

int pid    = parts.getInt(pid_col, row);
int status = std::abs(parts.getInt(status_col, row));
if (pid == 11 && status >= 2000 && status < 4000) {
    // electron detected by the Forward Detector
}
const int pid_col     = parts.getSchema().getEntryOrder("pid");
const int chi2pid_col = parts.getSchema().getEntryOrder("chi2pid");

int   pid     = parts.getInt(pid_col, row);
float chi2pid = parts.getFloat(chi2pid_col, row);
if (std::abs(pid) == 211 && std::abs(chi2pid) < 3.0f) {
    // pi+ or pi- passing the timing-based PID cut
}
const int pid_col  = parts.getSchema().getEntryOrder("pid");
const int beta_col = parts.getSchema().getEntryOrder("beta");

if (parts.getInt(pid_col, row) == 2212 && parts.getFloat(beta_col, 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