Plotting tools for HPS (Heavy Photon Search) analysis. Reads ROOT ntuples produced by hpstr, applies selections, fills histograms, and generates publication-quality matplotlib plots — all driven by a single YAML config file.
cd /sdf/data/hps/users/mgignac/software/2021-ana/plotting
pip install -e .Dependencies: numpy, matplotlib, uproot, awkward, pyyaml
python -m hpsplot examples/config.yaml -vThis reads the YAML config, loads ROOT files, applies selections, fills histograms, and saves plots to the configured output directory.
python -m hpsplot <config.yaml> [-o OUTPUT_DIR] [-v]
positional arguments:
config Path to YAML configuration file
options:
-o, --output-dir Override output directory
-v, --verbose Enable verbose (DEBUG) logging
A config file defines four sections: samples, regions, histograms, and plots.
Each sample points to a directory of ROOT files (or a single file) and specifies how to treat it.
tree: "vtxana_kf_Tight_tree" # default TTree name
samples:
- name: data
directory: /path/to/data/file.root
sample_type: data # data | background | signal
label: "Data"
color: "black"
weight: "1.0" # expression for per-event weight
scale: 1.0 # global scale factor
- name: tridents
directory: /path/to/tridents/ # all *.root files are chained
sample_type: background
label: "Tridents"
color: "#e74c3c"
weight: "weight" # reads the "weight" branch
scale: 42.6directory— path to a single.rootfile or a directory (chains all*.rootfiles viauproot.concatenate)sample_type— controls plotting behavior:datais drawn as points,backgroundis stacked,signalis overlaid as dashed linesweight— any valid expression using branch names (e.g.,"weight * sf")scale— multiplied on top of per-event weights (useful for cross-section normalization)
Regions define event selections using expressions evaluated on branch arrays.
regions:
- name: tight
selection: "ele_p > 0.4 and pos_p > 0.4 and vtx_chi2 < 10"
label: "Tight"
- name: preselection
selection: "" # empty = no cut (all events pass)
label: "Preselection"Define what to plot and how to bin it.
histograms:
- name: vtx_mass
variable: vtx_invM_ # expression to histogram
bins: 50
x_min: 0.0
x_max: 0.2
x_label: "$m_{vtx}$ [GeV]"
y_label: "Events" # optional, defaults to "Events"
log_y: false # optional, defaults to falseThe variable field supports any expression the parser understands (see below).
Plots combine samples, regions, and histograms into output figures.
plots:
# Stacked MC backgrounds + data points + ratio panel
- name: stack_tight
plot_type: stack
histograms: [vtx_mass, vtx_z]
regions: [tight]
samples: [tridents, wab, data]
data_sample: data
signal_samples: [] # optional: overlaid as dashed lines
ratio_y_min: 0.5 # optional, defaults to 0.5
ratio_y_max: 1.5 # optional, defaults to 1.5
# Normalized shape comparison
- name: overlay_comparison
plot_type: overlay
histograms: [vtx_mass]
regions: [tight, loose]
samples: [tridents, wab]
normalize: true # normalize each sample to unit areaPlot types:
| Type | Description |
|---|---|
stack |
Stacked MC backgrounds (filled), data points (markers with error bars), optional signal overlay (dashed), MC stat uncertainty band (hatched), and a data/MC ratio panel |
overlay |
Step histograms for each sample overlaid on the same axes, optionally normalized to unit area |
output_dir: "plots/" # where to save figures
output_format: "pdf" # pdf, png, svg, etc.
tree: "vtxana_kf_Tight_tree" # default TTree name (can be overridden per sample)The selection, variable, and weight fields all use a safe expression parser (no eval()). Supported syntax:
| Feature | Examples |
|---|---|
| Branch names | ele_p, vtx_chi2, vtx_invM_ |
| Arithmetic | ele_p + pos_p, weight * 0.5, x / y |
| Exponentiation | ele_p ** 2 |
| Unary minus | -vtx_Z |
| Comparisons | ele_p > 0.5, vtx_chi2 <= 10, n == 1, flag != 0 |
| Boolean logic | ele_p > 0.4 and pos_p > 0.4, a > 1 or b < 2, not flag > 0 |
| Functions | abs(vtx_Z), sqrt(x), log(y), log10(z), exp(w) |
| Multi-arg functions | max(ele_p, pos_p), min(a, b), pow(x, 2) |
| Trig functions | sin(x), cos(x), tan(x) |
| Parentheses | (ele_p + pos_p) * 2 |
| Constants | 1.0, 3.14, 1e-3 |
plotting/
├── pyproject.toml
├── README.md
├── examples/
│ └── config.yaml
└── src/
└── hpsplot/
├── __init__.py
├── __main__.py # CLI entry point
├── config.py # YAML → dataclasses
├── utils.py # Expression tokenizer + recursive descent parser
├── sample.py # ROOT file reading (uproot.concatenate)
├── region.py # Selection mask evaluation
├── histogram.py # Histogram filling + sqrt(sum(w²)) errors
├── results.py # Processing loop: samples × regions × histograms
└── plotting/
├── __init__.py
├── style.py # HPS matplotlib style
├── stack.py # Stacked MC + data + ratio panel
└── overlay.py # Normalized shape comparisons
- Load YAML config into dataclasses
- For each sample, determine needed branches from histogram variables, region selections, and weight expressions
- Read ROOT files with
uproot.concatenate() - For each region, compute a boolean selection mask
- For each histogram, evaluate the variable and weight expressions on masked data
- Fill
numpy.histogramwithsqrt(sum(w²))error propagation - Generate plots with the HPS matplotlib style