Tutorial III: Reading ROOT Input - artus-analysis/Artus GitHub Wiki
This tutorial is based on the ROOT files generated in the first step.
-
harry.py -i gaussians.root gaussians1000.root -f gaussians -x var0
-
harry.py -i gaussians3.root -f gaussians1 gaussians2 -x var0
-
harry.py -i gaussians3.root -f gaussians1 -x var0 var1
The ROOT function TTree::Draw
offers a possibility to execute C++ macros when processing trees. These macros need to be created by the function TTree::MakeProxy
. HarryPlotter supports this mechanism and takes care of creating and compiling this code. Users can use this mode with the setting --tree-draw-options proxy
and provide code at -x
(not -y/-z
) and -w
to provide for the extraction of float values to be plotted and float values for weights and cuts. Note, that the original ROOT functions only support bool cuts, but HarryPlotter modifies this to support also float weight. HarryPlotter creates files called proxy_*
in the current directory, which can only be deleted after the last plot in a given HarryPlotter (multiplot) run is finished. This is currently not so easy to implement and therefore files have to be deleted manually afterwards (rm proxy_*
). Reading from ROOT trees with proxies always requires an existing histogram to fill. This is why HarryPlotter always requires --x-bins
to be specified together with --tree-draw-options proxy
.
There are two important use cases for this option:
-
A frequent use case is a calculation based on LorentzVector (LV) objects. As an example, a tree containing two LV-type objects is considered here:
harry.py -i <file.root> -f <path/to/tree> -q | grep -e lep.LV
outputs
lep1LV (ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<float> >) lep2LV (ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<float> >)
Then the mass of the system of the two LV's can be plotted with
harry.py -i <file.root> -f <path/to/tree> --tree-draw-options proxy --x-bins <binning> \ -x "((*lep1LV.obj.GetPtr())+(*lep2LV.obj.GetPtr())).M()"
The expression
*lep1LV.obj.GetPtr()
is needed in order to access theROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<float> >
type object, which is wrapped like this by theTTree::MakeProxy
function. This is equivalent to filling LV vector objects on-the-fly:harry.py -i <file.root> -f <path/to/tree> --tree-draw-options proxy --x-bins <binning> \ -x "(ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<float>>(lep1LV->Pt(), lep1LV->Eta(), lep1LV->Phi(), lep1LV->M()) + ROOT::Math::LorentzVector<ROOT::Math::PtEtaPhiM4D<float>>(lep2LV->Pt(), lep2LV->Eta(), lep2LV->Phi(), lep2LV->M())).M()" \ --proxy-prefixes "#include <Math/LorentzVector.h>\n#include <Math/PtEtaPhiM4D.h>"
In this example is is necessary to tell HarryPlotter to import two header files in the proxy code. Similarly, one could plot the
Delta R
between these LV's:harry.py -i <file.root> -f <path/to/tree> --tree-draw-options proxy --x-bins <binning> \ -x "ROOT::Math::VectorUtil::DeltaR((*lep1LV.obj.GetPtr()), (*lep2LV.obj.GetPtr()))" \ --proxy-prefixes "#include <Math/VectorUtil.h>"
-
The parameter
--proxy-prefixes
can take any C++ code, that can be added to the header file created by theTTree::MakeProxy
function. A common use case for the need of more complicated structures could be the case, when scale factors or weights have to be read in from external histograms based on values of branches in a tree while iterating over this tree. A solution could be to write a class which reads in the histograms and provides the scale factors:#pragma once #include <string> #include <TFile.h> #include <TH1.h> class ScaleFactors { public: ScaleFactors(std::string filename) { // read in histogram(s) here // histogram = ... } ~ScaleFactors() { delete histogram; histogram = nullptr; } double GetScaleFactor(double pt) { double scaleFactor = 1.0; // find bin corresponding to "pt" in the histogram // extract and return the scale factor from bin content(s) return scaleFactor; } private: TH1* histogram = nullptr; };
Then a possible plotting command could look like this:
harry.py -i <file.root> -f <path/to/tree> --tree-draw-options proxy --x-bins <binning> \ -x ... -w scaleFactors.GetScaleFactor(lep1LV->Pt())" \ --proxy-prefixes "#include <path/to/header.h>\nScaleFactors scaleFactors(\"path/to/file.root\");"
In case it is necessary to read in more than one object based on an instance of the same class, one can put
HASH_NAME
into the names of variables in order to make them unique and avoid compiler problems.harry.py -i <file.root> -f <path/to/tree> --tree-draw-options proxy --x-bins <binning> \ -x ... -w scaleFactors_HASH_NAME.GetScaleFactor(lep1LV->Pt())" \ --proxy-prefixes "#include <path/to/header.h>\nScaleFactors scaleFactors_HASH_NAME(\"path/to/file.root\");"
In case of problems it is advised to look at the corresponding header (proxy_*.h
) and source (proxy_*.C
) files created by HarryPlotter. Usually compiler problems are solvable after understanding the problem in these files containing C++ code.
The code is compiled by ROOT's internal compiler. Therefore it might be, that not all standard C++ features are available. Especially linking against external libraries might be challenging.