Home | News | Documentation | Download

TTree: Declear arbitrary number of variables for adding TBranches with a loop

I have a list of names whose number and content depend on input file, and want to add TBranches with these names to a TTree, fill the tree in a loop, and write it the output file.

How can I fill the address parameter when adding the branches?

I tried to declare double array or array of pointers got with malloc to hold the addresses, but none of them work. It causes segmentation violation to build an array with vectors inside, so I cannot use the vectors.

ROOT Version: 6.18/04
Platform: Linux (NixOS 20.09 Nightingale)
Compiler: gcc (g++)

Easiest is to use std::vector<std::string>>

But how can I fill std::string to make it appear like Int_t of Float_t?

The following is similar to what I have tried:

#include <TFile.h>
#include <TString.h>
#include <RtypesCore.h>
#include <vector>

TFile *fileOut = new TFile("output_file.root", "recreate");
TTree *ttSelected = new TTree("Selected", "Selected entries");

UInt_t nLeafIn, nLeafElectron nElectron;
std::vector<TString> vNameLeafElectron, vTitleLeafElectron;
TFile *fileIn = new TFile("path/to/input_file.root");
TTree *ttEvents = (TTree *) fileIn->Get("Events");
TObjArray tarrLeafAll = ttEvents->GetListOfLeaves();
UInt_t nLeafAll = tarrLeafAll->Size();

for (UInt_t i=0; i<nLeafAll; i++) {
  TLeaf *tlfCurrent = (TLeaf *) tarrLeafAll->At(i);
  TString name = tlfCurrent->GetName();
  TString title = tlfCurrent->GetTitle();
  TString typename = tlfCurrent->GetTypeName();
  if (name.First("Electron_") == 0 && typeName == "Float_t") {
    vNameLeafElectron.push_back(name);
    vTitleLeafElectron.push_back(title);
  }
}
nElectron = *((UInt_t *) ttEvents->Get("nElectron"));
fileIn->Close(); 

Float_t aaFloatLeafElectron[nElectron][nElectron][nLeafElectron];
for (UInt_t i=0; i<nLeafElectron; i++) {
  ttSelected.Branch(vNameLeafElectron[i], aaFloatLeafElectron[i], vNameLeafElectron[i] + "/F", __SIZEOF_FLOAT__*nElectron);
}

// Some custom way to open and read input_file.root and get nEntries
// ...
// ULong_t nEntries = ...

for (ULong_t jEntry=0; jEntry<nEntries; jEntry++) {
  // Some way to get the entry
  // ...

  // Some selections
  // ...
  // if (not ...) {
  //   continue;
  // }
  // ...

  for (UInt_t i=0; i<nLeafElectron; i++) {
    for (UInt_t j=0; j<nElectron; j++) {
      aaFloatLeafElectron[i][j] = ...;
    }
  }

  ttSelected->Fill();
}

ttSelected->GetCurrentFile()->Write();
fileOut->close();

@pcanal

Let me simplify the question:

I have a root file with a TTree called Events inside, on which are some variables of different names including nElectron and Electron_*.

I want to build a TTree called Selected that saved all the variables named Electron_* in Events, but only the entries where nElectron are not zero.

How should I do that? Given that the variables with a name begin with "Electron_" differs between input files, I cannot define each of them manually (as below).

std::vector<Float_t> vElectron_pt;
vElectron_pt.clear();
Selected.Branch("Electron_pt", &vElectron, "Electron_pt/F", __SIZEOF_FLOAT__*nElectron)

How can I declare a variable to have addresses to fill into the position where &vElectron_pt is?

something like:

inputtree->SetBranchStatus("*", false);
inputtree->SetBranchStatus("nElectron", true);
inputtree->SetBranchStatus("Electron_*", true);
int nElectron;
intputtree->SetBranchAddress("nElectron", &nElectron);
// No need to set the other address unless there are used for
// the selection.

TTree *outputtree = intputtree->CloneTree(0);
outputtree->SetDirectory( outputfile );

for(long long e = 0; e < inputtree->GetEntriesFast(); ++e) {
   inputtree->GetEntry(e);
   if (nElectron)
      outputtree->Fill();
}

Alternatively you can also use RDataFrame’s Snapshot feature.
Cheers,
Philippe.

1 Like