Problem using TTree::Draw with non-simple expressions and cuts

Hi,

I’m trying to use TTree::Draw to draw some histograms with cuts instead of looping over all the entries. I’m hoping TTree::Draw will be faster than looping when I have millions of entries.

Here is a minimal ROOT script that demonstrates my problem (the full code can be copied and pasted into a file called “test.C” and run with root test.C with no modifications).

#include "TTree.h"
#include "TH1.h"
#include "TLorentzVector.h"

// A simple function
bool isPxPositive(TLorentzVector lv) { return lv.Px() > 0 ? true : false; }

void test() {
    // Create tree and two branches of type TLorentzVector
    TTree *tree = new TTree("Tree","Tree");
    TLorentzVector vec1, vec2;
    tree->Branch("vecA",&vec1);
    tree->Branch("vecB",&vec2);

    // Fill the tree with some random TLorentzVectors
    for (float i=0.0;i<1000.0;i++) {
        vec1.SetPtEtaPhiE(sin(i),cos(i),sin(i)*cos(i),sin(i)*sin(i));
        vec2.SetPtEtaPhiE(cos(i),cos(i)*cos(i),sin(i),sin(i)*cos(i));
        tree->Fill();
    }

    // The "simple function" works just fine
    printf("The PtEtaPhiE L.V. defined by (sin(x),cos(x),sin(x)*cos(x),sin(x)^2) with x = 1000 has Px > 0: %s\n", isPxPositive(vec1) ? "true" : "false");

    // This works just fine
    tree->Draw("vecA.Px()>>hist0","vecA.Px()>0","goff");
    tree->Draw("vecA.M()>>hist1","vecA.M()>0","goff");

    // This does not -- I get a "Bad numerical expression", "Variable compilation failed" error
    //
    // Searching for this error on the forums, people suggest substituting in a string, but I cannot
    // do that here for obvious reasons -- this isn't a simple variable I can substitute in; the
    // function itself requires the existence of the argument
    tree->Draw("vecA.Px()","isPxPositive(vecA)","goff");
    tree->Draw("(vecA + vecB).M()","1","goff");

    // The following alternative process works, but it requires I loop over the tree
    //
    // I do this part to mimic getting a tree from a file
    TTree *treecopy;
    treecopy = tree;

    // Define pointers to branches in the tree
    TLorentzVector *vec1copy = 0;
    TLorentzVector *vec2copy = 0;
    treecopy->SetBranchAddress("vecA",&vec1copy);
    treecopy->SetBranchAddress("vecB",&vec2copy);
    // Define histograms
    TH1F *hist2 = new TH1F("hist2","Title",100,0,1);

    for (unsigned i=0;i<treecopy->GetEntries();i++) {
        // vec1copy and vec2copy get populated with the ith tree entry
        treecopy->GetEntry(i);
        // I can do whatever I like with the current vec1copy and vec2copy
        if (isPxPositive(*vec1copy)) { hist2->Fill(vec1copy->Px()); }
    }

}

Essentially, the TTree::Draw function complains if the expression I’m trying to plot or the cut I’m trying to apply is anything but extremely basic, single-line formulae and basic, single-line conditionals on the bare variables.

If you’re wondering why I wrote the “simple function” when I could just apply the cut simply, it’s because in my real code, I have a rather complex set of conditionals that I wrapped inside a function. It’s not reasonable to expect all cuts to have a simple form; there are many variables and calculations at play.

Additionally, I could fill hundreds of branches of type float and int in my tree, corresponding to every possible value I could ever want (for example, instead of having two TLorentzVector branches, I could just have 27 branches, corresponding to pt, eta, phi, energy, mass, rapidity, px, py, and pz for each of particle1, particle2, and particle1 + particle2, so that I don’t have to work with TLorentzVectors and its member functions at all), but this is not an ideal solution.

So unless I can find a way to implement complex expression and complex conditionals in TTree::Draw, I’m stuck either saving hundreds of branches in a simple way, or looping through the events in the tree after its creation.

Thanks in advance for any advice.

Hi,

you can use a “Proxy”.
Here is how you write one

// myProxy.C
TH1D *h1 = nullptr;

void myProxy_Begin(TTree*) {
  // create here histogram
  h1 = new TH1D("h1","h1",100,0,1000);
}

double  myProxy() {
   // put here user code
   for (auto&& track : *tracks) {
      double pt = track.Pt();
      h1->Fill(pt);
   }
   return 0;
}

void myProxy_Terminate() {
  // draw the histogram
  h1->Draw();
}

in order to run it on a tree (see attached file: it contains a trivial tree called events):

root eventsSmall.root
root[0] events->Draw("myProxy.C+")

Danilo