LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
RootOutputTree.cc
Go to the documentation of this file.
2 // vim: set sw=2:
3 
7 #include "cetlib/container_algorithms.h"
9 
10 #include "Rtypes.h"
11 #include "TBranch.h"
12 #include "TClass.h"
13 #include "TClassRef.h"
14 #include "TFile.h"
15 #include "TTreeCloner.h"
16 
17 #include <functional>
18 #include <limits>
19 
20 using namespace cet;
21 using namespace std;
22 
23 namespace art {
24 
25  TTree*
26  RootOutputTree::makeTTree(TFile* filePtr, string const& name, int splitLevel)
27  {
28  auto tree = new TTree{name.c_str(), "", splitLevel};
29  if (!tree) {
31  << "Failed to create the tree: " << name << "\n";
32  }
33  if (tree->IsZombie()) {
35  << "Tree: " << name << " is a zombie.\n";
36  }
37  tree->SetDirectory(filePtr);
38  // Turn off autosave because it leaves too many deleted tree
39  // keys in the output file.
40  tree->SetAutoSave(numeric_limits<Long64_t>::max());
41  return tree;
42  }
43 
44  bool
45  RootOutputTree::checkSplitLevelAndBasketSize(
46  cet::exempt_ptr<TTree const> inputTree) const
47  {
48  // Do the split level and basket size match in the input and output?
49  if (inputTree == nullptr) {
50  return false;
51  }
52  for (auto outputBranch : readBranches_) {
53  if (outputBranch == nullptr) {
54  continue;
55  }
56  TBranch* inputBranch =
57  const_cast<TTree*>(inputTree.get())->GetBranch(outputBranch->GetName());
58  if (inputBranch == nullptr) {
59  continue;
60  }
61  if ((inputBranch->GetSplitLevel() != outputBranch->GetSplitLevel()) ||
62  (inputBranch->GetBasketSize() != outputBranch->GetBasketSize())) {
63  mf::LogInfo("FastCloning")
64  << "Fast Cloning disabled because split level or basket size "
65  "do not match";
66  return false;
67  }
68  }
69  return true;
70  }
71 
72  void
73  RootOutputTree::writeTTree(TTree* tree) noexcept(false)
74  {
75  // Update the tree-level entry count because we have been
76  // using branch fill instead of tree fill.
77  if (tree->GetNbranches() != 0) {
78  tree->SetEntries(-1);
79  }
80  // Use auto save here instead of write because it deletes
81  // the old tree key from the file, does not flush the
82  // baskets, and writes out the streamer infos, unlike write.
83  tree->AutoSave();
84  }
85 
86  void
87  RootOutputTree::writeTree() const
88  {
89  writeTTree(tree_);
90  writeTTree(metaTree_);
91  }
92 
93  bool
94  RootOutputTree::fastCloneTree(cet::exempt_ptr<TTree const> intree)
95  {
96  unclonedReadBranches_.clear();
97  unclonedReadBranchNames_.clear();
98  if (!fastCloningEnabled_) {
99  return false;
100  }
101 
102  bool cloned{false};
103  if (intree->GetEntries() != 0) {
104  TTreeCloner cloner(const_cast<TTree*>(intree.get()),
105  tree_,
106  "",
107  TTreeCloner::kIgnoreMissingTopLevel |
108  TTreeCloner::kNoWarnings |
109  TTreeCloner::kNoFileCache);
110  if (cloner.IsValid()) {
111  tree_->SetEntries(tree_->GetEntries() + intree->GetEntries());
112  cloner.Exec();
113  cloned = true;
114  } else {
115  fastCloningEnabled_ = false;
116  mf::LogInfo("fastCloneTree")
117  << "INFO: Unable to fast clone tree " << intree->GetName() << '\n'
118  << "INFO: ROOT reason is:\n"
119  << "INFO: " << cloner.GetWarning() << '\n'
120  << "INFO: Processing will continue, tree will be slow cloned.";
121  }
122  }
123  for (auto const& val : readBranches_) {
124  if (val->GetEntries() != tree_->GetEntries()) {
125  unclonedReadBranches_.push_back(val);
126  unclonedReadBranchNames_.insert(string(val->GetName()));
127  }
128  }
129  return cloned;
130  }
131 
132  static void
134  vector<TBranch*> const& branches,
135  bool saveMemory,
136  int64_t threshold)
137  {
138  for (auto const b : branches) {
139  auto bytesWritten = b->Fill();
140  if (saveMemory && (bytesWritten > threshold)) {
141  b->FlushBaskets();
142  b->DropBaskets("all");
143  }
144  }
145  }
146 
147  void
148  RootOutputTree::fillTree()
149  {
151  metaTree_, metaBranches_, false, saveMemoryObjectThreshold_);
152  bool saveMemory = (saveMemoryObjectThreshold_ > -1);
154  tree_, producedBranches_, saveMemory, saveMemoryObjectThreshold_);
155  if (fastCloningEnabled_) {
157  tree_, unclonedReadBranches_, saveMemory, saveMemoryObjectThreshold_);
158  } else {
160  tree_, readBranches_, saveMemory, saveMemoryObjectThreshold_);
161  }
162  ++nEntries_;
163  }
164 
165  void
166  RootOutputTree::resetOutputBranchAddress(BranchDescription const& pd)
167  {
168  TBranch* br = tree_->GetBranch(pd.branchName().c_str());
169  if (br == nullptr) {
170  return;
171  }
172  tree_->ResetBranchAddress(br);
173  }
174 
175  void
176  RootOutputTree::setOutputBranchAddress(BranchDescription const& pd,
177  void const*& pProd)
178  {
179  if (TBranch* br = tree_->GetBranch(pd.branchName().c_str())) {
180  br->SetAddress(&pProd);
181  }
182  }
183 
184  void
185  RootOutputTree::addOutputBranch(BranchDescription const& pd,
186  void const*& pProd)
187  {
188  TClassRef cls = TClass::GetClass(pd.wrappedName().c_str());
189  if (auto br = tree_->GetBranch(pd.branchName().c_str())) {
190  // Already have this branch, possibly update the branch address.
191  if (pProd == nullptr) {
192  // The OutputItem is freshly constructed and has not been
193  // passed to SetAddress yet. If selectProducts has just been
194  // called, we get here just after the branch object has been
195  // deleted with a ResetBranchAddress() to prepare for the
196  // OutputItem being replaced, and the OutputItem has just been
197  // recreated.
198  auto prod = reinterpret_cast<EDProduct*>(cls->New());
199  pProd = prod;
200  br->SetAddress(&pProd);
201  pProd = nullptr;
202  delete prod;
203  }
204  return;
205  }
206  auto bsize = pd.basketSize();
207  if (bsize == BranchDescription::invalidBasketSize) {
208  bsize = basketSize_;
209  }
210  auto splitlvl = pd.splitLevel();
211  if (splitlvl == BranchDescription::invalidSplitLevel) {
212  splitlvl = splitLevel_;
213  }
214  if (pProd != nullptr) {
216  << "OutputItem product pointer is not nullptr!\n";
217  }
218  auto prod = reinterpret_cast<EDProduct*>(cls->New());
219  pProd = prod;
220  TBranch* branch = tree_->Branch(pd.branchName().c_str(),
221  pd.wrappedName().c_str(),
222  &pProd,
223  bsize,
224  splitlvl);
225 
226  // Note that root will have just allocated a dummy product as the
227  // I/O buffer for the branch we have created. We will replace
228  // this I/O buffer in RootOutputFile::fillBranches() with the
229  // actual product or our own dummy using
230  // TBranchElement::SetAddress(), which will cause root to
231  // automatically delete the dummy product it allocated here.
232 
233  pProd = nullptr;
234  delete prod;
235  if (pd.compression() != BranchDescription::invalidCompression) {
236  branch->SetCompressionSettings(pd.compression());
237  }
238  if (nEntries_ > 0) {
239  // Backfill the branch with dummy entries to match the number
240  // of entries already written to the data tree.
241  std::unique_ptr<EDProduct> dummy(static_cast<EDProduct*>(cls->New()));
242  pProd = dummy.get();
243  int bytesWritten{};
244  for (auto i = nEntries_; i > 0; --i) {
245  auto cnt = branch->Fill();
246  if (cnt <= 0) {
247  // FIXME: Throw a fatal error here!
248  }
249  bytesWritten += cnt;
250  if ((saveMemoryObjectThreshold_ > -1) &&
251  (bytesWritten > saveMemoryObjectThreshold_)) {
252  branch->FlushBaskets();
253  branch->DropBaskets("all");
254  }
255  }
256  }
257  if (pd.produced()) {
258  producedBranches_.push_back(branch);
259  } else {
260  readBranches_.push_back(branch);
261  }
262  }
263 
264 } // namespace art
std::string const & wrappedName() const
ntuple GetBranch("organID") -> SetAddress(&xx)
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
STL namespace.
static void fillTreeBranches(TTree *, vector< TBranch * > const &branches, bool saveMemory, int64_t threshold)
Int_t max
Definition: plot.C:27
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
HLT enums.
std::string const & branchName() const