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