My Analysis Tools

This page contains the documentation for my analysis code in UserCode/musella/Tools. The library can be downloaded and compiled as:

cd /src
export CVSROOT=:gserver:cmscvs.cern.ch:/cvs_server/repositories/CMSSW
cvs co -d musella/Tools UserCode/musella/Tools
scramv1 b -j 4 
A ROOT dictionary containing the most important classes is generated at compile time (see here). This can be used to load the classes in CINT and and python.
from ROOT import gSystem 
gSystem.Load('${CMSSW_BASE}/lib/${SCRAM_ARCH}/libmusellaTools.so')
from ROOT import Event, Photons, Electrons, NtupleAnalyzer 

Generic NTuple reading

The most efficient way to save complex objects into a ROOT is to use dictionaries. In this way the serialization/deserialization process is left to the framework and the efforts on the user side are reduced to the creation of classes and dictionaries. In this case, however, all users need to share the dictionaries and care has to be taken to ensure backward compatibility of the produced trees.

For these and other reasons physicists often end up producing plain ntuples. Also, in big collaborations, ntuples are often produced by analysis groups and different groups usually use different ntuples. This is why, I ended up developing a set of classes that would allow to read different type of ntuples and present them through the same interface, leaving the downstream analysis code unchanged.

The solution I came up with is probably not very elegant nor extremely efficient, but it is very flexible and easy to use.

  • The Event class manages the booking of branches and the looping over the tree. It class contains the Collection objects and allows to read only a subset of the events.
    Usage example:
// event loop 
TFile * file = TFile::Open("<some file>"); 
TTree * tree = file-&gt;Get("<some tree>"); 
Event ev(tree); 
for( ev.toBegin(); !ev.atEnd(); ev.next() ){ 
      Photons & photons = ev.photons(); 
      if( photons.size() > 0 ) { std::cout << photons[0].pt() << endl; 
} 
Show more examples... (Hide)
// Loop over 1/3 of the events. 
std::vector<int> keep_first(1,0); 
ev.prescale(3,keep_first); 
for( ev.toBegin(); !ev.atEnd(); ev.next() ){ 
      std::cout << (int) ev << std::endl; 
}

// Read the other 2/3 of the events. 
std::vector<int> keep_others; keep_others.push_back(1); keep_others.push_back(2); 
ev.prescale(3,keep_others); 
for( ev.toBegin(); !ev.atEnd(); ev.next() ){ 
      std::cout <<; (int) ev << std::endl; 
} 
  • The Collection and Particle classes are used to access the content of the tree branches.
    The Collection class a aggregates several tree branches into a coherent structure and keeps track of selected candidates.
    The Particle class indexes the Collection class and presents the value of all branches as class methods.
Particle p = collection[0]; 
cout << p.energy() - c.energy(0) << std::endl; 
The buffers used to read the tree branches are contained in the Collection class, which binds the buffers to a configurable set ofdefined, that can be used to read event branches. The branch names can be fully specified by the user at run time through specific methods.
class Collection { 
public: 
      //! Define branch names 
      void branchName(const std::string & name, const std::string & val) { branchNames_[name] = val; } 
      //! Common prefix for branch names 
      void prefix(const std::string & pfx) { prefix_ = pfx; }; 
}

ev.photons().branchName("energy", "energy"); 
ev.photons().branchName("eta", "eta"); 
ev.photons().branchName("phi", "phi"); 
A few macro allow to easily add branches to the Collection class. Show the code... Hide the code
// Define buffers and getters 
class Collection { 
public: 
      COLL_GETTER_MULT(energy, Float_t, escale_ ); 
      COLL_GETTER(eta , Float_t ); 
      COLL_GETTER(phi , Float_t ); 
}

// Define particle getters 
class Particle { 
public: 
      PART_GETTER(energy, Float_t, c_ );
      PART_GETTER(eta, Float_t, c_ ); 
      PART_GETTER(phi, Float_t, c_ ); 
}

// Branch binding 
void Collection::getBranches(Event & ev) { 
      GET_BRANCH(energy); 
      GET_BRANCH(eta); 
      GET_BRANCH(phi); 
} 

NTuple analyser

The analysis code is supposed to be contained in classes denominated NTuple analyzers. A base class called NTupleAnalyzer implements all the basic blocks needed to loop over events.

The class Implements the loop over datasets and events, histograms bookkeeping and basic event selection. It provides hook for the analysis code defining virtual methods to be overridden by inheriting classes.

// Analyzer
class MyAnalyzer : public NtupleAnalyzer {
begin:
      void begin() {
            openOutput( "myHistoFile.root" );
            bookHistogram("bx_id","Bunch crossing id",0xdec,1,0xdec+1);
      }

      virtual void analyze(Event &e) {
            histogram("bx_id")->Fill(e.bx());
      }

      void end() {
           closeOutput();
      }
}
  • Loop over events.
// n.events to analyze per dataset (-1 for all)
int maxEvent = 100;
// Event handling object
Event ev;
// Fill list of datasets
std::vector<Dataset *> myDatasets;
myDatasets.push_back( <dataset 1> );
myDatasets.push_back( <dataset 2> );
...

// Instantiate analyzer
MyAnalyzer myAnalyzer;

// Loop over datatsers
myAnalyzer.analyze(myDatasets,&ev,maxEvent);

Helper classes

A few helper classes can be used in association with the NtupleAnalyzer class, to build analyses.

class MyAnalyzer : public NtupleAnalyzer {
begin:
      AutoPlot myPlotter_;

      begin() {
             openOutput("myHistos.root");
             // define plots
             vector<string> plots;
             plots.push_back("bx:(0xdec:1:0xded):Bunch crossing; bx; events");
             plots.push_back("metEt:(200:0:200):MET; MET [GeV]; Events/GeV");
             myPlotter_.setPlots(plots);
             // allocate functors
             myPlotter_.makeFormulas();
             // add to the list of plotters
             plotters_.push_back(&myPlotter_);
      }

      void beginDataset() {
            // Book histograms for all categories
            myPlotter_.bookHistograms("");
            myPlotter_.bookHistograms("cat1");
            myPlotter_.bookHistograms("cat2");
      }

      virtual void analyze(Event &e) {
           float weight = unweigh_ ? 1. : e.getTree()->GetWeight();
           if( eventSelected ) {
                   // define which categories should be filled for this event
                   vector<string> categories(1,"");
                   if( cat1 ) categories.push_back("cat1");
                   if( cat2 ) categories.push_back("cat2");
                   // fill all plots
                   myPlotter_.fillPlots(weight, categories);
           }
      }

      void end() {
           closeOutput();
      }
}
  • The PhotonSelector class can be used to define a set of selection criteria for photon objects. The class keeps track of which selection criteria are satisfied and allows to define groups of selection criteria and study N-M groups of cuts.

Sample weighing

The Dataset class is used to manage different dataset with different weights. The weight of a simulated sample is calculated through the formula $ w_i = \sigma_i / N_i \cdot L $ where $\sigma_i$ is the cross section of the simulated process, $N_i$ is the number of simulated events and $L$ the luminosity being simulated. The class inherits from the TChain class and provides weights trough the standard GetWeight() method from the TTree class, thus in a completely transparent way for the analysis code. The relevant methods of the class are shown below. Show the code.... Hide the code.

class Dataset : public TChain {
public:
        // Add file to dataset
	void AddFile(const char* fname, double xsec, TString processId="", double minScale=-1., double maxScale=1.e+99, 
		     Long64_t nentries = kBigNumber, const char* tname = "");

        // Set equiv. lumi for weight calculation
	void setRefLumi(double lumi) { refLumi_ = lumi; };
	
        // Define event scale expression (uses TTreeFormula)
	void eventScale(const char * formula);
	
        // Recalculate weights
	void reweight();
	
        // Get weight for current event
	virtual Double_t GetWeight() const;
}

Static web pages

Static web pages can be generated using the HtmlHelper class.

HtmlHelper helper("<output director>");

helper.body().add(new HtmlTag("h1")) << "Usage example of the HtmlHelper class";
helper.body().add(new HtmlTag("p")) << "This will generate a static page with one plot.";

TCanvas * canv  = new TCanvas();
// generate the plot
....
helper.body().add(new HtmlPlot(canv)).caption("This is the plot");

helper.dump();

Cut optimization

http://cmssw.cvs.cern.ch/cgi-bin/cmssw.cgi/UserCode/musella/Tools/interface/Optimizer.h?view=markup

Template fitting

http://cmssw.cvs.cern.ch/cgi-bin/cmssw.cgi/UserCode/musella/Tools/interface/Purity.h?view=markup

http://cmssw.cvs.cern.ch/cgi-bin/cmssw.cgi/UserCode/musella/Tools/interface/purity_fit.h?view=markup

Edit | Attach | Watch | Print version | History: r6 < r5 < r4 < r3 < r2 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r6 - 2011-01-24 - PasqualeMusella
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    Main All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright &© 2008-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
or Ideas, requests, problems regarding TWiki? use Discourse or Send feedback