Logo ROOT   6.12/07
Reference Guide
TDFActionHelpers.hxx
Go to the documentation of this file.
1 // Author: Enrico Guiraud, Danilo Piparo CERN 12/2016
2 
3 /*************************************************************************
4  * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers. *
5  * All rights reserved. *
6  * *
7  * For the licensing terms see $ROOTSYS/LICENSE. *
8  * For the list of contributors see $ROOTSYS/README/CREDITS. *
9  *************************************************************************/
10 
11 #ifndef ROOT_TDFOPERATIONS
12 #define ROOT_TDFOPERATIONS
13 
14 #include "ROOT/TBufferMerger.hxx" // for SnapshotHelper
15 #include "ROOT/TypeTraits.hxx"
16 #include "ROOT/TDFUtils.hxx"
17 #include "ROOT/TThreadedObject.hxx"
18 #include "ROOT/TArrayBranch.hxx"
19 #include "TH1.h"
20 #include "TTreeReader.h" // for SnapshotHelper
21 #include "TFile.h" // for SnapshotHelper
22 
23 #include <algorithm>
24 #include <memory>
25 #include <stdexcept>
26 #include <type_traits>
27 #include <vector>
28 
29 /// \cond HIDDEN_SYMBOLS
30 
31 namespace ROOT {
32 namespace Internal {
33 namespace TDF {
34 using namespace ROOT::TypeTraits;
35 using namespace ROOT::Experimental::TDF;
36 
37 using Hist_t = ::TH1D;
38 
39 template <typename F>
40 class ForeachSlotHelper {
41  F fCallable;
42 
43 public:
44  using BranchTypes_t = RemoveFirstParameter_t<typename CallableTraits<F>::arg_types>;
45  ForeachSlotHelper(F &&f) : fCallable(f) {}
46  ForeachSlotHelper(ForeachSlotHelper &&) = default;
47  ForeachSlotHelper(const ForeachSlotHelper &) = delete;
48 
49  void InitSlot(TTreeReader *, unsigned int) {}
50 
51  template <typename... Args>
52  void Exec(unsigned int slot, Args &&... args)
53  {
54  // check that the decayed types of Args are the same as the branch types
55  static_assert(std::is_same<TypeList<typename std::decay<Args>::type...>, BranchTypes_t>::value, "");
56  fCallable(slot, std::forward<Args>(args)...);
57  }
58 
59  void Finalize() { /* noop */}
60 };
61 
62 class CountHelper {
63  const std::shared_ptr<ULong64_t> fResultCount;
64  std::vector<ULong64_t> fCounts;
65 
66 public:
67  using BranchTypes_t = TypeList<>;
68  CountHelper(const std::shared_ptr<ULong64_t> &resultCount, const unsigned int nSlots);
69  CountHelper(CountHelper &&) = default;
70  CountHelper(const CountHelper &) = delete;
71  void InitSlot(TTreeReader *, unsigned int) {}
72  void Exec(unsigned int slot);
73  void Finalize();
74  ULong64_t &PartialUpdate(unsigned int slot);
75 };
76 
77 class FillHelper {
78  // this sets a total initial size of 16 MB for the buffers (can increase)
79  static constexpr unsigned int fgTotalBufSize = 2097152;
80  using BufEl_t = double;
81  using Buf_t = std::vector<BufEl_t>;
82 
83  std::vector<Buf_t> fBuffers;
84  std::vector<Buf_t> fWBuffers;
85  const std::shared_ptr<Hist_t> fResultHist;
86  unsigned int fNSlots;
87  unsigned int fBufSize;
88  /// Histograms containing "snapshots" of partial results. Non-null only if a registered callback requires it.
89  std::vector<std::unique_ptr<Hist_t>> fPartialHists;
90  Buf_t fMin;
91  Buf_t fMax;
92 
93  void UpdateMinMax(unsigned int slot, double v);
94 
95 public:
96  FillHelper(const std::shared_ptr<Hist_t> &h, const unsigned int nSlots);
97  FillHelper(FillHelper &&) = default;
98  FillHelper(const FillHelper &) = delete;
99  void InitSlot(TTreeReader *, unsigned int) {}
100  void Exec(unsigned int slot, double v);
101  void Exec(unsigned int slot, double v, double w);
102 
103  template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0>
104  void Exec(unsigned int slot, const T &vs)
105  {
106  auto &thisBuf = fBuffers[slot];
107  for (auto &v : vs) {
108  UpdateMinMax(slot, v);
109  thisBuf.emplace_back(v); // TODO: Can be optimised in case T == BufEl_t
110  }
111  }
112 
113  template <typename T, typename W,
114  typename std::enable_if<IsContainer<T>::value && IsContainer<W>::value, int>::type = 0>
115  void Exec(unsigned int slot, const T &vs, const W &ws)
116  {
117  auto &thisBuf = fBuffers[slot];
118  for (auto &v : vs) {
119  UpdateMinMax(slot, v);
120  thisBuf.emplace_back(v); // TODO: Can be optimised in case T == BufEl_t
121  }
122 
123  auto &thisWBuf = fWBuffers[slot];
124  for (auto &w : ws) {
125  thisWBuf.emplace_back(w); // TODO: Can be optimised in case T == BufEl_t
126  }
127  }
128 
129  Hist_t &PartialUpdate(unsigned int);
130 
131  void Finalize();
132 };
133 
134 extern template void FillHelper::Exec(unsigned int, const std::vector<float> &);
135 extern template void FillHelper::Exec(unsigned int, const std::vector<double> &);
136 extern template void FillHelper::Exec(unsigned int, const std::vector<char> &);
137 extern template void FillHelper::Exec(unsigned int, const std::vector<int> &);
138 extern template void FillHelper::Exec(unsigned int, const std::vector<unsigned int> &);
139 extern template void FillHelper::Exec(unsigned int, const std::vector<float> &, const std::vector<float> &);
140 extern template void FillHelper::Exec(unsigned int, const std::vector<double> &, const std::vector<double> &);
141 extern template void FillHelper::Exec(unsigned int, const std::vector<char> &, const std::vector<char> &);
142 extern template void FillHelper::Exec(unsigned int, const std::vector<int> &, const std::vector<int> &);
143 extern template void
144 FillHelper::Exec(unsigned int, const std::vector<unsigned int> &, const std::vector<unsigned int> &);
145 
146 template <typename HIST = Hist_t>
147 class FillTOHelper {
148  std::unique_ptr<TThreadedObject<HIST>> fTo;
149 
150 public:
151  FillTOHelper(FillTOHelper &&) = default;
152  FillTOHelper(const FillTOHelper &) = delete;
153 
154  FillTOHelper(const std::shared_ptr<HIST> &h, const unsigned int nSlots) : fTo(new TThreadedObject<HIST>(*h))
155  {
156  fTo->SetAtSlot(0, h);
157  // Initialise all other slots
158  for (unsigned int i = 0; i < nSlots; ++i) {
159  fTo->GetAtSlot(i);
160  }
161  }
162 
163  void InitSlot(TTreeReader *, unsigned int) {}
164 
165  void Exec(unsigned int slot, double x0) // 1D histos
166  {
167  fTo->GetAtSlotRaw(slot)->Fill(x0);
168  }
169 
170  void Exec(unsigned int slot, double x0, double x1) // 1D weighted and 2D histos
171  {
172  fTo->GetAtSlotRaw(slot)->Fill(x0, x1);
173  }
174 
175  void Exec(unsigned int slot, double x0, double x1, double x2) // 2D weighted and 3D histos
176  {
177  fTo->GetAtSlotRaw(slot)->Fill(x0, x1, x2);
178  }
179 
180  void Exec(unsigned int slot, double x0, double x1, double x2, double x3) // 3D weighted histos
181  {
182  fTo->GetAtSlotRaw(slot)->Fill(x0, x1, x2, x3);
183  }
184 
185  template <typename X0, typename std::enable_if<IsContainer<X0>::value, int>::type = 0>
186  void Exec(unsigned int slot, const X0 &x0s)
187  {
188  auto thisSlotH = fTo->GetAtSlotRaw(slot);
189  for (auto &x0 : x0s) {
190  thisSlotH->Fill(x0); // TODO: Can be optimised in case T == vector<double>
191  }
192  }
193 
194  template <typename X0, typename X1,
195  typename std::enable_if<IsContainer<X0>::value && IsContainer<X1>::value, int>::type = 0>
196  void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s)
197  {
198  auto thisSlotH = fTo->GetAtSlotRaw(slot);
199  if (x0s.size() != x1s.size()) {
200  throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
201  }
202  auto x0sIt = std::begin(x0s);
203  const auto x0sEnd = std::end(x0s);
204  auto x1sIt = std::begin(x1s);
205  for (; x0sIt != x0sEnd; x0sIt++, x1sIt++) {
206  thisSlotH->Fill(*x0sIt, *x1sIt); // TODO: Can be optimised in case T == vector<double>
207  }
208  }
209 
210  template <typename X0, typename X1, typename X2,
211  typename std::enable_if<IsContainer<X0>::value && IsContainer<X1>::value && IsContainer<X2>::value,
212  int>::type = 0>
213  void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const X2 &x2s)
214  {
215  auto thisSlotH = fTo->GetAtSlotRaw(slot);
216  if (!(x0s.size() == x1s.size() && x1s.size() == x2s.size())) {
217  throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
218  }
219  auto x0sIt = std::begin(x0s);
220  const auto x0sEnd = std::end(x0s);
221  auto x1sIt = std::begin(x1s);
222  auto x2sIt = std::begin(x2s);
223  for (; x0sIt != x0sEnd; x0sIt++, x1sIt++, x2sIt++) {
224  thisSlotH->Fill(*x0sIt, *x1sIt, *x2sIt); // TODO: Can be optimised in case T == vector<double>
225  }
226  }
227  template <typename X0, typename X1, typename X2, typename X3,
228  typename std::enable_if<IsContainer<X0>::value && IsContainer<X1>::value && IsContainer<X2>::value &&
230  int>::type = 0>
231  void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const X2 &x2s, const X3 &x3s)
232  {
233  auto thisSlotH = fTo->GetAtSlotRaw(slot);
234  if (!(x0s.size() == x1s.size() && x1s.size() == x2s.size() && x1s.size() == x3s.size())) {
235  throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
236  }
237  auto x0sIt = std::begin(x0s);
238  const auto x0sEnd = std::end(x0s);
239  auto x1sIt = std::begin(x1s);
240  auto x2sIt = std::begin(x2s);
241  auto x3sIt = std::begin(x3s);
242  for (; x0sIt != x0sEnd; x0sIt++, x1sIt++, x2sIt++, x3sIt++) {
243  thisSlotH->Fill(*x0sIt, *x1sIt, *x2sIt, *x3sIt); // TODO: Can be optimised in case T == vector<double>
244  }
245  }
246  void Finalize() { fTo->Merge(); }
247 
248  HIST &PartialUpdate(unsigned int slot) { return *fTo->GetAtSlotRaw(slot); }
249 };
250 
251 // In case of the take helper we have 4 cases:
252 // 1. The column is not an TArrayBranch, the collection is not a vector
253 // 2. The column is not an TArrayBranch, the collection is a vector
254 // 3. The column is an TArrayBranch, the collection is not a vector
255 // 4. The column is an TArrayBranch, the collection is a vector
256 
257 // Case 1.: The column is not an TArrayBranch, the collection is not a vector
258 // No optimisations, no transformations: just copies.
259 template <typename RealT_t, typename T, typename COLL>
260 class TakeHelper {
261  std::vector<std::shared_ptr<COLL>> fColls;
262 
263 public:
264  using BranchTypes_t = TypeList<T>;
265  TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
266  {
267  fColls.emplace_back(resultColl);
268  for (unsigned int i = 1; i < nSlots; ++i)
269  fColls.emplace_back(std::make_shared<COLL>());
270  }
271  TakeHelper(TakeHelper &&) = default;
272  TakeHelper(const TakeHelper &) = delete;
273 
274  void InitSlot(TTreeReader *, unsigned int) {}
275 
276  void Exec(unsigned int slot, T &v) { fColls[slot]->emplace_back(v); }
277 
278  void Finalize()
279  {
280  auto rColl = fColls[0];
281  for (unsigned int i = 1; i < fColls.size(); ++i) {
282  auto &coll = fColls[i];
283  for (T &v : *coll) {
284  rColl->emplace_back(v);
285  }
286  }
287  }
288 
289  COLL &PartialUpdate(unsigned int slot) { return *fColls[slot].get(); }
290 };
291 
292 // Case 2.: The column is not an TArrayBranch, the collection is a vector
293 // Optimisations, no transformations: just copies.
294 template <typename RealT_t, typename T>
295 class TakeHelper<RealT_t, T, std::vector<T>> {
296  std::vector<std::shared_ptr<std::vector<T>>> fColls;
297 
298 public:
299  using BranchTypes_t = TypeList<T>;
300  TakeHelper(const std::shared_ptr<std::vector<T>> &resultColl, const unsigned int nSlots)
301  {
302  fColls.emplace_back(resultColl);
303  for (unsigned int i = 1; i < nSlots; ++i) {
304  auto v = std::make_shared<std::vector<T>>();
305  v->reserve(1024);
306  fColls.emplace_back(v);
307  }
308  }
309  TakeHelper(TakeHelper &&) = default;
310  TakeHelper(const TakeHelper &) = delete;
311 
312  void InitSlot(TTreeReader *, unsigned int) {}
313 
314  void Exec(unsigned int slot, T &v) { fColls[slot]->emplace_back(v); }
315 
316  // This is optimised to treat vectors
317  void Finalize()
318  {
319  ULong64_t totSize = 0;
320  for (auto &coll : fColls)
321  totSize += coll->size();
322  auto rColl = fColls[0];
323  rColl->reserve(totSize);
324  for (unsigned int i = 1; i < fColls.size(); ++i) {
325  auto &coll = fColls[i];
326  rColl->insert(rColl->end(), coll->begin(), coll->end());
327  }
328  }
329 
330  std::vector<T> &PartialUpdate(unsigned int slot) { return *fColls[slot]; }
331 };
332 
333 // Case 3.: The column is a TArrayBranch, the collection is not a vector
334 // No optimisations, transformations from TArrayBranchs to vectors
335 template <typename RealT_t, typename COLL>
336 class TakeHelper<RealT_t, TArrayBranch<RealT_t>, COLL> {
337  std::vector<std::shared_ptr<COLL>> fColls;
338 
339 public:
340  using BranchTypes_t = TypeList<TArrayBranch<RealT_t>>;
341  TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
342  {
343  fColls.emplace_back(resultColl);
344  for (unsigned int i = 1; i < nSlots; ++i)
345  fColls.emplace_back(std::make_shared<COLL>());
346  }
347  TakeHelper(TakeHelper &&) = default;
348  TakeHelper(const TakeHelper &) = delete;
349 
350  void InitSlot(TTreeReader *, unsigned int) {}
351 
352  void Exec(unsigned int slot, TArrayBranch<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
353 
354  void Finalize()
355  {
356  auto rColl = fColls[0];
357  for (unsigned int i = 1; i < fColls.size(); ++i) {
358  auto &coll = fColls[i];
359  for (auto &v : *coll) {
360  rColl->emplace_back(v);
361  }
362  }
363  }
364 };
365 
366 // Case 4.: The column is an TArrayBranch, the collection is a vector
367 // Optimisations, transformations from TArrayBranchs to vectors
368 template <typename RealT_t>
369 class TakeHelper<RealT_t, TArrayBranch<RealT_t>, std::vector<RealT_t>> {
370  std::vector<std::shared_ptr<std::vector<std::vector<RealT_t>>>> fColls;
371 
372 public:
373  using BranchTypes_t = TypeList<TArrayBranch<RealT_t>>;
374  TakeHelper(const std::shared_ptr<std::vector<std::vector<RealT_t>>> &resultColl, const unsigned int nSlots)
375  {
376  fColls.emplace_back(resultColl);
377  for (unsigned int i = 1; i < nSlots; ++i) {
378  auto v = std::make_shared<std::vector<RealT_t>>();
379  v->reserve(1024);
380  fColls.emplace_back(v);
381  }
382  }
383  TakeHelper(TakeHelper &&) = default;
384  TakeHelper(const TakeHelper &) = delete;
385 
386  void InitSlot(TTreeReader *, unsigned int) {}
387 
388  void Exec(unsigned int slot, TArrayBranch<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
389 
390  // This is optimised to treat vectors
391  void Finalize()
392  {
393  ULong64_t totSize = 0;
394  for (auto &coll : fColls)
395  totSize += coll->size();
396  auto rColl = fColls[0];
397  rColl->reserve(totSize);
398  for (unsigned int i = 1; i < fColls.size(); ++i) {
399  auto &coll = fColls[i];
400  rColl->insert(rColl->end(), coll->begin(), coll->end());
401  }
402  }
403 };
404 
405 template <typename F, typename T>
406 class ReduceHelper {
407  F fReduceFun;
408  const std::shared_ptr<T> fReduceRes;
409  std::vector<T> fReduceObjs;
410 
411 public:
412  using BranchTypes_t = TypeList<T>;
413  ReduceHelper(F &&f, const std::shared_ptr<T> &reduceRes, const unsigned int nSlots)
414  : fReduceFun(std::move(f)), fReduceRes(reduceRes), fReduceObjs(nSlots, *reduceRes)
415  {
416  }
417  ReduceHelper(ReduceHelper &&) = default;
418  ReduceHelper(const ReduceHelper &) = delete;
419 
420  void InitSlot(TTreeReader *, unsigned int) {}
421 
422  void Exec(unsigned int slot, const T &value) { fReduceObjs[slot] = fReduceFun(fReduceObjs[slot], value); }
423 
424  void Finalize()
425  {
426  for (auto &t : fReduceObjs)
427  *fReduceRes = fReduceFun(*fReduceRes, t);
428  }
429 
430  T &PartialUpdate(unsigned int slot) { return fReduceObjs[slot]; }
431 };
432 
433 template <typename ResultType>
434 class MinHelper {
435  const std::shared_ptr<ResultType> fResultMin;
436  std::vector<ResultType> fMins;
437 
438 public:
439  MinHelper(MinHelper &&) = default;
440  MinHelper(const std::shared_ptr<ResultType> &minVPtr, const unsigned int nSlots)
441  : fResultMin(minVPtr), fMins(nSlots, std::numeric_limits<ResultType>::max())
442  {
443  }
444 
445  void Exec(unsigned int slot, ResultType v) { fMins[slot] = std::min(v, fMins[slot]); }
446 
447  void InitSlot(TTreeReader *, unsigned int) {}
448 
449  template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0>
450  void Exec(unsigned int slot, const T &vs)
451  {
452  for (auto &&v : vs)
453  fMins[slot] = std::min(v, fMins[slot]);
454  }
455 
456  void Finalize()
457  {
458  *fResultMin = std::numeric_limits<ResultType>::max();
459  for (auto &m : fMins)
460  *fResultMin = std::min(m, *fResultMin);
461  }
462 
463  ResultType &PartialUpdate(unsigned int slot) { return fMins[slot]; }
464 };
465 
466 // TODO
467 // extern template void MinHelper::Exec(unsigned int, const std::vector<float> &);
468 // extern template void MinHelper::Exec(unsigned int, const std::vector<double> &);
469 // extern template void MinHelper::Exec(unsigned int, const std::vector<char> &);
470 // extern template void MinHelper::Exec(unsigned int, const std::vector<int> &);
471 // extern template void MinHelper::Exec(unsigned int, const std::vector<unsigned int> &);
472 
473 template <typename ResultType>
474 class MaxHelper {
475  const std::shared_ptr<ResultType> fResultMax;
476  std::vector<ResultType> fMaxs;
477 
478 public:
479  MaxHelper(MaxHelper &&) = default;
480  MaxHelper(const MaxHelper &) = delete;
481  MaxHelper(const std::shared_ptr<ResultType> &maxVPtr, const unsigned int nSlots)
482  : fResultMax(maxVPtr), fMaxs(nSlots, std::numeric_limits<ResultType>::lowest())
483  {
484  }
485 
486  void InitSlot(TTreeReader *, unsigned int) {}
487  void Exec(unsigned int slot, ResultType v) { fMaxs[slot] = std::max(v, fMaxs[slot]); }
488 
489  template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0>
490  void Exec(unsigned int slot, const T &vs)
491  {
492  for (auto &&v : vs)
493  fMaxs[slot] = std::max((ResultType)v, fMaxs[slot]);
494  }
495 
496  void Finalize()
497  {
498  *fResultMax = std::numeric_limits<ResultType>::lowest();
499  for (auto &m : fMaxs) {
500  *fResultMax = std::max(m, *fResultMax);
501  }
502  }
503 
504  ResultType &PartialUpdate(unsigned int slot) { return fMaxs[slot]; }
505 };
506 
507 // TODO
508 // extern template void MaxHelper::Exec(unsigned int, const std::vector<float> &);
509 // extern template void MaxHelper::Exec(unsigned int, const std::vector<double> &);
510 // extern template void MaxHelper::Exec(unsigned int, const std::vector<char> &);
511 // extern template void MaxHelper::Exec(unsigned int, const std::vector<int> &);
512 // extern template void MaxHelper::Exec(unsigned int, const std::vector<unsigned int> &);
513 
514 template <typename ResultType>
515 class SumHelper {
516  const std::shared_ptr<ResultType> fResultSum;
517  std::vector<ResultType> fSums;
518 
519 public:
520  SumHelper(SumHelper &&) = default;
521  SumHelper(const SumHelper &) = delete;
522  SumHelper(const std::shared_ptr<ResultType> &sumVPtr, const unsigned int nSlots)
523  : fResultSum(sumVPtr), fSums(nSlots, *sumVPtr - *sumVPtr)
524  {
525  }
526 
527  void InitSlot(TTreeReader *, unsigned int) {}
528  void Exec(unsigned int slot, ResultType v) { fSums[slot] += v; }
529 
530  template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0>
531  void Exec(unsigned int slot, const T &vs)
532  {
533  for (auto &&v : vs)
534  fSums[slot] += static_cast<ResultType>(v);
535  }
536 
537  void Finalize()
538  {
539  for (auto &m : fSums)
540  *fResultSum += m;
541  }
542 
543  ResultType &PartialUpdate(unsigned int slot) { return fSums[slot]; }
544 };
545 
546 class MeanHelper {
547  const std::shared_ptr<double> fResultMean;
548  std::vector<ULong64_t> fCounts;
549  std::vector<double> fSums;
550  std::vector<double> fPartialMeans;
551 
552 public:
553  MeanHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
554  MeanHelper(MeanHelper &&) = default;
555  MeanHelper(const MeanHelper &) = delete;
556  void InitSlot(TTreeReader *, unsigned int) {}
557  void Exec(unsigned int slot, double v);
558 
559  template <typename T, typename std::enable_if<IsContainer<T>::value, int>::type = 0>
560  void Exec(unsigned int slot, const T &vs)
561  {
562  for (auto &&v : vs) {
563  fSums[slot] += v;
564  fCounts[slot]++;
565  }
566  }
567 
568  void Finalize();
569 
570  double &PartialUpdate(unsigned int slot);
571 };
572 
573 extern template void MeanHelper::Exec(unsigned int, const std::vector<float> &);
574 extern template void MeanHelper::Exec(unsigned int, const std::vector<double> &);
575 extern template void MeanHelper::Exec(unsigned int, const std::vector<char> &);
576 extern template void MeanHelper::Exec(unsigned int, const std::vector<int> &);
577 extern template void MeanHelper::Exec(unsigned int, const std::vector<unsigned int> &);
578 
579 template <typename T>
580 struct AddRefIfNotArrayBranch {
581  using type = T &;
582 };
583 
584 template <typename T>
585 struct AddRefIfNotArrayBranch<TArrayBranch<T>> {
586  using type = TArrayBranch<T>;
587 };
588 
589 template <typename T>
590 using AddRefIfNotArrayBranch_t = typename AddRefIfNotArrayBranch<T>::type;
591 
592 /// Helper function for SnapshotHelper and SnapshotHelperMT. It creates new branches for the output TTree of a Snapshot.
593 template <typename T>
594 void SetBranchesHelper(TTree * /*inputTree*/, TTree &outputTree, const std::string & /*validName*/,
595  const std::string &name, T *address)
596 {
597  outputTree.Branch(name.c_str(), address);
598 }
599 
600 /// Helper function for SnapshotHelper and SnapshotHelperMT. It creates new branches for the output TTree of a Snapshot.
601 /// This overload is called for columns of type `TArrayBranch<T>`. For TDF, these represent c-style arrays in ROOT
602 /// files, so we are sure that there are input trees to which we can ask the correct branch title
603 template <typename T>
604 void SetBranchesHelper(TTree *inputTree, TTree &outputTree, const std::string &validName, const std::string &name,
605  TArrayBranch<T> *ab)
606 {
607  auto *const inputBranch = inputTree->GetBranch(validName.c_str());
608  auto *const leaf = static_cast<TLeaf *>(inputBranch->GetListOfLeaves()->UncheckedAt(0));
609  const auto bname = leaf->GetName();
610  const auto counterStr =
611  leaf->GetLeafCount() ? std::string(leaf->GetLeafCount()->GetName()) : std::to_string(leaf->GetLenStatic());
612  const auto btype = leaf->GetTypeName();
613  const auto rootbtype = TypeName2ROOTTypeName(btype);
614  const auto leaflist = std::string(bname) + "[" + counterStr + "]/" + rootbtype;
615  auto *const outputBranch = outputTree.Branch(name.c_str(), ab->GetData(), leaflist.c_str());
616  outputBranch->SetTitle(inputBranch->GetTitle());
617 }
618 
619 /// Helper object for a single-thread Snapshot action
620 template <typename... BranchTypes>
621 class SnapshotHelper {
622  std::unique_ptr<TFile> fOutputFile;
623  std::unique_ptr<TTree> fOutputTree; // must be a ptr because TTrees are not copy/move constructible
624  bool fIsFirstEvent{true};
625  const ColumnNames_t fValidBranchNames; // This contains the resolved aliases
626  const ColumnNames_t fBranchNames;
627  TTree *fInputTree = nullptr; // Current input tree. Set at initialization time (`InitSlot`)
628 
629 public:
630  SnapshotHelper(std::string_view filename, std::string_view dirname, std::string_view treename,
631  const ColumnNames_t &vbnames, const ColumnNames_t &bnames, const TSnapshotOptions &options)
632  : fOutputFile(TFile::Open(std::string(filename).c_str(), options.fMode.c_str(), /*ftitle=*/"",
633  ROOT::CompressionSettings(options.fCompressionAlgorithm, options.fCompressionLevel))),
634  fValidBranchNames(vbnames), fBranchNames(bnames)
635  {
636  if (!dirname.empty()) {
637  std::string dirnameStr(dirname);
638  fOutputFile->mkdir(dirnameStr.c_str());
639  fOutputFile->cd(dirnameStr.c_str());
640  }
641  std::string treenameStr(treename);
642  fOutputTree.reset(
643  new TTree(treenameStr.c_str(), treenameStr.c_str(), options.fSplitLevel, /*dir=*/fOutputFile.get()));
644 
645  if (options.fAutoFlush)
646  fOutputTree->SetAutoFlush(options.fAutoFlush);
647  }
648 
649  SnapshotHelper(const SnapshotHelper &) = delete;
650  SnapshotHelper(SnapshotHelper &&) = default;
651 
652  void InitSlot(TTreeReader *r, unsigned int /* slot */)
653  {
654  if (!r) // empty source, nothing to do
655  return;
656  fInputTree = r->GetTree();
657  // AddClone guarantees that if the input file changes the branches of the output tree are updated with the new
658  // addresses of the branch values
659  fInputTree->AddClone(fOutputTree.get());
660  }
661 
662  void Exec(unsigned int /* slot */, AddRefIfNotArrayBranch_t<BranchTypes>... values)
663  {
664  if (fIsFirstEvent) {
665  using ind_t = GenStaticSeq_t<sizeof...(BranchTypes)>;
666  SetBranches(values..., ind_t());
667  }
668  fOutputTree->Fill();
669  }
670 
671  template <int... S>
672  void SetBranches(AddRefIfNotArrayBranch_t<BranchTypes>... values, StaticSeq<S...> /*dummy*/)
673  {
674  // hack to call TTree::Branch on all variadic template arguments
675  int expander[] = {
676  (SetBranchesHelper(fInputTree, *fOutputTree, fValidBranchNames[S], fBranchNames[S], &values), 0)..., 0};
677  (void)expander; // avoid unused variable warnings for older compilers such as gcc 4.9
678  fIsFirstEvent = false;
679  }
680 
681  void Finalize() { fOutputTree->Write(); }
682 };
683 
684 /// Helper object for a multi-thread Snapshot action
685 template <typename... BranchTypes>
686 class SnapshotHelperMT {
687  const unsigned int fNSlots;
688  std::unique_ptr<ROOT::Experimental::TBufferMerger> fMerger; // must use a ptr because TBufferMerger is not movable
689  std::vector<std::shared_ptr<ROOT::Experimental::TBufferMergerFile>> fOutputFiles;
690  std::vector<TTree *> fOutputTrees; // ROOT will own/manage these TTrees, must not delete
691  std::vector<int> fIsFirstEvent; // vector<bool> is evil
692  const std::string fDirName; // name of TFile subdirectory in which output must be written (possibly empty)
693  const std::string fTreeName; // name of output tree
694  const TSnapshotOptions fOptions; // struct holding options to pass down to TFile and TTree in this action
695  const ColumnNames_t fValidBranchNames; // This contains the resolved aliases
696  const ColumnNames_t fBranchNames;
697  std::vector<TTree *> fInputTrees; // Current input trees. Set at initialization time (`InitSlot`)
698 
699 public:
700  using BranchTypes_t = TypeList<BranchTypes...>;
701  SnapshotHelperMT(const unsigned int nSlots, std::string_view filename, std::string_view dirname,
702  std::string_view treename, const ColumnNames_t &vbnames, const ColumnNames_t &bnames,
703  const TSnapshotOptions &options)
704  : fNSlots(nSlots), fMerger(new ROOT::Experimental::TBufferMerger(
705  std::string(filename).c_str(), options.fMode.c_str(),
706  ROOT::CompressionSettings(options.fCompressionAlgorithm, options.fCompressionLevel))),
707  fOutputFiles(fNSlots), fOutputTrees(fNSlots, nullptr), fIsFirstEvent(fNSlots, 1), fDirName(dirname),
708  fTreeName(treename), fOptions(options), fValidBranchNames(vbnames), fBranchNames(bnames), fInputTrees(fNSlots)
709  {
710  }
711  SnapshotHelperMT(const SnapshotHelperMT &) = delete;
712  SnapshotHelperMT(SnapshotHelperMT &&) = default;
713 
714  void InitSlot(TTreeReader *r, unsigned int slot)
715  {
716  ::TDirectory::TContext c; // do not let tasks change the thread-local gDirectory
717  if (!fOutputTrees[slot]) {
718  // first time this thread executes something, let's create a TBufferMerger output directory
719  fOutputFiles[slot] = fMerger->GetFile();
720  } else {
721  // this thread is now re-executing the task, let's flush the current contents of the TBufferMergerFile
722  fOutputFiles[slot]->Write();
723  }
724  TDirectory *treeDirectory = fOutputFiles[slot].get();
725  if (!fDirName.empty()) {
726  treeDirectory = fOutputFiles[slot]->mkdir(fDirName.c_str());
727  }
728  // re-create output tree as we need to create its branches again, with new input variables
729  // TODO we could instead create the output tree and its branches, change addresses of input variables in each task
730  fOutputTrees[slot] = new TTree(fTreeName.c_str(), fTreeName.c_str(), fOptions.fSplitLevel, /*dir=*/treeDirectory);
731  fOutputTrees[slot]->ResetBit(kMustCleanup); // do not mingle with the thread-unsafe gListOfCleanups
732  if (fOptions.fAutoFlush)
733  fOutputTrees[slot]->SetAutoFlush(fOptions.fAutoFlush);
734  if (r) {
735  // not an empty-source TDF
736  fInputTrees[slot] = r->GetTree();
737  // AddClone guarantees that if the input file changes the branches of the output tree are updated with the new
738  // addresses of the branch values
739  fInputTrees[slot]->AddClone(fOutputTrees[slot]);
740  }
741  fIsFirstEvent[slot] = 1; // reset first event flag for this slot
742  }
743 
744  void Exec(unsigned int slot, AddRefIfNotArrayBranch_t<BranchTypes>... values)
745  {
746  if (fIsFirstEvent[slot]) {
747  using ind_t = GenStaticSeq_t<sizeof...(BranchTypes)>;
748  SetBranches(slot, values..., ind_t());
749  fIsFirstEvent[slot] = 0;
750  }
751  fOutputTrees[slot]->Fill();
752  auto entries = fOutputTrees[slot]->GetEntries();
753  auto autoFlush = fOutputTrees[slot]->GetAutoFlush();
754  if ((autoFlush > 0) && (entries % autoFlush == 0))
755  fOutputFiles[slot]->Write();
756  }
757 
758  template <int... S>
759  void SetBranches(unsigned int slot, AddRefIfNotArrayBranch_t<BranchTypes>... values, StaticSeq<S...> /*dummy*/)
760  {
761  // hack to call TTree::Branch on all variadic template arguments
762  int expander[] = {
763  (SetBranchesHelper(fInputTrees[slot], *fOutputTrees[slot], fValidBranchNames[S], fBranchNames[S], &values),
764  0)...,
765  0};
766  (void)expander; // avoid unused variable warnings for older compilers such as gcc 4.9
767  }
768 
769  void Finalize()
770  {
771  for (auto &file : fOutputFiles) {
772  if (file)
773  file->Write();
774  }
775  }
776 };
777 
778 } // end of NS TDF
779 } // end of NS Internal
780 } // end of NS ROOT
781 
782 /// \endcond
783 
784 #endif
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
A TLeaf describes individual elements of a TBranch See TBranch structure in TTree.
Definition: TLeaf.h:32
A collection of options to steer the creation of the dataset on file.
Definition: TDFUtils.hxx:38
char TypeName2ROOTTypeName(const std::string &b)
Convert type name (e.g.
Definition: TDFUtils.cxx:185
auto * m
Definition: textangle.C:8
TTreeReader is a simple, robust and fast interface to read values from a TTree, TChain or TNtuple...
Definition: TTreeReader.h:43
basic_string_view< char > string_view
Definition: RStringView.h:35
Namespace for new ROOT classes and functions.
Definition: StringConv.hxx:21
void ws()
Definition: ws.C:62
double T(double x)
Definition: ChebyshevPol.h:34
TH1 * h
Definition: legend2.C:5
STL namespace.
TTree * GetTree() const
Definition: TTreeReader.h:162
static const double x2[5]
When using TDataFrame to read data from a ROOT file, users can specify that the type of a branch is T...
RooArgSet S(const RooAbsArg &v1)
#define F(x, y, z)
ROOT::R::TRInterface & r
Definition: Object.C:4
SVector< double, 2 > v
Definition: Dict.h:5
Lightweight storage for a collection of types.
Definition: TypeTraits.hxx:27
static const double x1[5]
Describe directory structure in memory.
Definition: TDirectory.h:34
int type
Definition: TGX11.cxx:120
unsigned long long ULong64_t
Definition: RtypesCore.h:70
ROOT type_traits extensions.
Definition: TypeTraits.hxx:23
typedef void((*Func_t)())
std::shared_ptr< TBufferMergerFile > GetFile()
Returns a TBufferMergerFile to which data can be written.
Definition: file.py:1
THist< 1, double, THistStatContent, THistStatUncertainty > TH1D
Definition: THist.hxx:284
Check for container traits.
Definition: TypeTraits.hxx:98
char name[80]
Definition: TGX11.cxx:109
static const double x3[11]