tree->GetEntry() fails inside function

So I was trying to write a function that can take a vector of expressions which are then used to create a new branch on a given tree where these expressions are evaluated.

For this I first build a vector of TTreeFormula, from which I extract the names of the branches needed to evaluate all formulas (not taking care of making them unique yet, will add this in the future).

I then create the new branches (using a second character vector which contains the names of the new branches), disable all branches except the newly created and the needed ones.

Finally, I loop over all entries of the tree and fill the new branches. Here is the code of the function:

#include "cppitertools/zip.hpp"

void AddBranches(std::vector<const char *> names, std::vector<const char *> expressions, TTree *tree)
{
   auto                        n_expressions = expressions.size();
   std::vector<TTreeFormula *> formulas;
   std::vector<TBranch *>      branches;
   std::vector<Double_t>       variables;

   // Define formulas and new branches
   for (auto expression : iter::zip(names, expressions)) {
      formulas.push_back(new TTreeFormula(std::get<0>(expression), std::get<1>(expression), tree));
      variables.push_back(Double_t(0.));
      branches.push_back(tree->Branch(std::get<0>(expression), &variables.back()));
   }

   // Activate only needed branches
   tree->SetBranchStatus("*", kFALSE);
   for (auto branch : branches) {
      tree->SetBranchStatus(branch->GetName(), kTRUE);
   }
   for (auto formula : formulas) {
      for (Int_t i = 0; i < formula->GetNcodes(); i++) {
         auto branch_name = formula->GetLeaf(i)->GetName();
         tree->SetBranchStatus(branch_name, kTRUE);
         variables.push_back(Double_t(0.));
         tree->SetBranchAddress(branch_name, &variables.back());
      }
   }
   std::cout << "Setup of new branches done" << std::endl;

   // Calculate content of new branches
   Long64_t nentries = tree->GetEntries();
   for (Long64_t i = 0; i < nentries; i++) {
      if (i % 100000 == 0) {
         std::cout << i << "/" << nentries << std::endl;
      }
      if (tree->GetEntry(i)) {
         for (auto j = 0; j < n_expressions; j++) {
            variables[j] = formulas[j]->EvalInstance();
            branches[j]->Fill();
         }
      }
   }
   // re-activate all branches
   tree->SetBranchStatus("*", kTRUE);
}

The cppitertools can be found here.

This seems to work fine when running it manually in a ROOT session with a given tree (a few GB large) and expressions. However, it quickly fails when trying to run it with root script.cxx++ with the following error, which I don’t understand at all:

===========================================================
#6  0x00007fa77db9ee11 in TList::FindObject (this=<optimized out>, obj=0x558a06492e50) at /cern/root-git2/core/cont/src/TList.cxx:525
#7  0x00007fa77dba1abf in TMap::GetValue (this=0x558a05d8a850, key=key
entry=0x558a06492e50) at /cern/root-git2/core/cont/src/TMap.cxx:249
#8  0x00007fa77b5c895f in TFile::GetCacheRead (this=this
entry=0x558a05d899f0, tree=0x558a06492e50) at /cern/root-git2/io/io/src/TFile.cxx:1210
#9  0x00007fa76ff8952e in TBranch::GetBasket (this=0x558a06cfcb10, basketnumber=0) at /cern/root-git2/tree/tree/src/TBranch.cxx:1141
#10 0x00007fa76ff89ad3 in TBranch::GetEntry (this=0x558a06cfcb10, entry=0, getall=<optimized out>) at /cern/root-git2/tree/tree/src/TBranch.cxx:1275
#11 0x00007fa76ffe2e37 in TTree::<lambda()>::operator() (__closure=<synthetischer Zeiger>) at /cern/root-git2/tree/tree/src/TTree.cxx:5334
#12 TTree::GetEntry (this=0x558a06492e50, entry=0, getall=0) at /cern/root-git2/tree/tree/src/TTree.cxx:5403
#13 0x00007fa76dae57f9 in AddBranches(std::vector<char const*, std::allocator<char const*> >, std::vector<char const*, std::allocator<char const*> >, TTree*) () from /home/andreas/cernbox/work/drell-yan/scripts/reduce_alt_cxx.so
#14 0x00007fa76dae68a0 in reduce_alt(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) () from /home/andreas/cernbox/work/drell-yan/scripts/reduce_alt_cxx.so
#15 0x00007fa77e341156 in ?? ()
#16 0x00007ffd0cfa3978 in ?? ()
#17 0x00007ffd0cfa3940 in ?? ()
#18 0x0000000000000015 in ?? ()
#19 0x00007fa778df8c7d in std::_Function_handler<unsigned long (), llvm::orc::LazyEmittingLayer<llvm::orc::IRCompileLayer<cling::IncrementalJIT::RemovableObjectLinkingLayer> >::EmissionDeferredSet::find(llvm::StringRef, bool, llvm::orc::IRCompileLayer<cling::IncrementalJIT::RemovableObjectLinkingLayer>&)::{lambda()#1}>::_M_invoke(std::_Any_data const&) () from /cern/root-6.09.02/lib/libCling.so
#20 0x00007fa778da60e7 in cling::Interpreter::RunFunction(clang::FunctionDecl const*, cling::Value*) [clone .part.275] () from /cern/root-6.09.02/lib/libCling.so
#21 0x00007fa778da8fad in cling::Interpreter::EvaluateInternal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cling::CompilationOptions, cling::Value*, cling::Transaction**, unsigned long) () from /cern/root-6.09.02/lib/libCling.so
#22 0x00007fa778da91d6 in cling::Interpreter::process(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cling::Value*, cling::Transaction**) () from /cern/root-6.09.02/lib/libCling.so
#23 0x00007fa778e35f12 in cling::MetaProcessor::process(char const*, cling::Interpreter::CompilationResult&, cling::Value*) () from /cern/root-6.09.02/lib/libCling.so
#24 0x00007fa778d30086 in HandleInterpreterException (metaProcessor=<optimized out>, input_line=<optimized out>, compRes=
0x7ffd0cfa3dd4: cling::Interpreter::kSuccess, result=result
entry=0x7ffd0cfa3de0) at /cern/root-git2/core/metacling/src/TCling.cxx:1886
===========================================================

It succeeds if I limit the size of the tree to have only a few (less than 3000) entries.

   std::vector<Double_t>       variables;

   // Define formulas and new branches
   for (auto expression : iter::zip(names, expressions)) {
      formulas.push_back(new TTreeFormula(std::get<0>(expression), std::get<1>(expression), tree));
      variables.push_back(Double_t(0.));
      branches.push_back(tree->Branch(std::get<0>(expression), &variables.back()));
  }

The problem appears because the address passed to the new branch is not ‘stable’. I.e. the location of the content of the variables vector will change as soon as the vector is resize but the branch will not be informed. Either reserve a size large enough for the vector (if you know the size before hand) or do two loops.

   std::vector<Double_t>       variables;

   // Define formulas and new branches
   for (auto expression : iter::zip(names, expressions)) {
      formulas.push_back(new TTreeFormula(std::get<0>(expression), std::get<1>(expression), tree));
      variables.push_back(Double_t(0.));
   }
   size_t i = 0;
   for (auto expression : iter::zip(names, expressions)) {
      branches.push_back(tree->Branch(std::get<0>(expression), &variables.at( i )));
      i++;
  }

Cheers,
Philippe.

Thanks, that was indeed the problem. I opted for an additional loop, changing it around a bit by setting the address initially to the wrong place and later set all addresses correctly in one go:

void AddBranches(std::vector<const char *> names, std::vector<const char *> expressions, TTree *tree)
{
   auto                        n_expressions = expressions.size();
   std::vector<TTreeFormula *> formulas;
   std::vector<TBranch *>      branches;
   std::vector<Double_t>       variables;
   std::set<const char *>      branch_names_set;

   // Define formulas and new branches
   for (auto expression : iter::zip(names, expressions)) {
      formulas.push_back(new TTreeFormula(std::get<0>(expression), std::get<1>(expression), tree));
      for (Int_t i = 0; i < formulas.back()->GetNcodes(); i++) {
         branch_names_set.insert(formulas.back()->GetLeaf(i)->GetName());
         variables.push_back(Double_t(0.));
      }
      variables.push_back(Double_t(0.));
      // Address will be overwritten later, it must just be non-null here
      branches.push_back(tree->Branch(std::get<0>(expression), &variables.back()));
   }
   // Need this because iter::chain can't deal with a set and vector
   std::vector<const char *> branch_names(branch_names_set.begin(), branch_names_set.end());
   // Activate only needed branches
   tree->SetBranchStatus("*", kFALSE);
   size_t k = 0;
   for (auto &&branch_name : iter::chain(names, branch_names)) {
      tree->SetBranchStatus(branch_name, kTRUE);
      // Set branch addresses correctly. Overwrites also those of the newly created ones
      tree->SetBranchAddress(branch_name, &variables.at(k));
      k++;
   }
   std::cout << "Setup of new branches done" << std::endl;

   // Calculate content of new branches
   Long64_t nentries = tree->GetEntries();
   for (Long64_t i = 0; i < nentries; i++) {
      if (i % 100000 == 0) {
         std::cout << i << "/" << nentries << std::endl;
      }
      if (tree->GetEntry(i)) {
         for (auto j = 0; j < n_expressions; j++) {
            variables[j] = formulas[j]->EvalInstance();
            branches[j]->Fill();
         }
      }
   }
   // re-activate all branches
   tree->SetBranchStatus("*", kTRUE);
}

tree->ResetBranchAddresses(); // disconnect from local variables tree->SetBranchStatus("*", kTRUE);

1 Like

While it did not crash without this, it is probably a good idea…

Do you know if not resetting these addresses makes it slower when calculating another variable? Since I turn off all unneeded branches, this should not be the case, right?

It should not be a significant difference.

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