- Introduction
RooUtilClass- The
EventClass - User-Friendly Classes
- Accessing User-Friendly Classes
- Cut Functions
- Common Cut Functions
- Combining Cut Function
- Creating Ntuples From EventNtuple
roodask- Speed Optimizations
- Debugging
- For Developers
RooUtil is a utility class to help analyze the EventNtuple in ROOT-based analysis frameworks. It handles all the ROOT and provides user-friendly classes that allow for coherent looping through parallel branches.
This documentation contains quick references. For additional help:
- see the Analysis Tools Tutorial
- look at the example ROOT macros
The constructor takes two arguments:
filenamecan be the name of a single ROOT file (ending in.root) or a list of ROOT files- (optional)
treenamethe name of the tree
All branches and leaves can be accessed through the Event class like so:
#include "EventNtuple/rooutil/inc/RooUtil.hh"
void PrintEvents(std::string filename) {
RooUtil util(filename);
std::cout << filename << " has " << util.GetNEvents() << " events" << std::endl;
// Now loop through the events
for (int i_event = 0; i_event < util.GetNEvents(); ++i_event) {
auto& event = util.GetEvent(i_event);
// Any single-object branch can be accessed with...
event.branchname->leafname;
// ...and vector-object branches can be looped through like this
for (auto& obj : *(event.branchname)) {
obj.leafname;
}
}
}
However to aid in the complex structure of the ntuple, we also provide helper classes that combine certain branches together.
Because some vector-object branches in the EventNtuple should be paired and looped through coherenetly, we have several user-friendly classes that help with this. They contain the relevent branches from the EventNtuple, often reduced in dimensionality.
The Track class contains all information related to a single track
- single objects:
trk,trkmc,trkcalohit,trkqual,trkpid - vectors:
trksegs,trksegmcs,trksegpars_{lh,ch,kl},trkmcsim,trkhits,trkhitsmc,trkmats,trkhitcalibs
Example: PlotTrackNHits_RecoVsTrue.C
The TrackSegment class contains paired reco and MC information about a single track segment
- single objects:
trkseg,trksegmc,trksegpars_{lh,ch,kl}
Note: that some TrackSegements contain only a trkseg or only a trksegmc
Example: PlotEntranceMomentumResolution.C
The TrackHit class contains paired reco and MC information about a single track hit and also the calib info (if available).
- single objects:
trkhit,trkhitmc,trkhitcalib
Note: In the EventNtuple, the branches are filled such that the first N hits in each correspond one-to-one with each other, and the remainining, unused MC hits complete the trkhitmc branch. The TrackHit class handles this for you.
Example: PlotTrackHitTimesMC.C
The CrvCoinc class contains paired reco and MC information about a single CRV coincidence
- single objects:
crvcoinc,crvcoincmc
Example: PlotCRVPEsVsMCEDep.C
The MCParticle class contains information about a single SimParticle in the genealogy
- single objects:
mcsim
Example: PlotMCParentPosZ.C
The EventNtupleTimeCluster class contains all information related to a single time cluster
- single objects:
timecluster
The CaloCluster class contains all information related to a single calorimeter cluster
- single objects:
calocluster,caloclustermc - vector objects:
calohits,calomcsim,calohitsmc
Examples: PlotCaloClusterEnergy.C, PlotCaloClusterEnergy_RecoVsTrue.C, PlotCaloClusterAndHits.C, PlotCaloCluster_SimParticles.C
Some branches are not contained in any of the above classes:
trkcalohitmccrvdigiscrvpulsesandcrvpulsesmccrvcoincsmcplanecalorecodigis,calodigistrig_branchesmcsteps_virtualdetector
Reach out to the developers on the #analysis-tools Slack channel if you need to have these added somewhere.
The Event class provides access to Tracks, CaloClusters and CrvCoincs:
CountTracks(): counts the number of tracks in the eventGetTracks(): gets the tracks (passes you a copy)CountCaloClusters(): counts the number of calo clusters in the eventGetCaloClusters(): gets the calo clusters (passes you a copy)CountCrvCoincs(): counts the number of CRV coincidences in the eventGetCrvCoincs(): gets the CRV coincidences
The Track class provides access to the TrackSegments, the TrackHits, and the MCParticles:
GetSegments(),CountSegments()GetHits(),CountHits()GetMCParticles(),CountMCParticles()
The CaloCluster class provides access to the CaloHits and the MCParticles:
GetHits(),CountHits()GetMCParticles(),CountMCParticles()
All of these can be passed a cut function (see below) to count / select just a subset of the objects
RooUtil also provides easy ways to select a subset of objects using the above user-friendly classes. A simple C++-style function with the signature:
bool function_name(Object& obj);
can be passed to the various GetObject() and CountObject() functions.
Here is an example using the is_e_minus() function that is defined in EventNtuple/rooutil/inc/common_cuts.hh
#include "EventNtuple/rooutil/inc/RooUtil.hh"
#include "EventNtuple/rooutil/inc/common_cuts.hh"
...
RooUtil util(filename);
for (int i_event = 0; i_event < util.GetNEvents(); ++i_event) {
auto& event = util.GetEvent(i_event);
auto& all_tracks = event.GetTracks();
auto& e_minus_tracks = event.GetTracks(is_e_minus); // gives you a vector of tracks that are only used the e-minus fit hypothesis
}
Many cuts are already available in ```common_cuts.hh`` and can be easiliy listed with:
rooutilhelper --list-available-cuts
Feel free to add to common_cuts.hh and put in a pull request on GitHub. Some notes on the file:
- We use the following special characters for the cuts to be printed nicely with
rooutilhelper://+gives the cut section headingbool function_name(args) // explanationensures the cut name has an explanation printed too
There are two ways to combine cut functions:
- Write a new one yourself that uses already existing functions e.g:
// before your main function
bool my_cut(const Track& track) {
return good_track(track) && is_e_minus(track);
}
...
// in your main function's event loop
int n_e_minus_good_tracks = event.CountTracks(my_cut);
- or, you can combine cuts in a lambda function (similar to python):
int n_e_minus_good_tracks = event.CountTracks([](const Track& track){ return is_e_minus(track) && good_track(track); });
RooUtil can also be used to create reduced EventNtuples. You create the output ntuple with RooUtil::CreateOutputEventNtuple() and fill it in your event loop with RooUtil::FillOutputEventNtuple(). The created reduced EventNtuple is compatible with RooUtil again. See example here: CreateNtuple.C
If you want to also remove tracks from the event, you should use SelectTracks(cut) instead of GetTracks(cut). Note that this changes the Event so if you want to get the whole event again, you should use RooUtil::GetEvent() again.
It's also possible to use RooUtil to create a new ntuple with a different structure (e.g. one entry per track). See an example in CreateTrackNtuple.C for how this can be done
roodask allows you to run a RooUtil-based macro or program in parallel over multiple files using dask. Here we present two examples: running a ROOT macro from the examples folder and running the RooCount reference analysis.
We need to set up the environment and get a filelist:
cd /to/your/work/area/
mu2einit
muse setup AnalysisMusingMDC2025
pyenv ana
mkdir filelists
metacat query files from mu2e:nts.mu2e.ensembleMDS3aMix1BBTriggered.MDC2025-001.root | mdh print-url -l disk -s path - > filelists/nts.mu2e.ensembleMDS3aMix1BBTriggered.MDC2025-001.root.list
You will also need to write a "manifest" JSON file. We will use the following examples:
- for running a ROOT macro use this manifest file
- for running a program use this manifest file
This will run the example macro: PlotEntranceMomentumResolution_roodask.C
roodask --manifest EventNtuple/rooutil/roodask/macro_manifest.json --filelist filelists/nts.mu2e.ensembleMDS3aMix1BBTriggered.MDC2025-001.root.list --n-workers 2 --threads-per-worker 1 --max-files=3
This will produce an output/ directory with an output file per job
- you can add the
--hadd merged.rootoption if you want an hadded output (if you want to runhaddwith multi-threading add--hadd-joption - you can run a program on the hadded file with the
--post-haddoption (see Example 2 for a concrete example)
This will run the RooCount RefAna:
roodask --manifest EventNtuple/rooutil/roodask/refana_manifest.json --filelist filelist.txt --n-workers 2 --threads-per-worker 1 --max-files=3 --hadd merged.root --post-hadd 'RooCountAna {merged} MDS3a'
This will produce a single output file (output/merged.root) as well as run the RooCountAna program on the merged output
- There is a dedicated README for technical details
- There is a
--scheduleroption that will be important when we get things up and running on EAF
By default, RooUtil will read all the branches for every entry. If you are finding that this is too slow, then you can explicity turn on only the branches that you will be reading. This can increase the speed by as much as a factor of 10.
RooUtil util(filename);
util.TurnOffAllBranches(); // first turn all branches off
util.TurnOnBranches(std::vector<std::string>{"branch1", "branch2"});
Debugging output can be turned on like this:
RooUtil util(filename);
util.SetDebug(true);
Beware: there are a lot of debug messages.
If a new branch is added to the EventNtuple, then the following needs to be done to so that RooUtil can access the branch:
- In Event.hh add the pointers at the bottom of the file
- In Event.hh constructor, set the branch address using the
CheckForBranch()function - In Event.hh add the #include to the underlying info struct
- In RooUtil.hh add it to the
CreateOutputEventNtuple()function - Add to validation places:
- PrintEvents.C: at least the first and last leaf in the struct
- create_val_file_rooutil.C
- copy contents of struct into place where histograms are defined
- then copy and replace "type " with "TH1F* h_branchname_" (e.g. "float " with "TH1F* h_trkcalohit_")
- then copy in " = new TH1F("h_branchname_", "", 100,0,100); //" after the histogram name
- delete line after "//"
- add in leaf names to histname (and make separate x, y, z histograms for XYZVectorF leaves)
- copy histogram lines into main loop
- search and replace
TH1F*(note space) with nothing - search and replace
= new TH1F("h_with->Fill( - search and replace
", "", 100,0,100);with); - search and replace
(branchname_with(branchname.in theFill()commands - update histogram ranges
- (Optional) If appropriate, add branches to other classes (e.g. Track.hh) and to
Event::Update()- be sure to test it with an example script
- Add to this documentation