Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TTreeReader.cxx
Go to the documentation of this file.
1 // @(#)root/treeplayer:$Id$
2 // Author: Axel Naumann, 2011-09-21
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2013, Rene Brun and Fons Rademakers and al. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "TTreeReader.h"
13 
14 #include "TChain.h"
15 #include "TDirectory.h"
16 #include "TEntryList.h"
17 #include "TTreeCache.h"
18 #include "TTreeReaderValue.h"
19 #include "TFriendProxy.h"
20 
21 
22 // clang-format off
23 /**
24  \class TTreeReader
25  \ingroup treeplayer
26  \brief A simple, robust and fast interface to read values from ROOT columnar datasets such as TTree, TChain or TNtuple
27 
28  TTreeReader is associated to TTreeReaderValue and TTreeReaderArray which are handles to concretely
29  access the information in the dataset.
30 
31  Example code can be found in
32  - tutorials/tree/hsimpleReader.C
33  - tutorials/tree/h1analysisTreeReader.C
34  - <a href="http://root.cern.ch/gitweb?p=roottest.git;a=tree;f=root/tree/reader;hb=HEAD">This example</a>
35 
36  You can generate a skeleton of `TTreeReaderValue<T>` and `TTreeReaderArray<T>` declarations
37  for all of a tree's branches using `TTree::MakeSelector()`.
38 
39  Roottest contains an
40  <a href="http://root.cern.ch/gitweb?p=roottest.git;a=tree;f=root/tree/reader;hb=HEAD">example</a>
41  showing the full power.
42 
43 A simpler analysis example can be found below: it histograms a function of the px and py branches.
44 
45 ~~~{.cpp}
46 // A simple TTreeReader use: read data from hsimple.root (written by hsimple.C)
47 
48 #include "TFile.h"
49 #include "TH1F.h"
50 #include "TTreeReader.h"
51 #include "TTreeReaderValue.h"
52 
53 void hsimpleReader() {
54  // Create a histogram for the values we read.
55  TH1F("h1", "ntuple", 100, -4, 4);
56 
57  // Open the file containing the tree.
58  TFile *myFile = TFile::Open("$ROOTSYS/tutorials/hsimple.root");
59 
60  // Create a TTreeReader for the tree, for instance by passing the
61  // TTree's name and the TDirectory / TFile it is in.
62  TTreeReader myReader("ntuple", myFile);
63 
64  // The branch "px" contains floats; access them as myPx.
65  TTreeReaderValue<Float_t> myPx(myReader, "px");
66  // The branch "py" contains floats, too; access those as myPy.
67  TTreeReaderValue<Float_t> myPy(myReader, "py");
68 
69  // Loop over all entries of the TTree or TChain.
70  while (myReader.Next()) {
71  // Just access the data as if myPx and myPy were iterators (note the '*'
72  // in front of them):
73  myHist->Fill(*myPx + *myPy);
74  }
75 
76  myHist->Draw();
77 }
78 ~~~
79 
80 A more complete example including error handling and a few combinations of
81 TTreeReaderValue and TTreeReaderArray would look like this:
82 
83 ~~~{.cpp}
84 #include <TFile.h>
85 #include <TH1.h>
86 #include <TTreeReader.h>
87 #include <TTreeReaderValue.h>
88 #include <TTreeReaderArray.h>
89 
90 #include "TriggerInfo.h"
91 #include "Muon.h"
92 #include "Tau.h"
93 
94 #include <vector>
95 #include <iostream>
96 
97 bool CheckValue(ROOT::Internal::TTreeReaderValueBase& value) {
98  if (value->GetSetupStatus() < 0) {
99  std::cerr << "Error " << value->GetSetupStatus()
100  << "setting up reader for " << value->GetBranchName() << '\n';
101  return false;
102  }
103  return true;
104 }
105 
106 
107 // Analyze the tree "MyTree" in the file passed into the function.
108 // Returns false in case of errors.
109 bool analyze(TFile* file) {
110  // Create a TTreeReader named "MyTree" from the given TDirectory.
111  // The TTreeReader gives access to the TTree to the TTreeReaderValue and
112  // TTreeReaderArray objects. It knows the current entry number and knows
113  // how to iterate through the TTree.
114  TTreeReader reader("MyTree", file);
115 
116  // Read a single float value in each tree entries:
117  TTreeReaderValue<float> weight(reader, "event.weight");
118 
119  // Read a TriggerInfo object from the tree entries:
120  TTreeReaderValue<TriggerInfo> triggerInfo(reader, "triggerInfo");
121 
122  //Read a vector of Muon objects from the tree entries:
123  TTreeReaderValue<std::vector<Muon>> muons(reader, "muons");
124 
125  //Read the pT for all jets in the tree entry:
126  TTreeReaderArray<double> jetPt(reader, "jets.pT");
127 
128  // Read the taus in the tree entry:
129  TTreeReaderArray<Tau> taus(reader, "taus");
130 
131 
132  // Now iterate through the TTree entries and fill a histogram.
133 
134  TH1F("hist", "TTreeReader example histogram", 10, 0., 100.);
135 
136  bool firstEntry = true;
137  while (reader.Next()) {
138  if (firstEntry) {
139  // Check that branches exist and their types match our expectation.
140  if (!CheckValue(weight)) return false;
141  if (!CheckValue(triggerInfo)) return false;
142  if (!CheckValue(muons)) return false;
143  if (!CheckValue(jetPt)) return false;
144  if (!CheckValue(taus)) return false;
145  firstentry = false;
146  }
147 
148  // Access the TriggerInfo object as if it's a pointer.
149  if (!triggerInfo->hasMuonL1())
150  continue;
151 
152  // Ditto for the vector<Muon>.
153  if (!muons->size())
154  continue;
155 
156  // Access the jetPt as an array, whether the TTree stores this as
157  // a std::vector, std::list, TClonesArray or Jet* C-style array, with
158  // fixed or variable array size.
159  if (jetPt.GetSize() < 2 || jetPt[0] < 100)
160  continue;
161 
162  // Access the array of taus.
163  if (!taus.IsEmpty()) {
164  // Access a float value - need to dereference as TTreeReaderValue
165  // behaves like an iterator
166  float currentWeight = *weight;
167  for (const Tau& tau: taus) {
168  hist->Fill(tau.eta(), currentWeight);
169  }
170  }
171  } // TTree entry / event loop
172 
173  // Return true if we have iterated through all entries.
174  return reader.GetEntryStatus() == TTreeReader::kEntryBeyondEnd;
175 }
176 ~~~
177 */
178 // clang-format on
179 
180 ClassImp(TTreeReader);
181 
182 using namespace ROOT::Internal;
183 
184 // Provide some storage for the poor little symbol.
185 constexpr const char * const TTreeReader::fgEntryStatusText[TTreeReader::kEntryBeyondEnd + 1];
186 
187 ////////////////////////////////////////////////////////////////////////////////
188 /// Default constructor. Call SetTree to connect to a TTree.
189 
190 TTreeReader::TTreeReader() : fNotify(this) {}
191 
192 ////////////////////////////////////////////////////////////////////////////////
193 /// Access data from tree.
194 
195 TTreeReader::TTreeReader(TTree* tree, TEntryList* entryList /*= nullptr*/):
196  fTree(tree),
197  fEntryList(entryList),
198  fNotify(this)
199 {
200  if (!fTree) {
201  ::Error("TTreeReader::TTreeReader", "TTree is NULL!");
202  } else {
203  Initialize();
204  }
205 }
206 
207 ////////////////////////////////////////////////////////////////////////////////
208 /// Access data from the tree called keyname in the directory (e.g. TFile)
209 /// dir, or the current directory if dir is NULL. If keyname cannot be
210 /// found, or if it is not a TTree, IsInvalid() will return true.
211 
212 TTreeReader::TTreeReader(const char* keyname, TDirectory* dir, TEntryList* entryList /*= nullptr*/):
213  fEntryList(entryList),
214  fNotify(this)
215 {
216  if (!dir) dir = gDirectory;
217  dir->GetObject(keyname, fTree);
218  if (!fTree) {
219  std::string msg = "No TTree called ";
220  msg += keyname;
221  msg += " was found in the selected TDirectory.";
222  Error("TTreeReader", "%s", msg.c_str());
223  }
224  Initialize();
225 }
226 
227 ////////////////////////////////////////////////////////////////////////////////
228 /// Tell all value readers that the tree reader does not exist anymore.
229 
230 TTreeReader::~TTreeReader()
231 {
232  for (std::deque<ROOT::Internal::TTreeReaderValueBase*>::const_iterator
233  i = fValues.begin(), e = fValues.end(); i != e; ++i) {
234  (*i)->MarkTreeReaderUnavailable();
235  }
236  if (fTree && fNotify.IsLinked())
237  fNotify.RemoveLink(*fTree);
238 
239  // Need to clear the map of proxies before deleting the director otherwise
240  // they will have a dangling pointer.
241  fProxies.clear();
242 
243  for (auto feproxy: fFriendProxies) {
244  delete feproxy;
245  }
246  fFriendProxies.clear();
247 
248  delete fDirector;
249 }
250 
251 ////////////////////////////////////////////////////////////////////////////////
252 /// Initialization of the director.
253 
254 void TTreeReader::Initialize()
255 {
256  fEntry = -1;
257  if (!fTree) {
258  fEntryStatus = kEntryNoTree;
259  fLoadTreeStatus = kNoTree;
260  return;
261  }
262 
263  fLoadTreeStatus = kLoadTreeNone;
264  if (fTree->InheritsFrom(TChain::Class())) {
265  SetBit(kBitIsChain);
266  }
267 
268  fDirector = new ROOT::Internal::TBranchProxyDirector(fTree, -1);
269 
270  if (!fNotify.IsLinked()) {
271  fNotify.PrependLink(*fTree);
272 
273  if (fTree->GetTree()) {
274  // The current TTree is already available.
275  fSetEntryBaseCallingLoadTree = kTRUE;
276  Notify();
277  fSetEntryBaseCallingLoadTree = kFALSE;
278  }
279  }
280 }
281 
282 ////////////////////////////////////////////////////////////////////////////////
283 /// Callback from TChain and TTree's LoadTree.
284 
285 Bool_t TTreeReader::Notify()
286 {
287 
288  if (fSetEntryBaseCallingLoadTree) {
289  if (fLoadTreeStatus == kExternalLoadTree) {
290  // This can happen if someone switched trees behind us.
291  // Likely cause: a TChain::LoadTree() e.g. from TTree::Process().
292  // This means that "local" should be set!
293  // There are two entities switching trees which is bad.
294  Warning("SetEntryBase()",
295  "The current tree in the TChain %s has changed (e.g. by TTree::Process) "
296  "even though TTreeReader::SetEntry() was called, which switched the tree "
297  "again. Did you mean to call TTreeReader::SetLocalEntry()?",
298  fTree->GetName());
299  }
300  fLoadTreeStatus = kInternalLoadTree;
301  } else {
302  fLoadTreeStatus = kExternalLoadTree;
303  }
304 
305  if (!fEntryList && fTree->GetEntryList() && !TestBit(kBitHaveWarnedAboutEntryListAttachedToTTree)) {
306  Warning("SetEntryBase()",
307  "The TTree / TChain has an associated TEntryList. "
308  "TTreeReader ignores TEntryLists unless you construct the TTreeReader passing a TEntryList.");
309  SetBit(kBitHaveWarnedAboutEntryListAttachedToTTree);
310  }
311 
312  if (!fDirector->Notify()) {
313  Error("SetEntryBase()", "There was an error while notifying the proxies.");
314  return false;
315  }
316 
317  if (fProxiesSet) {
318  for (auto value: fValues) {
319  value->NotifyNewTree(fTree->GetTree());
320  }
321  }
322 
323  return kTRUE;
324 }
325 
326 ////////////////////////////////////////////////////////////////////////////////
327 /// Tell readers we now have a tree.
328 /// fValues gets insertions during this loop (when parameterized arrays are read),
329 /// invalidating iterators. Use old-school counting instead.
330 
331 Bool_t TTreeReader::SetProxies() {
332 
333  for (size_t i = 0; i < fValues.size(); ++i) {
334  ROOT::Internal::TTreeReaderValueBase* reader = fValues[i];
335  reader->CreateProxy();
336  if (!reader->GetProxy()){
337  return kFALSE;
338  }
339 
340  }
341  // If at least one proxy was there and no error occurred, we assume the proxies to be set.
342  fProxiesSet = !fValues.empty();
343 
344  // Now we need to properly set the TTreeCache. We do this in steps:
345  // 1. We set the entry range according to the entry range of the TTreeReader
346  // 2. We add to the cache the branches identifying them by the name the user provided
347  // upon creation of the TTreeReader{Value, Array}s
348  // 3. We stop the learning phase.
349  // Operations 1, 2 and 3 need to happen in this order. See: https://sft.its.cern.ch/jira/browse/ROOT-9773?focusedCommentId=87837
350  if (fProxiesSet) {
351  const auto curFile = fTree->GetCurrentFile();
352  if (curFile && fTree->GetTree()->GetReadCache(curFile, true)) {
353  if (!(-1LL == fEndEntry && 0ULL == fBeginEntry)) {
354  // We need to avoid to pass -1 as end entry to the SetCacheEntryRange method
355  const auto lastEntry = (-1LL == fEndEntry) ? fTree->GetEntriesFast() : fEndEntry;
356  fTree->SetCacheEntryRange(fBeginEntry, lastEntry);
357  }
358  for (auto value: fValues) {
359  fTree->AddBranchToCache(value->GetProxy()->GetBranchName(), true);
360  }
361  fTree->StopCacheLearningPhase();
362  }
363  }
364 
365  return kTRUE;
366 }
367 
368 ////////////////////////////////////////////////////////////////////////////////
369 /// Set the range of entries to be loaded by `Next()`; end will not be loaded.
370 ///
371 /// If end <= begin, `end` is ignored (set to `-1`) and only `begin` is used.
372 /// Example:
373 ///
374 /// ~~~ {.cpp}
375 /// reader.SetEntriesRange(3, 5);
376 /// while (reader.Next()) {
377 /// // Will load entries 3 and 4.
378 /// }
379 /// ~~~
380 ///
381 /// \param beginEntry The first entry to be loaded by `Next()`.
382 /// \param endEntry The entry where `Next()` will return kFALSE, not loading it.
383 
384 TTreeReader::EEntryStatus TTreeReader::SetEntriesRange(Long64_t beginEntry, Long64_t endEntry)
385 {
386  if (beginEntry < 0)
387  return kEntryNotFound;
388  // Complain if the entries number is larger than the tree's / chain's / entry
389  // list's number of entries, unless it's a TChain and "max entries" is
390  // uninitialized (i.e. TTree::kMaxEntries).
391  if (beginEntry >= GetEntries(false) && !(IsChain() && GetEntries(false) == TTree::kMaxEntries)) {
392  Error("SetEntriesRange()", "first entry out of range 0..%lld", GetEntries(false));
393  return kEntryNotFound;
394  }
395 
396  if (endEntry > beginEntry)
397  fEndEntry = endEntry;
398  else
399  fEndEntry = -1;
400  if (beginEntry - 1 < 0)
401  Restart();
402  else {
403  EEntryStatus es = SetEntry(beginEntry - 1);
404  if (es != kEntryValid) {
405  Error("SetEntriesRange()", "Error setting first entry %lld: %s",
406  beginEntry, fgEntryStatusText[(int)es]);
407  return es;
408  }
409  }
410 
411  fBeginEntry = beginEntry;
412 
413  return kEntryValid;
414 }
415 
416 void TTreeReader::Restart() {
417  fDirector->SetReadEntry(-1);
418  fProxiesSet = false; // we might get more value readers, meaning new proxies.
419  fEntry = -1;
420  if (const auto curFile = fTree->GetCurrentFile()) {
421  if (auto tc = fTree->GetTree()->GetReadCache(curFile, true)) {
422  tc->DropBranch("*", true);
423  tc->ResetCache();
424  }
425  }
426 }
427 
428 ////////////////////////////////////////////////////////////////////////////////
429 /// Returns the number of entries of the TEntryList if one is provided, else
430 /// of the TTree / TChain, independent of a range set by SetEntriesRange()
431 /// by calling TTree/TChain::GetEntriesFast.
432 
433 
434 Long64_t TTreeReader::GetEntries() const {
435  if (fEntryList)
436  return fEntryList->GetN();
437  if (!fTree)
438  return -1;
439  return fTree->GetEntriesFast();
440 }
441 
442 
443 ////////////////////////////////////////////////////////////////////////////////
444 /// Returns the number of entries of the TEntryList if one is provided, else
445 /// of the TTree / TChain, independent of a range set by SetEntriesRange().
446 ///
447 /// \param force If `IsChain()` and `force`, determines whether all TFiles of
448 /// this TChain should be opened to determine the exact number of entries
449 /// of the TChain. If `!IsChain()`, `force` is ignored.
450 
451 Long64_t TTreeReader::GetEntries(Bool_t force) {
452  if (fEntryList)
453  return fEntryList->GetN();
454  if (!fTree)
455  return -1;
456  if (force) {
457  fSetEntryBaseCallingLoadTree = kTRUE;
458  auto res = fTree->GetEntries();
459  // Go back to where we were:
460  fTree->LoadTree(GetCurrentEntry());
461  fSetEntryBaseCallingLoadTree = kFALSE;
462  return res;
463  }
464  return fTree->GetEntriesFast();
465 }
466 
467 
468 
469 ////////////////////////////////////////////////////////////////////////////////
470 /// Load an entry into the tree, return the status of the read.
471 /// For chains, entry is the global (i.e. not tree-local) entry number, unless
472 /// `local` is `true`, in which case `entry` specifies the entry number within
473 /// the current tree. This is needed for instance for TSelector::Process().
474 
475 TTreeReader::EEntryStatus TTreeReader::SetEntryBase(Long64_t entry, Bool_t local)
476 {
477  if (IsInvalid()) {
478  fEntryStatus = kEntryNoTree;
479  fEntry = -1;
480  return fEntryStatus;
481  }
482 
483  fEntry = entry;
484 
485  Long64_t entryAfterList = entry;
486  if (fEntryList) {
487  if (entry >= fEntryList->GetN()) {
488  // Passed the end of the chain, Restart() was not called:
489  // don't try to load entries anymore. Can happen in these cases:
490  // while (tr.Next()) {something()};
491  // while (tr.Next()) {somethingelse()}; // should not be calling somethingelse().
492  fEntryStatus = kEntryNotFound;
493  return fEntryStatus;
494  }
495  if (entry >= 0) entryAfterList = fEntryList->GetEntry(entry);
496  if (local && IsChain()) {
497  // Must translate the entry list's entry to the current TTree's entry number.
498  local = kFALSE;
499  }
500  }
501 
502  TTree* treeToCallLoadOn = local ? fTree->GetTree() : fTree;
503 
504  fSetEntryBaseCallingLoadTree = kTRUE;
505  const Long64_t loadResult = treeToCallLoadOn->LoadTree(entryAfterList);
506  fSetEntryBaseCallingLoadTree = kFALSE;
507 
508  if (loadResult < 0) {
509  // ROOT-9628 We cover here the case when:
510  // - We deal with a TChain
511  // - The last file is opened
512  // - The TTree is not correctly loaded
513  // The system is robust against issues with TTrees associated to the chain
514  // when they are not at the end of it.
515  if (loadResult == -3 && TestBit(kBitIsChain) && !fTree->GetTree()) {
516  fDirector->Notify();
517  if (fProxiesSet) {
518  for (auto value: fValues) {
519  value->NotifyNewTree(fTree->GetTree());
520  }
521  }
522  Warning("SetEntryBase()",
523  "There was an issue opening the last file associated to the TChain "
524  "being processed.");
525  fEntryStatus = kEntryChainFileError;
526  return fEntryStatus;
527  }
528 
529  if (loadResult == -2) {
530  fDirector->Notify();
531  if (fProxiesSet) {
532  for (auto value: fValues) {
533  value->NotifyNewTree(fTree->GetTree());
534  }
535  }
536  fEntryStatus = kEntryNotFound;
537  return fEntryStatus;
538  }
539 
540  if (loadResult == -1) {
541  // The chain is empty
542  fEntryStatus = kEntryNotFound;
543  return fEntryStatus;
544  }
545 
546  if (loadResult == -4) {
547  // The TChainElement corresponding to the entry is missing or
548  // the TTree is missing from the file.
549  fDirector->Notify();
550  if (fProxiesSet) {
551  for (auto value: fValues) {
552  value->NotifyNewTree(fTree->GetTree());
553  }
554  }
555  fEntryStatus = kEntryNotFound;
556  return fEntryStatus;
557  }
558 
559  Warning("SetEntryBase()",
560  "Unexpected error '%lld' in %s::LoadTree", loadResult,
561  treeToCallLoadOn->IsA()->GetName());
562 
563  fEntryStatus = kEntryUnknownError;
564  return fEntryStatus;
565  }
566 
567  if (!fProxiesSet) {
568  if (!SetProxies()) {
569  fEntryStatus = kEntryDictionaryError;
570  return fEntryStatus;
571  }
572  }
573 
574  if (fEndEntry >= 0 && entry >= fEndEntry) {
575  fEntryStatus = kEntryBeyondEnd;
576  return fEntryStatus;
577  }
578  fDirector->SetReadEntry(loadResult);
579  fEntryStatus = kEntryValid;
580  return fEntryStatus;
581 }
582 
583 ////////////////////////////////////////////////////////////////////////////////
584 /// Set (or update) the which tree to read from. `tree` can be
585 /// a TTree or a TChain.
586 
587 void TTreeReader::SetTree(TTree* tree, TEntryList* entryList /*= nullptr*/)
588 {
589  fTree = tree;
590  fEntryList = entryList;
591  fEntry = -1;
592 
593  if (fTree) {
594  fLoadTreeStatus = kLoadTreeNone;
595  SetBit(kBitIsChain, fTree->InheritsFrom(TChain::Class()));
596  } else {
597  fLoadTreeStatus = kNoTree;
598  }
599 
600  if (!fDirector) {
601  Initialize();
602  }
603  else {
604  fDirector->SetTree(fTree);
605  fDirector->SetReadEntry(-1);
606  }
607 }
608 
609 ////////////////////////////////////////////////////////////////////////////////
610 /// Set (or update) the which tree to read from, passing the name of a tree in a
611 /// directory.
612 ///
613 /// \param keyname - name of the tree in `dir`
614 /// \param dir - the `TDirectory` to load `keyname` from (or gDirectory if `nullptr`)
615 /// \param entryList - the `TEntryList` to attach to the `TTreeReader`.
616 
617 void TTreeReader::SetTree(const char* keyname, TDirectory* dir, TEntryList* entryList /*= nullptr*/)
618 {
619  TTree* tree = nullptr;
620  if (!dir)
621  dir = gDirectory;
622  dir->GetObject(keyname, tree);
623  SetTree(tree, entryList);
624 }
625 
626 ////////////////////////////////////////////////////////////////////////////////
627 /// Add a value reader for this tree.
628 
629 Bool_t TTreeReader::RegisterValueReader(ROOT::Internal::TTreeReaderValueBase* reader)
630 {
631  if (fProxiesSet) {
632  Error("RegisterValueReader",
633  "Error registering reader for %s: TTreeReaderValue/Array objects must be created before the call to Next() / SetEntry() / SetLocalEntry(), or after TTreeReader::Restart()!",
634  reader->GetBranchName());
635  return false;
636  }
637  fValues.push_back(reader);
638  return true;
639 }
640 
641 ////////////////////////////////////////////////////////////////////////////////
642 /// Remove a value reader for this tree.
643 
644 void TTreeReader::DeregisterValueReader(ROOT::Internal::TTreeReaderValueBase* reader)
645 {
646  std::deque<ROOT::Internal::TTreeReaderValueBase*>::iterator iReader
647  = std::find(fValues.begin(), fValues.end(), reader);
648  if (iReader == fValues.end()) {
649  Error("DeregisterValueReader", "Cannot find reader of type %s for branch %s", reader->GetDerivedTypeName(), reader->fBranchName.Data());
650  return;
651  }
652  fValues.erase(iReader);
653 }