Clone tree, add branches and save new tree

Hi all

I have a root file with a tree, I want to clone that tree, use some of the variables stored there, calculate something and then save a new root file with the new tree including the new variables.
I think I managed to do it local, but when I tried to run in a batch system I am getting or an empty new file, or different sets of errors (mostly regarding the stored of the branch).

Here is a simplify version of my code:

#include <iostream>
#include <limits>
#include <sys/time.h>

#include "TTree.h"
#include "TChain.h"
#include <TROOT.h>
#include <TFile.h>
#include <TTreeReader.h>
#include <TTreeReaderValue.h>
#include <TTreeReaderArray.h>

using namespace std;

typedef vector<TLorentzVector> TLorentzVectors;
typedef vector<double> doubles;

const TLorentzVector makeVector(double pt, double eta, double phi, double mass) {
        TLorentzVector lv;
        lv.SetPtEtaPhiM(pt, eta, phi, mass);
        return lv;
}

int main(int argc, char *argv[]) {

        auto oldFile = TFile::Open( "inNameFile.root" );
        TTree *newtree = (TTree*)oldFile->Get("tree");
        TTreeReader newreader( "tree", oldFile );

        TFile *newfile = new TFile("outNameFile.root","recreate");

   TTreeReaderValue<Int_t> nleps = {newreader, "nleps"};
   TTreeReaderArray<Float_t> leps_pt = {newreader, "leps_pt"};
   TTreeReaderArray<Float_t> leps_eta = {newreader, "leps_eta"};
   TTreeReaderArray<Float_t> leps_phi = {newreader, "leps_phi"};
   TTreeReaderArray<Float_t> leps_mass = {newreader, "leps_mass"};

        double newVar;
        newtree->Branch("newVar", &newVar, "newVar/F");
        TH1F hnewVar("newVar", "newVar", 10, 0, 1);

        int dummy=0;
        while (newreader.Next()) {

              if ( *nleps == 1  ) {

                      if ((++dummy)==100) break;
                      TLorentzVectors leptons;
                      TLorentzVector lepton = makeVector( leps_pt[0], leps_eta[0], leps_phi[0], leps_mass[0] );
                      newVar = lepton.M();  /// for instance, the variable is not important
                      hnewVar.Fill( lepton.M() );
              } else {
                      newVar = -999;
              }
              newtree->Fill();
        }

        newfile->cd();
        newtree->Write();
        hnewVar.Write();
        newfile->Close();

        //  return 0;
}

Is it something that I am doing wrong? I found it weird that running local it works fine.
This is the error that I am getting now:

Error in <TBranch::TBranch::WriteBasketImpl>: basket's WriteBuffer failed.

Error in <TBranch::TBranch::Fill>: Failed to write out basket.

Error in <TTree::Fill>: Failed filling branch:tree.jets_pt, nbytes=-1, entry=1938
 This error is symptomatic of a Tree created as a memory-resident Tree
 Instead of doing:
    TTree *T = new TTree(...)
    TFile *f = new TFile(...)
 you should do:
    TFile *f = new TFile(...)
    TTree *T = new TTree(...)
Error in <TBranch::TBranch::WriteBasketImpl>: basket's WriteBuffer failed.


cheers,


ROOT Version: 6.10/09
Platform: Linux
Compiler: Not Provided


1 Like

Hi,

I propose a different approach to achieve what you want, based on RDataFrame: would that work for you?

Cheers,
D

Thanks @Danilo. Sure, I am not attached to the code that I shown :smiley:

This is great.
Try to go through the page linked in the post above. If this works for you we are here to help.

Cheers,
D

@Danilo ok, I took a quick look, but besides been a nice, more simplify way to access the data and apply some selections. I dont see how this can solved my problem. Perhaps can you provide a short example?
Also because for simplicity in my example, I stored the mass of a TLorentzVector, but in principle I need to load other classes and calculate some other more complicated things.
Thanks

I am preparing a proposal.

There it is (quickly written, has a lot of room for improvement):

// ROOT::EnableImplicitMT(); // uncomment to run in parallel mode
using TLorentzVectors = vector<TLorentzVector>;
using doubles = vector<double>;
using crvecRef = const ROOT::VecOps::RVec<double>&;

const TLorentzVector CalcInvMass(double pt, double eta, double phi, double mass) {
        TLorentzVector lv;
        lv.SetPtEtaPhiM(pt, eta, phi, mass);
        return lv;
}

doubles CalcInvMasses(int nParts, crvecRef pts, crvecRef etas, crvecRef phis, crvecRef masses)
{
   doubles masses(nParts);
   for (auto i : ROOT::TSeqU(nParts)) {
      masses[i] = CalcInvMass(pts[i], etas[i], phis[i], masses[i]);
   }
   return masses;
}

ROOT::RDataFrame rdf("tree", "inNameFile.root");
auto rdf1 = rdf.Define("leptonMasses", CalcInvMasses, {nleps, leps_pt, leps_eta, leps_phi, leps_mass});
auto newDf = rdf1.Snapshot("tree", "outNameFile.root", {"leptonMasses"});
auto newHist = rdf1.Histo1D("leptonMasses");

Cheers,
D

Thanks you so much @Danilo for the help. However, I tried to quickly implement your test and I am having problems with the RDataFrame library. I used:

#include "ROOT/RDataFrame.hxx"

but I am getting this error:

fatal error: ROOT/RDataFrame.hxx: No such file or directory
 #include "ROOT/RDataFrame.hxx"

Try the latest ROOT release.

Thanks @Wile_E_Coyote for the answer. This means that RDataFrame is only available in the latest version of root?
Then perhaps is there another solution for my problem?
thanks

grep -r CloneTree ${ROOTSYS}/t[eu]*

But, see this thread and some notes about various flavours of automatically generated “analysis skeletons” .

Thanks @Wile_E_Coyote, I am looking at the files and links that you provide. Just to confirm it, since I am planning to use the Tree::CloneTree() I have to use MakeProxy for the analysis skeleton, correct?

None of the tutorials, which use CloneTree, requires MakeProxy. TTreeReader should also be fine but I guess we need @pcanal to confirm it.

If I were you, I would really try to switch to ROOT 6.14.

I would love to change to ROOT 6.14 but other libraries that I need to load to run other calculations are based on previous versions of root. Also, I cannot load these version in the machine from my university. (unless there is a way that I am not aware of).

No, you do not have to use MakeProxy.

        auto oldFile = TFile::Open( "inNameFile.root" );
        TTree *newtree = (TTree*)oldFile->Get("tree");
        TTreeReader newreader( "tree", oldFile );
        TFile *newfile = new TFile("outNameFile.root","recreate");

        ....
        newtree->Branch("newVar", &newVar, "newVar/F");

at this point the TTree pointed to by newtree and the one use by the TTreeReader are (likely) the same.
Doing just

        TTree *newtree = (TTree*)oldFile->Get("tree");

Does not clone the TTree, it is the old TTree with its old data.

        newtree->Branch("newVar", &newVar, "newVar/F");

so that is adding a branch to the old TTree … That TTree is still attached to the read-only file and hence all the error message are correct.
You likely meant:

    auto oldFile = TFile::Open( "inNameFile.root" );
    TTree *oldtree = (TTree*)oldFile->Get("tree");
    TTreeReader newreader( "tree", oldFile );
    TFile *newfile = new TFile("outNameFile.root","recreate");

    ....
    newtree = oldtree->CloneTree(0); // Done after the creation of the new file so that it is attached to the new file.
    newtree->Branch("newVar", &newVar, "newVar/F");

Cheers,
Philippe.
1 Like

@pcanal Well, this thread (“TTreeReader with cloneTree”) seems to suggest that you need some special precautions when mixing CloneTree and TTreeReader. Maybe you could comment on this issue there (for future reference).

@Wile_E_Coyote the post you are linking was about triggering the reading/loading of the data.

Yes, that was the problem (without taking special precautions “the branch in newtree is fill with 0” so, reader.Next() was not sufficient).

Thanks @pcanal for the explanation. I think my (stupid) mistake was cloning the tree before creating a new file. I changed that and it worked.

Thanks again