TLorentzVector class methods in TTree::Draw(), TFormula

ROOT Version: 6.24/06
Platform: CentOS7
Compiler: gcc probably, it’s from /cvmfs/atlas.cern.ch/repo/sw/software/23.0/sw/lcg/releases/LCG_101_ATLAS_26/ROOT/6.24.06a/x86_64-centos7-gcc11-opt/bin/root


Hello experts,

I’m currently working with some DAOD files in xAOD from Athena 23.0.9.

The salient detail is that I (interactively) load a TTree, grab some particles from a branch, get their momentum as a TLorentzVector using the p4() method, and then try to use the TLorentzVector methods:

root output/DAOD_TRUTH3.600_600.output.pool.root
#include "xAODRootAccess/Init.h"
#include "xAODRootAccess/MakeTransientTree.h"
xAOD::Init();
TTree *t0 = xAOD::MakeTransientTree(_file0);
TCut cut_C1 = "TruthBSMWithDecayParticles.absPdgId()==1000024 && TruthBSMWithDecayParticles.child(0).absPdgId()!=1000024";

If I run the following line and use the Phi() method, it works just fine and draws a histogram of the particle \phi:

t0->Draw("TruthBSMWithDecayParticles.child(0).p4().Phi()", cut_C1);

But if I run this line using the DeltaPhi() method instead, I get an error:

t0->Draw("TruthBSMWithDecayParticles.child(0).p4().DeltaPhi(TruthBSMWithDecayParticles.child(1).p4())", cut_C1);
TTreeFormula::DefinedV... ERROR   Unknown method:child(0).p4().DeltaPhi(TruthBSMWithDecayParticles.child(1).p4()) in TLorentzVector
TTreeFormula::Compile     ERROR    Bad numerical expression : "TruthBSMWithDecayParticles.child(0).p4().DeltaPhi(TruthBSMWithDecayParticles.child(1).p4())"
TSelectorDraw::AbortPr... INFO    Variable compilation failed: {TruthBSMWithDecayParticles.child(0).p4().DeltaPhi(TruthBSMWithDecayParticles.child(1).p4()),TruthBSMWithDecayParticles.absPdgId()==1000024 && TruthBSMWithDecayParticles.child(0).absPdgId()!=1000024}

Clearly TTree::Draw recognizes some TLorentzVector method, but I don’t understand the Unknown method error message here (why does it rewind all the way to child?).


I have another option, which is to try using ROOT::Math::LorentzVector methods instead, since I can get a ROOT::Math::LorentzVector<ROOT::Math::PxPyPzEVector> from the genvecP4() method, but I can’t seem to get TTree::Draw to recognize the DeltaR method either:

t0->Draw("ROOT::Math::VectorUtil::DeltaR(TruthBSMWithDecayParticles.child(0).p4().genvecP4(),TruthBSMWithDecayParticles.child(1).genvecP4())", cu
TTreeFormula::Compile     ERROR    Bad numerical expression : "ROOT::Math::VectorUtil::DeltaR(TruthBSMWithDecayParticles.child(0).p4().genvecP4(),TruthBSMWithDecayParticles.child(1).genvecP4())"
TSelectorDraw::AbortPr... INFO    Variable compilation failed: {ROOT::Math::VectorUtil::DeltaR(TruthBSMWithDecayParticles.child(0).p4().genvecP4(),TruthBSMWithDecayParticles.child(1).genvecP4()),TruthBSMWithDecayParticles.absPdgId()==1000024 && TruthBSMWithDecayParticles.child(0).absPdgId()!=1000024}

Ugh. I see this message from a decade-and-a-half ago saying that TTree::Draw cannot call functions whose arguments are non-numerical. Is this still true this many years later?
EDIT: Seems to be also true 8 years ago… what on earth is a TTree Proxy? I’m supposed to shove this into an external script somewhere?

I guess it is still the case but @pcanal can confirm.

Hi @rmcgover ,

How about using RDataFrame?

#include "xAODRootAccess/Init.h"
#include "xAODRootAccess/MakeTransientTree.h"
#include <ROOT/RDatFrame.hxx>
xAOD::Init();
TTree *t0 = xAOD::MakeTransientTree(_file0);
ROOT::RDataFrame df{*t0};
auto h = df.Filter("TruthBSMWithDecayParticles.absPdgId()==1000024 && "
                   "TruthBSMWithDecayParticles.child(0).absPdgId()!=1000024")
           .Define("deltar", "ROOT::Math::VectorUtil::DeltaR("
                             "TruthBSMWithDecayParticles.child(0).genvecP4(),"
                             "TruthBSMWithDecayParticles.child(1).genvecP4())")
           .Histo1D("deltar");
h->Draw();

This should be a good starting point for your case, hope it helps.

Cheers,
Vincenzo

Hi Vincenzo,

Thanks for the template! That got me a lot closer than anything else so far.

#include <ROOT/RDataFrame.hxx>
#include "xAODRootAccess/Init.h"
#include "xAODRootAccess/MakeTransientTree.h"
xAOD::Init();
TTree *t0 = xAOD::MakeTransientTree(_file0);
ROOT::RDataFrame df{*t0};
TCut cut_C1 = "TruthBSMWithDecayParticles.absPdgId()==1000024 && TruthBSMWithDecayParticles.child(0).absPdgId()!=1000024";
auto h_deltaR_hC1_l = df
    .Filter(cut_C1.GetTitle())
    .Define("deltaR_hC1_l",
        "ROOT::Math::VectorUtil::DeltaR("
        "TruthBSMWithDecayParticles.child(0).genvecP4(),"
        "TruthBSMWithDecayParticles.child(1).genvecP4())"
    )
    .Histo1D(
        "h_deltaR_hC1_l", "", 40, 0, 10}, "deltaR_hC1_l"
    );

The above gets most of the way to compiling (I can see a lambda that looks correct in the error message), but then I get an error with xAOD types:

In file included from libxAODJetDict dictionary payload:109:
In file included from /cvmfs/atlas.cern.ch/repo/sw/software/23.0/Athena/23.0.9/InstallArea/x86_64-centos7-gcc11-opt/include/xAODJet/Jet.h:12:
In file included from /cvmfs/atlas.cern.ch/repo/sw/software/23.0/Athena/23.0.9/InstallArea/x86_64-centos7-gcc11-opt/include/xAODJet/versions/Jet_v1.h:18:
In file included from /cvmfs/atlas.cern.ch/repo/sw/software/23.0/Athena/23.0.9/InstallArea/x86_64-centos7-gcc11-opt/include/xAODBase/IParticleContainer.h:12:
/cvmfs/atlas.cern.ch/repo/sw/software/23.0/Athena/23.0.9/InstallArea/x86_64-centos7-gcc11-opt/include/AthContainers/DataVector.h:782:42: error: incomplete type 'DataVectorBase<xAOD::TruthParticle_v1>' named in nested name specifier
template <class T, class BASE = typename DataVectorBase<T>::Base>
                                         ^~~~~~~~~~~~~~~~~~~
input_line_172:2:19: note: in instantiation of default argument for 'DataVector<xAOD::TruthParticle_v1>' required here
auto lambda0 = [](DataVector<xAOD::TruthParticle_v1>& var0){return var0.absPdgId()==1000024 && var0.child(0).absPdgId()!=1000024
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Forward declarations from /cvmfs/atlas.cern.ch/repo/sw/software/23.0/Athena/23.0.9/InstallArea/x86_64-centos7-gcc11-opt/lib/Athena.rootmap:369:19: note: forward declaration of 'DataVectorBase<xAOD::TruthParticle_v1>'
template <> class DataVectorBase<xAOD::TruthParticle_v1>;
                  ^
terminate called after throwing an instance of 'std::runtime_error'
  what():
RDataFrame: An error occurred during just-in-time compilation. The lines above might indicate the cause of the crash
 All RDF objects that have not run an event loop yet should be considered in an invalid state.

I tried to #include "AthContainers/DataVector.h" but that seemed to make things worse, needing me to further #include "AthContainers/AthContainers/OwnershipPolicy.h" which also failed and I’m afraid I’m in dependency hell somehow.
EDIT: Actually the above lambda is probably NOT correct, TTreeFormula is smart enough to iterate the vector and evaluate the expression over the individual truth particles, but I don’t think RDataFrame is?
EDIT2: Looks like I’m not the only one with this problem, but I’m not a huge fan of the solutions[1, 2], and I’m having a hard time getting RDataFrame to recognize xAOD’s DataVector as a vector…

Dear @rmcgover ,

If the problem is just iterating over the DataVector, maybe a bit more explicit usage of the class will work


bool data_vector_filter(const DataVector<xAOD::TruthParticle_v1>& vec)
{
    return std::all_of(
        vec.begin(),
        vec.end(),
        [](const xAOD::TruthParticle_v1 &p){
            return (p.absPdgId()==1000024 && p.child(0).absPdgId()!=1000024);
        }
    );
}
[...]
auto h_deltaR_hC1_l = df
    .Filter(data_vector_filter, {"TruthBSMWithDecayParticles"})
// continue 

I am not familiar with xAOD types, so I may have misused DataVector or TruthParticle above, I hope it still gives the idea and it helps too.

Regarding the incomplete type errors, you surely need the proper headers included in the program at all times.

EDIT: moved the function above for hopefully easier reading

Cheers,
Vincenzo

Hi all,

I did eventually manage to get this working with RDataFrame, although it’s hardly elegant (I’d prefer to just store pointers to the particles or something, but ROOT complains about a “missing dictionary” for my “custom column” and I’m sick of blindly trying to include headers and invoking gInterpreter to fix it).

The code is essentially as follows:

#include <ROOT/RDataFrame.hxx>
#include "xAODRootAccess/Init.h"
#include "xAODRootAccess/MakeTransientTree.h"
// Setup xAOD
xAOD::Init();
// Define cuts and variables
using DVTP = DataVector<xAOD::TruthParticle_v1, DataVectorBase<xAOD::TruthParticle_v1>::Base>;
auto def_idx_C1 = [](DVTP& tbsmwdp) {
    // Index of chargino in TruthBSMWithDecayParticles
    for (int i = 0, n = tbsmwdp.size(); i < n; ++i) {
        if (tbsmwdp.at(i)->absPdgId() == 1000024
            && tbsmwdp.at(i)->child(0)->absPdgId() != 1000024
        ) return i;
    } return -1;
};
auto def_idx_hC1 = [](DVTP& tbsmwdp, int idx_C1) {
    // Child index of Higgs from chargino
    auto C1 = tbsmwdp.at(idx_C1);
    for (int i = 0, n = C1->nChildren(); i < n; ++i) {
        if (C1->child(i)->isHiggs()) return i;
    } return -1;
};
auto def_idx_l = [](DVTP& tbsmwdp, int idx_C1) {
    // Child index of lepton from chargino
    auto C1 = tbsmwdp.at(idx_C1);
    for (int i = 0, n = C1->nChildren(); i < n; ++i) {
        if (C1->child(i)->isChLepton()) return i;
    } return -1;
};
// Load data
TTree *t0 = xAOD::MakeTransientTree(_file0);
ROOT::RDataFrame df{*t0};
auto df_derived = df\
    .Define("idx_C1",  def_idx_C1,  {"TruthBSMWithDecayParticles"})\
    .Define("idx_hC1", def_idx_hC1, {"TruthBSMWithDecayParticles", "idx_C1"})\
    .Define("idx_l",   def_idx_l,   {"TruthBSMWithDecayParticles", "idx_C1"});
// Make histogram
auto h_deltaR_hC1_l = df.derived\
    .Histo1D(
        "h_deltaR_hC1_l", "", 40, 0, 10}, "deltaR_hC1_l"
    );
hist->Draw("E P");

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.