Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ActionHelpers.hxx
Go to the documentation of this file.
1/**
2 \file ROOT/RDF/ActionHelpers.hxx
3 \ingroup dataframe
4 \author Enrico Guiraud, CERN
5 \author Danilo Piparo, CERN
6 \date 2016-12
7 \author Vincenzo Eduardo Padulano
8 \date 2020-06
9*/
10
11/*************************************************************************
12 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. *
13 * All rights reserved. *
14 * *
15 * For the licensing terms see $ROOTSYS/LICENSE. *
16 * For the list of contributors see $ROOTSYS/README/CREDITS. *
17 *************************************************************************/
18
19#ifndef ROOT_RDFOPERATIONS
20#define ROOT_RDFOPERATIONS
21
22#include "Compression.h"
24#include "ROOT/RStringView.hxx"
25#include "ROOT/RVec.hxx"
26#include "ROOT/TBufferMerger.hxx" // for SnapshotHelper
28#include "ROOT/RDF/Utils.hxx"
29#include "ROOT/RMakeUnique.hxx"
31#include "ROOT/TypeTraits.hxx"
32#include "ROOT/RDF/RDisplay.hxx"
33#include "RtypesCore.h"
34#include "TBranch.h"
35#include "TClassEdit.h"
36#include "TClassRef.h"
37#include "TDirectory.h"
38#include "TError.h" // for R__ASSERT, Warning
39#include "TFile.h" // for SnapshotHelper
40#include "TH1.h"
41#include "TGraph.h"
42#include "TLeaf.h"
43#include "TObject.h"
44#include "TTree.h"
45#include "TTreeReader.h" // for SnapshotHelper
47
48#include <algorithm>
49#include <functional>
50#include <limits>
51#include <memory>
52#include <stdexcept>
53#include <string>
54#include <type_traits>
55#include <vector>
56#include <iomanip>
57#include <numeric> // std::accumulate in MeanHelper
58
59/// \cond HIDDEN_SYMBOLS
60
61namespace ROOT {
62namespace Detail {
63namespace RDF {
64template <typename Helper>
65class RActionImpl {
66public:
67 virtual ~RActionImpl() = default;
68 // call Helper::FinalizeTask if present, do nothing otherwise
69 template <typename T = Helper>
70 auto CallFinalizeTask(unsigned int slot) -> decltype(&T::FinalizeTask, void())
71 {
72 static_cast<Helper *>(this)->FinalizeTask(slot);
73 }
74
75 template <typename... Args>
76 void CallFinalizeTask(unsigned int, Args...) {}
77
78 // Helper functions for RMergeableValue
79 virtual std::unique_ptr<RMergeableValueBase> GetMergeableValue() const
80 {
81 throw std::logic_error("`GetMergeableValue` is not implemented for this type of action.");
82 }
83
84 virtual std::function<void(unsigned int)> GetDataBlockCallback() { return {}; }
85};
86
87} // namespace RDF
88} // namespace Detail
89
90namespace Internal {
91namespace RDF {
92using namespace ROOT::TypeTraits;
93using namespace ROOT::VecOps;
94using namespace ROOT::RDF;
95using namespace ROOT::Detail::RDF;
96
97using Hist_t = ::TH1D;
98
99class RBranchSet {
100 std::vector<TBranch *> fBranches;
101 std::vector<std::string> fNames;
102
103public:
104 TBranch *Get(const std::string &name) const
105 {
106 auto it = std::find(fNames.begin(), fNames.end(), name);
107 if (it == fNames.end())
108 return nullptr;
109 return fBranches[std::distance(fNames.begin(), it)];
110 }
111
112 void Insert(const std::string &name, TBranch *address)
113 {
114 if (address == nullptr) {
115 throw std::logic_error("Trying to insert a null branch address.");
116 }
117 if (std::find(fBranches.begin(), fBranches.end(), address) != fBranches.end()) {
118 throw std::logic_error("Trying to insert a branch address that's already present.");
119 }
120 if (std::find(fNames.begin(), fNames.end(), name) != fNames.end()) {
121 throw std::logic_error("Trying to insert a branch name that's already present.");
122 }
123 fNames.emplace_back(name);
124 fBranches.emplace_back(address);
125 }
126
127 void Clear()
128 {
129 fBranches.clear();
130 fNames.clear();
131 }
132};
133
134/// The container type for each thread's partial result in an action helper
135// We have to avoid to instantiate std::vector<bool> as that makes it impossible to return a reference to one of
136// the thread-local results. In addition, a common definition for the type of the container makes it easy to swap
137// the type of the underlying container if e.g. we see problems with false sharing of the thread-local results..
138template <typename T>
139using Results = typename std::conditional<std::is_same<T, bool>::value, std::deque<T>, std::vector<T>>::type;
140
141template <typename F>
142class ForeachSlotHelper : public RActionImpl<ForeachSlotHelper<F>> {
143 F fCallable;
144
145public:
147 ForeachSlotHelper(F &&f) : fCallable(f) {}
148 ForeachSlotHelper(ForeachSlotHelper &&) = default;
149 ForeachSlotHelper(const ForeachSlotHelper &) = delete;
150
151 void InitTask(TTreeReader *, unsigned int) {}
152
153 template <typename... Args>
154 void Exec(unsigned int slot, Args &&... args)
155 {
156 // check that the decayed types of Args are the same as the branch types
157 static_assert(std::is_same<TypeList<typename std::decay<Args>::type...>, ColumnTypes_t>::value, "");
158 fCallable(slot, std::forward<Args>(args)...);
159 }
160
161 void Initialize() { /* noop */}
162
163 void Finalize() { /* noop */}
164
165 std::string GetActionName() { return "ForeachSlot"; }
166};
167
168class CountHelper : public RActionImpl<CountHelper> {
169 const std::shared_ptr<ULong64_t> fResultCount;
170 Results<ULong64_t> fCounts;
171
172public:
173 using ColumnTypes_t = TypeList<>;
174 CountHelper(const std::shared_ptr<ULong64_t> &resultCount, const unsigned int nSlots);
175 CountHelper(CountHelper &&) = default;
176 CountHelper(const CountHelper &) = delete;
177 void InitTask(TTreeReader *, unsigned int) {}
178 void Exec(unsigned int slot);
179 void Initialize() { /* noop */}
180 void Finalize();
181
182 // Helper functions for RMergeableValue
183 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
184 {
185 return std::make_unique<RMergeableCount>(*fResultCount);
186 }
187
188 ULong64_t &PartialUpdate(unsigned int slot);
189
190 std::string GetActionName() { return "Count"; }
191};
192
193template <typename ProxiedVal_t>
194class ReportHelper : public RActionImpl<ReportHelper<ProxiedVal_t>> {
195 const std::shared_ptr<RCutFlowReport> fReport;
196 // Here we have a weak pointer since we need to keep track of the validity
197 // of the proxied node. It can happen that the user does not trigger the
198 // event loop by looking into the RResultPtr and the chain goes out of scope
199 // before the Finalize method is invoked.
200 std::weak_ptr<ProxiedVal_t> fProxiedWPtr;
201 bool fReturnEmptyReport;
202
203public:
204 using ColumnTypes_t = TypeList<>;
205 ReportHelper(const std::shared_ptr<RCutFlowReport> &report, const std::shared_ptr<ProxiedVal_t> &pp, bool emptyRep)
206 : fReport(report), fProxiedWPtr(pp), fReturnEmptyReport(emptyRep){};
207 ReportHelper(ReportHelper &&) = default;
208 ReportHelper(const ReportHelper &) = delete;
209 void InitTask(TTreeReader *, unsigned int) {}
210 void Exec(unsigned int /* slot */) {}
211 void Initialize() { /* noop */}
212 void Finalize()
213 {
214 // We need the weak_ptr in order to avoid crashes at tear down
215 if (!fReturnEmptyReport && !fProxiedWPtr.expired())
216 fProxiedWPtr.lock()->Report(*fReport);
217 }
218
219 std::string GetActionName() { return "Report"; }
220};
221
222class FillHelper : public RActionImpl<FillHelper> {
223 // this sets a total initial size of 16 MB for the buffers (can increase)
224 static constexpr unsigned int fgTotalBufSize = 2097152;
225 using BufEl_t = double;
226 using Buf_t = std::vector<BufEl_t>;
227
228 std::vector<Buf_t> fBuffers;
229 std::vector<Buf_t> fWBuffers;
230 const std::shared_ptr<Hist_t> fResultHist;
231 unsigned int fNSlots;
232 unsigned int fBufSize;
233 /// Histograms containing "snapshots" of partial results. Non-null only if a registered callback requires it.
234 Results<std::unique_ptr<Hist_t>> fPartialHists;
235 Buf_t fMin;
236 Buf_t fMax;
237
238 void UpdateMinMax(unsigned int slot, double v);
239
240public:
241 FillHelper(const std::shared_ptr<Hist_t> &h, const unsigned int nSlots);
242 FillHelper(FillHelper &&) = default;
243 FillHelper(const FillHelper &) = delete;
244 void InitTask(TTreeReader *, unsigned int) {}
245 void Exec(unsigned int slot, double v);
246 void Exec(unsigned int slot, double v, double w);
247
248 template <typename T, typename std::enable_if<IsDataContainer<T>::value || std::is_same<T, std::string>::value, int>::type = 0>
249 void Exec(unsigned int slot, const T &vs)
250 {
251 auto &thisBuf = fBuffers[slot];
252 // range-based for results in warnings on some compilers due to vector<bool>'s custom reference type
253 for (auto v = vs.begin(); v != vs.end(); ++v) {
254 UpdateMinMax(slot, *v);
255 thisBuf.emplace_back(*v); // TODO: Can be optimised in case T == BufEl_t
256 }
257 }
258
259 template <typename T, typename W,
260 typename std::enable_if<IsDataContainer<T>::value && IsDataContainer<W>::value, int>::type = 0>
261 void Exec(unsigned int slot, const T &vs, const W &ws)
262 {
263 auto &thisBuf = fBuffers[slot];
264
265 for (auto &v : vs) {
266 UpdateMinMax(slot, v);
267 thisBuf.emplace_back(v);
268 }
269
270 auto &thisWBuf = fWBuffers[slot];
271 for (auto &w : ws) {
272 thisWBuf.emplace_back(w); // TODO: Can be optimised in case T == BufEl_t
273 }
274 }
275
276 template <typename T, typename W,
277 typename std::enable_if<IsDataContainer<T>::value && !IsDataContainer<W>::value, int>::type = 0>
278 void Exec(unsigned int slot, const T &vs, const W w)
279 {
280 auto &thisBuf = fBuffers[slot];
281 for (auto &v : vs) {
282 UpdateMinMax(slot, v);
283 thisBuf.emplace_back(v); // TODO: Can be optimised in case T == BufEl_t
284 }
285
286 auto &thisWBuf = fWBuffers[slot];
287 thisWBuf.insert(thisWBuf.end(), vs.size(), w);
288 }
289
290 // ROOT-10092: Filling with a scalar as first column and a collection as second is not supported
291 template <typename T, typename W,
292 typename std::enable_if<IsDataContainer<W>::value && !IsDataContainer<T>::value, int>::type = 0>
293 void Exec(unsigned int, const T &, const W &)
294 {
295 throw std::runtime_error(
296 "Cannot fill object if the type of the first column is a scalar and the one of the second a container.");
297 }
298
299 Hist_t &PartialUpdate(unsigned int);
300
301 void Initialize() { /* noop */}
302
303 void Finalize();
304
305 // Helper functions for RMergeableValue
306 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
307 {
308 return std::make_unique<RMergeableFill<Hist_t>>(*fResultHist);
309 }
310
311 std::string GetActionName() { return "Fill"; }
312};
313
314extern template void FillHelper::Exec(unsigned int, const std::vector<float> &);
315extern template void FillHelper::Exec(unsigned int, const std::vector<double> &);
316extern template void FillHelper::Exec(unsigned int, const std::vector<char> &);
317extern template void FillHelper::Exec(unsigned int, const std::vector<int> &);
318extern template void FillHelper::Exec(unsigned int, const std::vector<unsigned int> &);
319extern template void FillHelper::Exec(unsigned int, const std::vector<float> &, const std::vector<float> &);
320extern template void FillHelper::Exec(unsigned int, const std::vector<double> &, const std::vector<double> &);
321extern template void FillHelper::Exec(unsigned int, const std::vector<char> &, const std::vector<char> &);
322extern template void FillHelper::Exec(unsigned int, const std::vector<int> &, const std::vector<int> &);
323extern template void
324FillHelper::Exec(unsigned int, const std::vector<unsigned int> &, const std::vector<unsigned int> &);
325
326template <typename HIST = Hist_t>
327class FillParHelper : public RActionImpl<FillParHelper<HIST>> {
328 std::vector<HIST *> fObjects;
329
330public:
331 FillParHelper(FillParHelper &&) = default;
332 FillParHelper(const FillParHelper &) = delete;
333
334 FillParHelper(const std::shared_ptr<HIST> &h, const unsigned int nSlots) : fObjects(nSlots, nullptr)
335 {
336 fObjects[0] = h.get();
337 // Initialise all other slots
338 for (unsigned int i = 1; i < nSlots; ++i) {
339 fObjects[i] = new HIST(*fObjects[0]);
340 if (auto objAsHist = dynamic_cast<TH1*>(fObjects[i])) {
341 objAsHist->SetDirectory(nullptr);
342 }
343 }
344 }
345
346 void InitTask(TTreeReader *, unsigned int) {}
347
348 void Exec(unsigned int slot, double x0) // 1D histos
349 {
350 fObjects[slot]->Fill(x0);
351 }
352
353 void Exec(unsigned int slot, double x0, double x1) // 1D weighted and 2D histos
354 {
355 fObjects[slot]->Fill(x0, x1);
356 }
357
358 void Exec(unsigned int slot, double x0, double x1, double x2) // 2D weighted and 3D histos
359 {
360 fObjects[slot]->Fill(x0, x1, x2);
361 }
362
363 void Exec(unsigned int slot, double x0, double x1, double x2, double x3) // 3D weighted histos
364 {
365 fObjects[slot]->Fill(x0, x1, x2, x3);
366 }
367
368 template <typename X0, typename std::enable_if<IsDataContainer<X0>::value || std::is_same<X0, std::string>::value, int>::type = 0>
369 void Exec(unsigned int slot, const X0 &x0s)
370 {
371 auto thisSlotH = fObjects[slot];
372 for (auto x0 = x0s.begin(); x0 != x0s.end(); x0++) {
373 thisSlotH->Fill(*x0); // TODO: Can be optimised in case T == vector<double>
374 }
375 }
376
377 // ROOT-10092: Filling with a scalar as first column and a collection as second is not supported
378 template <typename X0, typename X1,
379 typename std::enable_if<IsDataContainer<X1>::value && !IsDataContainer<X0>::value, int>::type = 0>
380 void Exec(unsigned int , const X0 &, const X1 &)
381 {
382 throw std::runtime_error(
383 "Cannot fill object if the type of the first column is a scalar and the one of the second a container.");
384 }
385
386 template <typename X0, typename X1,
387 typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value, int>::type = 0>
388 void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s)
389 {
390 auto thisSlotH = fObjects[slot];
391 if (x0s.size() != x1s.size()) {
392 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
393 }
394 auto x0sIt = std::begin(x0s);
395 const auto x0sEnd = std::end(x0s);
396 auto x1sIt = std::begin(x1s);
397 for (; x0sIt != x0sEnd; x0sIt++, x1sIt++) {
398 thisSlotH->Fill(*x0sIt, *x1sIt); // TODO: Can be optimised in case T == vector<double>
399 }
400 }
401
402 template <typename X0, typename W,
403 typename std::enable_if<IsDataContainer<X0>::value && !IsDataContainer<W>::value, int>::type = 0>
404 void Exec(unsigned int slot, const X0 &x0s, const W w)
405 {
406 auto thisSlotH = fObjects[slot];
407 for (auto &&x : x0s) {
408 thisSlotH->Fill(x, w);
409 }
410 }
411
412 template <typename X0, typename X1, typename X2,
413 typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value && IsDataContainer<X2>::value,
414 int>::type = 0>
415 void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const X2 &x2s)
416 {
417 auto thisSlotH = fObjects[slot];
418 if (!(x0s.size() == x1s.size() && x1s.size() == x2s.size())) {
419 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
420 }
421 auto x0sIt = std::begin(x0s);
422 const auto x0sEnd = std::end(x0s);
423 auto x1sIt = std::begin(x1s);
424 auto x2sIt = std::begin(x2s);
425 for (; x0sIt != x0sEnd; x0sIt++, x1sIt++, x2sIt++) {
426 thisSlotH->Fill(*x0sIt, *x1sIt, *x2sIt); // TODO: Can be optimised in case T == vector<double>
427 }
428 }
429
430 template <typename X0, typename X1, typename W,
431 typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value && !IsDataContainer<W>::value,
432 int>::type = 0>
433 void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const W w)
434 {
435 auto thisSlotH = fObjects[slot];
436 if (x0s.size() != x1s.size()) {
437 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
438 }
439 auto x0sIt = std::begin(x0s);
440 const auto x0sEnd = std::end(x0s);
441 auto x1sIt = std::begin(x1s);
442 for (; x0sIt != x0sEnd; x0sIt++, x1sIt++) {
443 thisSlotH->Fill(*x0sIt, *x1sIt, w); // TODO: Can be optimised in case T == vector<double>
444 }
445 }
446
447 template <typename X0, typename X1, typename X2, typename X3,
448 typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value && IsDataContainer<X2>::value &&
449 IsDataContainer<X3>::value,
450 int>::type = 0>
451 void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const X2 &x2s, const X3 &x3s)
452 {
453 auto thisSlotH = fObjects[slot];
454 if (!(x0s.size() == x1s.size() && x1s.size() == x2s.size() && x1s.size() == x3s.size())) {
455 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
456 }
457 auto x0sIt = std::begin(x0s);
458 const auto x0sEnd = std::end(x0s);
459 auto x1sIt = std::begin(x1s);
460 auto x2sIt = std::begin(x2s);
461 auto x3sIt = std::begin(x3s);
462 for (; x0sIt != x0sEnd; x0sIt++, x1sIt++, x2sIt++, x3sIt++) {
463 thisSlotH->Fill(*x0sIt, *x1sIt, *x2sIt, *x3sIt); // TODO: Can be optimised in case T == vector<double>
464 }
465 }
466
467 template <typename X0, typename X1, typename X2, typename W,
468 typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value && IsDataContainer<X2>::value &&
469 !IsDataContainer<W>::value,
470 int>::type = 0>
471 void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s, const X2 &x2s, const W w)
472 {
473 auto thisSlotH = fObjects[slot];
474 if (!(x0s.size() == x1s.size() && x1s.size() == x2s.size())) {
475 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
476 }
477 auto x0sIt = std::begin(x0s);
478 const auto x0sEnd = std::end(x0s);
479 auto x1sIt = std::begin(x1s);
480 auto x2sIt = std::begin(x2s);
481 for (; x0sIt != x0sEnd; x0sIt++, x1sIt++, x2sIt++) {
482 thisSlotH->Fill(*x0sIt, *x1sIt, *x2sIt, w);
483 }
484 }
485
486 void Initialize() { /* noop */}
487
488 void Finalize()
489 {
490 auto resObj = fObjects[0];
491 const auto nSlots = fObjects.size();
492 TList l;
493 l.SetOwner(); // The list will free the memory associated to its elements upon destruction
494 for (unsigned int slot = 1; slot < nSlots; ++slot) {
495 l.Add(fObjects[slot]);
496 }
497
498 resObj->Merge(&l);
499 }
500
501 HIST &PartialUpdate(unsigned int slot) { return *fObjects[slot]; }
502
503 // Helper functions for RMergeableValue
504 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
505 {
506 return std::make_unique<RMergeableFill<HIST>>(*fObjects[0]);
507 }
508
509 std::string GetActionName() { return "FillPar"; }
510};
511
512class FillTGraphHelper : public ROOT::Detail::RDF::RActionImpl<FillTGraphHelper> {
513public:
514 using Result_t = ::TGraph;
515
516private:
517 std::vector<::TGraph *> fGraphs;
518
519public:
520 FillTGraphHelper(FillTGraphHelper &&) = default;
521 FillTGraphHelper(const FillTGraphHelper &) = delete;
522
523 // The last parameter is always false, as at the moment there is no way to propagate the parameter from the user to
524 // this method
525 FillTGraphHelper(const std::shared_ptr<::TGraph> &g, const unsigned int nSlots) : fGraphs(nSlots, nullptr)
526 {
527 fGraphs[0] = g.get();
528 // Initialise all other slots
529 for (unsigned int i = 1; i < nSlots; ++i) {
530 fGraphs[i] = new TGraph(*fGraphs[0]);
531 }
532 }
533
534 void Initialize() {}
535 void InitTask(TTreeReader *, unsigned int) {}
536
537 template <typename X0, typename X1,
538 typename std::enable_if<IsDataContainer<X0>::value && IsDataContainer<X1>::value, int>::type = 0>
539 void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s)
540 {
541 if (x0s.size() != x1s.size()) {
542 throw std::runtime_error("Cannot fill Graph with values in containers of different sizes.");
543 }
544 auto thisSlotG = fGraphs[slot];
545 auto x0sIt = std::begin(x0s);
546 const auto x0sEnd = std::end(x0s);
547 auto x1sIt = std::begin(x1s);
548 for (; x0sIt != x0sEnd; x0sIt++, x1sIt++) {
549 thisSlotG->SetPoint(thisSlotG->GetN(), *x0sIt, *x1sIt);
550 }
551 }
552
553 template <typename X0, typename X1>
554 void Exec(unsigned int slot, X0 x0, X1 x1)
555 {
556 auto thisSlotG = fGraphs[slot];
557 thisSlotG->SetPoint(thisSlotG->GetN(), x0, x1);
558 }
559
560 void Finalize()
561 {
562 const auto nSlots = fGraphs.size();
563 auto resGraph = fGraphs[0];
564 TList l;
565 l.SetOwner(); // The list will free the memory associated to its elements upon destruction
566 for (unsigned int slot = 1; slot < nSlots; ++slot) {
567 l.Add(fGraphs[slot]);
568 }
569 resGraph->Merge(&l);
570 }
571
572 // Helper functions for RMergeableValue
573 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
574 {
575 return std::make_unique<RMergeableFill<Result_t>>(*fGraphs[0]);
576 }
577
578 std::string GetActionName() { return "Graph"; }
579
580 Result_t &PartialUpdate(unsigned int slot) { return *fGraphs[slot]; }
581};
582
583// In case of the take helper we have 4 cases:
584// 1. The column is not an RVec, the collection is not a vector
585// 2. The column is not an RVec, the collection is a vector
586// 3. The column is an RVec, the collection is not a vector
587// 4. The column is an RVec, the collection is a vector
588
589template <typename V, typename COLL>
590void FillColl(V&& v, COLL& c) {
591 c.emplace_back(v);
592}
593
594// Use push_back for bool since some compilers do not support emplace_back.
595template <typename COLL>
596void FillColl(bool v, COLL& c) {
597 c.push_back(v);
598}
599
600// Case 1.: The column is not an RVec, the collection is not a vector
601// No optimisations, no transformations: just copies.
602template <typename RealT_t, typename T, typename COLL>
603class TakeHelper : public RActionImpl<TakeHelper<RealT_t, T, COLL>> {
604 Results<std::shared_ptr<COLL>> fColls;
605
606public:
607 using ColumnTypes_t = TypeList<T>;
608 TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
609 {
610 fColls.emplace_back(resultColl);
611 for (unsigned int i = 1; i < nSlots; ++i)
612 fColls.emplace_back(std::make_shared<COLL>());
613 }
614 TakeHelper(TakeHelper &&);
615 TakeHelper(const TakeHelper &) = delete;
616
617 void InitTask(TTreeReader *, unsigned int) {}
618
619 void Exec(unsigned int slot, T &v) { FillColl(v, *fColls[slot]); }
620
621 void Initialize() { /* noop */}
622
623 void Finalize()
624 {
625 auto rColl = fColls[0];
626 for (unsigned int i = 1; i < fColls.size(); ++i) {
627 const auto &coll = fColls[i];
628 const auto end = coll->end();
629 // Use an explicit loop here to prevent compiler warnings introduced by
630 // clang's range-based loop analysis and vector<bool> references.
631 for (auto j = coll->begin(); j != end; j++) {
632 FillColl(*j, *rColl);
633 }
634 }
635 }
636
637 COLL &PartialUpdate(unsigned int slot) { return *fColls[slot].get(); }
638
639 std::string GetActionName() { return "Take"; }
640};
641
642// Case 2.: The column is not an RVec, the collection is a vector
643// Optimisations, no transformations: just copies.
644template <typename RealT_t, typename T>
645class TakeHelper<RealT_t, T, std::vector<T>> : public RActionImpl<TakeHelper<RealT_t, T, std::vector<T>>> {
646 Results<std::shared_ptr<std::vector<T>>> fColls;
647
648public:
649 using ColumnTypes_t = TypeList<T>;
650 TakeHelper(const std::shared_ptr<std::vector<T>> &resultColl, const unsigned int nSlots)
651 {
652 fColls.emplace_back(resultColl);
653 for (unsigned int i = 1; i < nSlots; ++i) {
654 auto v = std::make_shared<std::vector<T>>();
655 v->reserve(1024);
656 fColls.emplace_back(v);
657 }
658 }
659 TakeHelper(TakeHelper &&);
660 TakeHelper(const TakeHelper &) = delete;
661
662 void InitTask(TTreeReader *, unsigned int) {}
663
664 void Exec(unsigned int slot, T &v) { FillColl(v, *fColls[slot]); }
665
666 void Initialize() { /* noop */}
667
668 // This is optimised to treat vectors
669 void Finalize()
670 {
671 ULong64_t totSize = 0;
672 for (auto &coll : fColls)
673 totSize += coll->size();
674 auto rColl = fColls[0];
675 rColl->reserve(totSize);
676 for (unsigned int i = 1; i < fColls.size(); ++i) {
677 auto &coll = fColls[i];
678 rColl->insert(rColl->end(), coll->begin(), coll->end());
679 }
680 }
681
682 std::vector<T> &PartialUpdate(unsigned int slot) { return *fColls[slot]; }
683
684 std::string GetActionName() { return "Take"; }
685};
686
687// Case 3.: The column is a RVec, the collection is not a vector
688// No optimisations, transformations from RVecs to vectors
689template <typename RealT_t, typename COLL>
690class TakeHelper<RealT_t, RVec<RealT_t>, COLL> : public RActionImpl<TakeHelper<RealT_t, RVec<RealT_t>, COLL>> {
691 Results<std::shared_ptr<COLL>> fColls;
692
693public:
694 using ColumnTypes_t = TypeList<RVec<RealT_t>>;
695 TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
696 {
697 fColls.emplace_back(resultColl);
698 for (unsigned int i = 1; i < nSlots; ++i)
699 fColls.emplace_back(std::make_shared<COLL>());
700 }
701 TakeHelper(TakeHelper &&);
702 TakeHelper(const TakeHelper &) = delete;
703
704 void InitTask(TTreeReader *, unsigned int) {}
705
706 void Exec(unsigned int slot, RVec<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
707
708 void Initialize() { /* noop */}
709
710 void Finalize()
711 {
712 auto rColl = fColls[0];
713 for (unsigned int i = 1; i < fColls.size(); ++i) {
714 auto &coll = fColls[i];
715 for (auto &v : *coll) {
716 rColl->emplace_back(v);
717 }
718 }
719 }
720
721 std::string GetActionName() { return "Take"; }
722};
723
724// Case 4.: The column is an RVec, the collection is a vector
725// Optimisations, transformations from RVecs to vectors
726template <typename RealT_t>
727class TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>
728 : public RActionImpl<TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>> {
729
730 Results<std::shared_ptr<std::vector<std::vector<RealT_t>>>> fColls;
731
732public:
733 using ColumnTypes_t = TypeList<RVec<RealT_t>>;
734 TakeHelper(const std::shared_ptr<std::vector<std::vector<RealT_t>>> &resultColl, const unsigned int nSlots)
735 {
736 fColls.emplace_back(resultColl);
737 for (unsigned int i = 1; i < nSlots; ++i) {
738 auto v = std::make_shared<std::vector<RealT_t>>();
739 v->reserve(1024);
740 fColls.emplace_back(v);
741 }
742 }
743 TakeHelper(TakeHelper &&);
744 TakeHelper(const TakeHelper &) = delete;
745
746 void InitTask(TTreeReader *, unsigned int) {}
747
748 void Exec(unsigned int slot, RVec<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
749
750 void Initialize() { /* noop */}
751
752 // This is optimised to treat vectors
753 void Finalize()
754 {
755 ULong64_t totSize = 0;
756 for (auto &coll : fColls)
757 totSize += coll->size();
758 auto rColl = fColls[0];
759 rColl->reserve(totSize);
760 for (unsigned int i = 1; i < fColls.size(); ++i) {
761 auto &coll = fColls[i];
762 rColl->insert(rColl->end(), coll->begin(), coll->end());
763 }
764 }
765
766 std::string GetActionName() { return "Take"; }
767};
768
769// Extern templates for TakeHelper
770// NOTE: The move-constructor of specializations declared as extern templates
771// must be defined out of line, otherwise cling fails to find its symbol.
772template <typename RealT_t, typename T, typename COLL>
773TakeHelper<RealT_t, T, COLL>::TakeHelper(TakeHelper<RealT_t, T, COLL> &&) = default;
774template <typename RealT_t, typename T>
775TakeHelper<RealT_t, T, std::vector<T>>::TakeHelper(TakeHelper<RealT_t, T, std::vector<T>> &&) = default;
776template <typename RealT_t, typename COLL>
777TakeHelper<RealT_t, RVec<RealT_t>, COLL>::TakeHelper(TakeHelper<RealT_t, RVec<RealT_t>, COLL> &&) = default;
778template <typename RealT_t>
779TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>::TakeHelper(TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>> &&) = default;
780
781// External templates are disabled for gcc5 since this version wrongly omits the C++11 ABI attribute
782#if __GNUC__ > 5
783extern template class TakeHelper<bool, bool, std::vector<bool>>;
784extern template class TakeHelper<unsigned int, unsigned int, std::vector<unsigned int>>;
785extern template class TakeHelper<unsigned long, unsigned long, std::vector<unsigned long>>;
786extern template class TakeHelper<unsigned long long, unsigned long long, std::vector<unsigned long long>>;
787extern template class TakeHelper<int, int, std::vector<int>>;
788extern template class TakeHelper<long, long, std::vector<long>>;
789extern template class TakeHelper<long long, long long, std::vector<long long>>;
790extern template class TakeHelper<float, float, std::vector<float>>;
791extern template class TakeHelper<double, double, std::vector<double>>;
792#endif
793
794
795template <typename ResultType>
796class MinHelper : public RActionImpl<MinHelper<ResultType>> {
797 const std::shared_ptr<ResultType> fResultMin;
798 Results<ResultType> fMins;
799
800public:
801 MinHelper(MinHelper &&) = default;
802 MinHelper(const std::shared_ptr<ResultType> &minVPtr, const unsigned int nSlots)
803 : fResultMin(minVPtr), fMins(nSlots, std::numeric_limits<ResultType>::max())
804 {
805 }
806
807 void Exec(unsigned int slot, ResultType v) { fMins[slot] = std::min(v, fMins[slot]); }
808
809 void InitTask(TTreeReader *, unsigned int) {}
810
811 template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0>
812 void Exec(unsigned int slot, const T &vs)
813 {
814 for (auto &&v : vs)
815 fMins[slot] = std::min(static_cast<ResultType>(v), fMins[slot]);
816 }
817
818 void Initialize() { /* noop */}
819
820 void Finalize()
821 {
822 *fResultMin = std::numeric_limits<ResultType>::max();
823 for (auto &m : fMins)
824 *fResultMin = std::min(m, *fResultMin);
825 }
826
827 // Helper functions for RMergeableValue
828 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
829 {
830 return std::make_unique<RMergeableMin<ResultType>>(*fResultMin);
831 }
832
833 ResultType &PartialUpdate(unsigned int slot) { return fMins[slot]; }
834
835 std::string GetActionName() { return "Min"; }
836};
837
838// TODO
839// extern template void MinHelper::Exec(unsigned int, const std::vector<float> &);
840// extern template void MinHelper::Exec(unsigned int, const std::vector<double> &);
841// extern template void MinHelper::Exec(unsigned int, const std::vector<char> &);
842// extern template void MinHelper::Exec(unsigned int, const std::vector<int> &);
843// extern template void MinHelper::Exec(unsigned int, const std::vector<unsigned int> &);
844
845template <typename ResultType>
846class MaxHelper : public RActionImpl<MaxHelper<ResultType>> {
847 const std::shared_ptr<ResultType> fResultMax;
848 Results<ResultType> fMaxs;
849
850public:
851 MaxHelper(MaxHelper &&) = default;
852 MaxHelper(const MaxHelper &) = delete;
853 MaxHelper(const std::shared_ptr<ResultType> &maxVPtr, const unsigned int nSlots)
854 : fResultMax(maxVPtr), fMaxs(nSlots, std::numeric_limits<ResultType>::lowest())
855 {
856 }
857
858 void InitTask(TTreeReader *, unsigned int) {}
859 void Exec(unsigned int slot, ResultType v) { fMaxs[slot] = std::max(v, fMaxs[slot]); }
860
861 template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0>
862 void Exec(unsigned int slot, const T &vs)
863 {
864 for (auto &&v : vs)
865 fMaxs[slot] = std::max(static_cast<ResultType>(v), fMaxs[slot]);
866 }
867
868 void Initialize() { /* noop */}
869
870 void Finalize()
871 {
872 *fResultMax = std::numeric_limits<ResultType>::lowest();
873 for (auto &m : fMaxs) {
874 *fResultMax = std::max(m, *fResultMax);
875 }
876 }
877
878 // Helper functions for RMergeableValue
879 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
880 {
881 return std::make_unique<RMergeableMax<ResultType>>(*fResultMax);
882 }
883
884 ResultType &PartialUpdate(unsigned int slot) { return fMaxs[slot]; }
885
886 std::string GetActionName() { return "Max"; }
887};
888
889// TODO
890// extern template void MaxHelper::Exec(unsigned int, const std::vector<float> &);
891// extern template void MaxHelper::Exec(unsigned int, const std::vector<double> &);
892// extern template void MaxHelper::Exec(unsigned int, const std::vector<char> &);
893// extern template void MaxHelper::Exec(unsigned int, const std::vector<int> &);
894// extern template void MaxHelper::Exec(unsigned int, const std::vector<unsigned int> &);
895
896template <typename ResultType>
897class SumHelper : public RActionImpl<SumHelper<ResultType>> {
898 const std::shared_ptr<ResultType> fResultSum;
899 Results<ResultType> fSums;
900
901 /// Evaluate neutral element for this type and the sum operation.
902 /// This is assumed to be any_value - any_value if operator- is defined
903 /// for the type, otherwise a default-constructed ResultType{} is used.
904 template <typename T = ResultType>
905 auto NeutralElement(const T &v, int /*overloadresolver*/) -> decltype(v - v)
906 {
907 return v - v;
908 }
909
910 template <typename T = ResultType, typename Dummy = int>
911 ResultType NeutralElement(const T &, Dummy) // this overload has lower priority thanks to the template arg
912 {
913 return ResultType{};
914 }
915
916public:
917 SumHelper(SumHelper &&) = default;
918 SumHelper(const SumHelper &) = delete;
919 SumHelper(const std::shared_ptr<ResultType> &sumVPtr, const unsigned int nSlots)
920 : fResultSum(sumVPtr), fSums(nSlots, NeutralElement(*sumVPtr, -1))
921 {
922 }
923
924 void InitTask(TTreeReader *, unsigned int) {}
925 void Exec(unsigned int slot, ResultType v) { fSums[slot] += v; }
926
927 template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0>
928 void Exec(unsigned int slot, const T &vs)
929 {
930 for (auto &&v : vs)
931 fSums[slot] += static_cast<ResultType>(v);
932 }
933
934 void Initialize() { /* noop */}
935
936 void Finalize()
937 {
938 for (auto &m : fSums)
939 *fResultSum += m;
940 }
941
942 // Helper functions for RMergeableValue
943 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
944 {
945 return std::make_unique<RMergeableSum<ResultType>>(*fResultSum);
946 }
947
948 ResultType &PartialUpdate(unsigned int slot) { return fSums[slot]; }
949
950 std::string GetActionName() { return "Sum"; }
951};
952
953class MeanHelper : public RActionImpl<MeanHelper> {
954 const std::shared_ptr<double> fResultMean;
955 std::vector<ULong64_t> fCounts;
956 std::vector<double> fSums;
957 std::vector<double> fPartialMeans;
958
959public:
960 MeanHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
961 MeanHelper(MeanHelper &&) = default;
962 MeanHelper(const MeanHelper &) = delete;
963 void InitTask(TTreeReader *, unsigned int) {}
964 void Exec(unsigned int slot, double v);
965
966 template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0>
967 void Exec(unsigned int slot, const T &vs)
968 {
969 for (auto &&v : vs) {
970 fSums[slot] += v;
971 fCounts[slot]++;
972 }
973 }
974
975 void Initialize() { /* noop */}
976
977 void Finalize();
978
979 // Helper functions for RMergeableValue
980 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
981 {
982 const ULong64_t counts = std::accumulate(fCounts.begin(), fCounts.end(), 0ull);
983 return std::make_unique<RMergeableMean>(*fResultMean, counts);
984 }
985
986 double &PartialUpdate(unsigned int slot);
987
988 std::string GetActionName() { return "Mean"; }
989};
990
991extern template void MeanHelper::Exec(unsigned int, const std::vector<float> &);
992extern template void MeanHelper::Exec(unsigned int, const std::vector<double> &);
993extern template void MeanHelper::Exec(unsigned int, const std::vector<char> &);
994extern template void MeanHelper::Exec(unsigned int, const std::vector<int> &);
995extern template void MeanHelper::Exec(unsigned int, const std::vector<unsigned int> &);
996
997class StdDevHelper : public RActionImpl<StdDevHelper> {
998 // Number of subsets of data
999 const unsigned int fNSlots;
1000 const std::shared_ptr<double> fResultStdDev;
1001 // Number of element for each slot
1002 std::vector<ULong64_t> fCounts;
1003 // Mean of each slot
1004 std::vector<double> fMeans;
1005 // Squared distance from the mean
1006 std::vector<double> fDistancesfromMean;
1007
1008public:
1009 StdDevHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
1010 StdDevHelper(StdDevHelper &&) = default;
1011 StdDevHelper(const StdDevHelper &) = delete;
1012 void InitTask(TTreeReader *, unsigned int) {}
1013 void Exec(unsigned int slot, double v);
1014
1015 template <typename T, typename std::enable_if<IsDataContainer<T>::value, int>::type = 0>
1016 void Exec(unsigned int slot, const T &vs)
1017 {
1018 for (auto &&v : vs) {
1019 Exec(slot, v);
1020 }
1021 }
1022
1023 void Initialize() { /* noop */}
1024
1025 void Finalize();
1026
1027 // Helper functions for RMergeableValue
1028 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1029 {
1030 const ULong64_t counts = std::accumulate(fCounts.begin(), fCounts.end(), 0ull);
1031 const Double_t mean =
1032 std::inner_product(fMeans.begin(), fMeans.end(), fCounts.begin(), 0.) / static_cast<Double_t>(counts);
1033 return std::make_unique<RMergeableStdDev>(*fResultStdDev, counts, mean);
1034 }
1035
1036 std::string GetActionName() { return "StdDev"; }
1037};
1038
1039extern template void StdDevHelper::Exec(unsigned int, const std::vector<float> &);
1040extern template void StdDevHelper::Exec(unsigned int, const std::vector<double> &);
1041extern template void StdDevHelper::Exec(unsigned int, const std::vector<char> &);
1042extern template void StdDevHelper::Exec(unsigned int, const std::vector<int> &);
1043extern template void StdDevHelper::Exec(unsigned int, const std::vector<unsigned int> &);
1044
1045template <typename PrevNodeType>
1046class DisplayHelper : public RActionImpl<DisplayHelper<PrevNodeType>> {
1047private:
1049 const std::shared_ptr<Display_t> fDisplayerHelper;
1050 const std::shared_ptr<PrevNodeType> fPrevNode;
1051
1052public:
1053 DisplayHelper(const std::shared_ptr<Display_t> &d, const std::shared_ptr<PrevNodeType> &prevNode)
1054 : fDisplayerHelper(d), fPrevNode(prevNode)
1055 {
1056 }
1057 DisplayHelper(DisplayHelper &&) = default;
1058 DisplayHelper(const DisplayHelper &) = delete;
1059 void InitTask(TTreeReader *, unsigned int) {}
1060
1061 template <typename... Columns>
1062 void Exec(unsigned int, Columns &... columns)
1063 {
1064 fDisplayerHelper->AddRow(columns...);
1065 if (!fDisplayerHelper->HasNext()) {
1066 fPrevNode->StopProcessing();
1067 }
1068 }
1069
1070 void Initialize() {}
1071
1072 void Finalize() {}
1073
1074 std::string GetActionName() { return "Display"; }
1075};
1076
1077// std::vector<bool> is special, and not in a good way. As a consequence Snapshot of RVec<bool> needs to be treated
1078// specially. In particular, if RVec<bool> is filled with a (fixed or variable size) boolean array coming from
1079// a ROOT file, when writing out the correspinding branch from a Snapshot we do not have an address to set for the
1080// TTree branch (std::vector<bool> and, consequently, RVec<bool> do not provide a `data()` method).
1081// Bools is a lightweight wrapper around a C array of booleans that is meant to provide a stable address for the
1082// output TTree to read the contents of the snapshotted branches at Fill time.
1083class BoolArray {
1084 std::size_t fSize = 0;
1085 bool *fBools = nullptr;
1086
1087 bool *CopyVector(const RVec<bool> &v)
1088 {
1089 auto b = new bool[fSize];
1090 std::copy(v.begin(), v.end(), b);
1091 return b;
1092 }
1093
1094public:
1095 BoolArray() = default;
1096 BoolArray(const RVec<bool> &v) : fSize(v.size()), fBools(CopyVector(v)) {}
1097 BoolArray(const BoolArray &b) = delete;
1098 BoolArray &operator=(const BoolArray &b) = delete;
1099 BoolArray(BoolArray &&b)
1100 {
1101 fSize = b.fSize;
1102 fBools = b.fBools;
1103 b.fSize = 0;
1104 b.fBools = nullptr;
1105 }
1106 BoolArray &operator=(BoolArray &&b)
1107 {
1108 delete[] fBools;
1109 fSize = b.fSize;
1110 fBools = b.fBools;
1111 b.fSize = 0;
1112 b.fBools = nullptr;
1113 return *this;
1114 }
1115 ~BoolArray() { delete[] fBools; }
1116 std::size_t Size() const { return fSize; }
1117 bool *Data() { return fBools; }
1118};
1119using BoolArrayMap = std::map<std::string, BoolArray>;
1120
1121inline bool *UpdateBoolArrayIfBool(BoolArrayMap &boolArrays, RVec<bool> &v, const std::string &outName)
1122{
1123 // create a boolArrays entry
1124 boolArrays[outName] = BoolArray(v);
1125 return boolArrays[outName].Data();
1126}
1127
1128template <typename T>
1129T *UpdateBoolArrayIfBool(BoolArrayMap &, RVec<T> &v, const std::string &)
1130{
1131 return v.data();
1132}
1133
1134// Helper which gets the return value of the data() method if the type is an
1135// RVec (of anything but a bool), nullptr otherwise.
1136inline void *GetData(ROOT::VecOps::RVec<bool> & /*v*/)
1137{
1138 return nullptr;
1139}
1140
1141template <typename T>
1142void *GetData(ROOT::VecOps::RVec<T> &v)
1143{
1144 return v.data();
1145}
1146
1147template <typename T>
1148void *GetData(T & /*v*/)
1149{
1150 return nullptr;
1151}
1152
1153template <typename T>
1154void SetBranchesHelper(BoolArrayMap &, TTree *inputTree, TTree &outputTree, const std::string &inName,
1155 const std::string &name, TBranch *&branch, void *&branchAddress, T *address,
1156 RBranchSet &outputBranches)
1157{
1158 static TClassRef TBOClRef("TBranchObject");
1159 // FIXME we should be using FindBranch as a fallback if GetBranch fails
1160 TBranch *inputBranch = inputTree ? inputTree->GetBranch(inName.c_str()) : nullptr;
1161
1162 auto *outputBranch = outputBranches.Get(name);
1163 if (outputBranch) {
1164 // the output branch was already created, we just need to (re)set its address
1165 if (inputBranch && inputBranch->IsA() == TBOClRef) {
1166 outputBranch->SetAddress(reinterpret_cast<T **>(inputBranch->GetAddress()));
1167 } else if (outputBranch->IsA() != TBranch::Class()) {
1168 branchAddress = address;
1169 outputBranch->SetAddress(&branchAddress);
1170 } else {
1171 outputBranch->SetAddress(address);
1172 branchAddress = address;
1173 }
1174 return;
1175 }
1176
1177 if (inputBranch) {
1178 // Respect the original bufsize and splitlevel arguments
1179 // In particular, by keeping splitlevel equal to 0 if this was the case for `inputBranch`, we avoid
1180 // writing garbage when unsplit objects cannot be written as split objects (e.g. in case of a polymorphic
1181 // TObject branch, see https://bit.ly/2EjLMId ).
1182 const auto bufSize = inputBranch->GetBasketSize();
1183 const auto splitLevel = inputBranch->GetSplitLevel();
1184
1185 if (inputBranch->IsA() == TBOClRef) {
1186 // Need to pass a pointer to pointer
1187 outputBranch =
1188 outputTree.Branch(name.c_str(), reinterpret_cast<T **>(inputBranch->GetAddress()), bufSize, splitLevel);
1189 } else {
1190 outputBranch = outputTree.Branch(name.c_str(), address, bufSize, splitLevel);
1191 }
1192 } else {
1193 outputBranch = outputTree.Branch(name.c_str(), address);
1194 }
1195 outputBranches.Insert(name, outputBranch);
1196 // This is not an array branch, so we don't need to register the address of the input branch.
1197 branch = nullptr;
1198 branchAddress = nullptr;
1199}
1200
1201/// Helper function for SnapshotHelper and SnapshotHelperMT. It creates new branches for the output TTree of a Snapshot.
1202/// This overload is called for columns of type `RVec<T>`. For RDF, these can represent:
1203/// 1. c-style arrays in ROOT files, so we are sure that there are input trees to which we can ask the correct branch title
1204/// 2. RVecs coming from a custom column or a source
1205/// 3. vectors coming from ROOT files
1206/// 4. TClonesArray
1207///
1208/// In case of 1., we keep aside the pointer to the branch and the pointer to the input value (in `branch` and
1209/// `branchAddress`) so we can intercept changes in the address of the input branch and tell the output branch.
1210template <typename T>
1211void SetBranchesHelper(BoolArrayMap &boolArrays, TTree *inputTree, TTree &outputTree, const std::string &inName,
1212 const std::string &outName, TBranch *&branch, void *&branchAddress, RVec<T> *ab,
1213 RBranchSet &outputBranches)
1214{
1215 TBranch *inputBranch = nullptr;
1216 if (inputTree) {
1217 inputBranch = inputTree->GetBranch(inName.c_str());
1218 if (!inputBranch) {
1219 // try harder
1220 inputBranch = inputTree->FindBranch(inName.c_str());
1221 }
1222 }
1223 auto *outputBranch = outputBranches.Get(outName);
1224 const bool isTClonesArray = inputBranch != nullptr && std::string(inputBranch->GetClassName()) == "TClonesArray";
1225 const auto mustWriteStdVec = !inputBranch || isTClonesArray ||
1227
1228 if (mustWriteStdVec) {
1229 // Treat:
1230 // 2. RVec coming from a custom column or a source
1231 // 3. RVec coming from a column on disk of type vector (the RVec is adopting the data of that vector)
1232 // 4. TClonesArray.
1233 // In all cases, we write out a std::vector<T> when the column is RVec<T>
1234 if (isTClonesArray) {
1235 Warning("Snapshot",
1236 "Branch \"%s\" contains TClonesArrays but the type specified to Snapshot was RVec<T>. The branch will "
1237 "be written out as a std::vector instead of a TClonesArray. Specify that the type of the branch is "
1238 "TClonesArray as a Snapshot template parameter to write out a TClonesArray instead.", inName.c_str());
1239 }
1240 if (outputBranch) {
1241 branchAddress = &(ab->fData);
1242 outputBranch->SetAddress(&branchAddress);
1243 } else {
1244 auto *b = outputTree.Branch(outName.c_str(), &(ab->fData));
1245 outputBranches.Insert(outName, b);
1246 }
1247 return;
1248 }
1249
1250 // Treat 1, the C-array case
1251 auto *const leaf = static_cast<TLeaf *>(inputBranch->GetListOfLeaves()->UncheckedAt(0));
1252 const auto bname = leaf->GetName();
1253 const auto counterStr =
1254 leaf->GetLeafCount() ? std::string(leaf->GetLeafCount()->GetName()) : std::to_string(leaf->GetLenStatic());
1255 const auto btype = leaf->GetTypeName();
1256 const auto rootbtype = TypeName2ROOTTypeName(btype);
1257 const auto leaflist = std::string(bname) + "[" + counterStr + "]/" + rootbtype;
1258
1259 /// RVec<bool> is special because std::vector<bool> is special. In particular, it has no `data()`,
1260 /// so we need to explicitly manage storage of the data that the tree needs to Fill branches with.
1261 auto dataPtr = UpdateBoolArrayIfBool(boolArrays, *ab, outName);
1262
1263 if (outputBranch) {
1264 if (outputBranch->IsA() != TBranch::Class()) {
1265 branchAddress = dataPtr;
1266 outputBranch->SetAddress(&branchAddress);
1267 } else {
1268 outputBranch->SetAddress(dataPtr);
1269 }
1270 } else {
1271 outputBranch = outputTree.Branch(outName.c_str(), dataPtr, leaflist.c_str());
1272 outputBranch->SetTitle(inputBranch->GetTitle());
1273 outputBranches.Insert(outName, outputBranch);
1274 // Record the branch ptr and the address associated to it if this is not a bool array
1275 if (!std::is_same<bool, T>::value) {
1276 branch = outputBranch;
1277 branchAddress = GetData(*ab);
1278 }
1279 }
1280}
1281
1282// generic version, no-op
1283template <typename T>
1284void UpdateBoolArray(BoolArrayMap &, T&, const std::string &, TTree &) {}
1285
1286// RVec<bool> overload, update boolArrays if needed
1287inline void UpdateBoolArray(BoolArrayMap &boolArrays, RVec<bool> &v, const std::string &outName, TTree &t)
1288{
1289 // in case the RVec<bool> does not correspond to a bool C-array
1290 if (boolArrays.find(outName) == boolArrays.end())
1291 return;
1292
1293 if (v.size() > boolArrays[outName].Size()) {
1294 boolArrays[outName] = BoolArray(v); // resize and copy
1295 t.SetBranchAddress(outName.c_str(), boolArrays[outName].Data());
1296 }
1297 else {
1298 std::copy(v.begin(), v.end(), boolArrays[outName].Data()); // just copy
1299 }
1300}
1301
1302void ValidateSnapshotOutput(const RSnapshotOptions &opts, const std::string &treeName, const std::string &fileName);
1303
1304/// Helper object for a single-thread Snapshot action
1305template <typename... ColTypes>
1306class SnapshotHelper : public RActionImpl<SnapshotHelper<ColTypes...>> {
1307 const std::string fFileName;
1308 const std::string fDirName;
1309 const std::string fTreeName;
1310 const RSnapshotOptions fOptions;
1311 std::unique_ptr<TFile> fOutputFile;
1312 std::unique_ptr<TTree> fOutputTree; // must be a ptr because TTrees are not copy/move constructible
1313 bool fBranchAddressesNeedReset{true};
1314 const ColumnNames_t fInputBranchNames; // This contains the resolved aliases
1315 const ColumnNames_t fOutputBranchNames;
1316 TTree *fInputTree = nullptr; // Current input tree. Set at initialization time (`InitTask`)
1317 BoolArrayMap fBoolArrays; // Storage for C arrays of bools to be written out
1318 // TODO we might be able to unify fBranches, fBranchAddresses and fOutputBranches
1319 std::vector<TBranch *> fBranches; // Addresses of branches in output, non-null only for the ones holding C arrays
1320 std::vector<void *> fBranchAddresses; // Addresses of objects associated to output branches
1321 RBranchSet fOutputBranches;
1322
1323public:
1324 using ColumnTypes_t = TypeList<ColTypes...>;
1325 SnapshotHelper(std::string_view filename, std::string_view dirname, std::string_view treename,
1326 const ColumnNames_t &vbnames, const ColumnNames_t &bnames, const RSnapshotOptions &options)
1327 : fFileName(filename), fDirName(dirname), fTreeName(treename), fOptions(options), fInputBranchNames(vbnames),
1328 fOutputBranchNames(ReplaceDotWithUnderscore(bnames)), fBranches(vbnames.size(), nullptr),
1329 fBranchAddresses(vbnames.size(), nullptr)
1330 {
1331 ValidateSnapshotOutput(fOptions, fTreeName, fFileName);
1332 }
1333
1334 SnapshotHelper(const SnapshotHelper &) = delete;
1335 SnapshotHelper(SnapshotHelper &&) = default;
1336
1337 void InitTask(TTreeReader *r, unsigned int /* slot */)
1338 {
1339 if (r)
1340 fInputTree = r->GetTree();
1341 fBranchAddressesNeedReset = true;
1342 }
1343
1344 void Exec(unsigned int /* slot */, ColTypes &... values)
1345 {
1346 using ind_t = std::index_sequence_for<ColTypes...>;
1347 if (!fBranchAddressesNeedReset) {
1348 UpdateCArraysPtrs(values..., ind_t{});
1349 } else {
1350 SetBranches(values..., ind_t{});
1351 fBranchAddressesNeedReset = false;
1352 }
1353 UpdateBoolArrays(values..., ind_t{});
1354 fOutputTree->Fill();
1355 }
1356
1357 template <std::size_t... S>
1358 void UpdateCArraysPtrs(ColTypes &... values, std::index_sequence<S...> /*dummy*/)
1359 {
1360 // This code deals with branches which hold C arrays of variable size. It can happen that the buffers
1361 // associated to those is re-allocated. As a result the value of the pointer can change therewith
1362 // leaving associated to the branch of the output tree an invalid pointer.
1363 // With this code, we set the value of the pointer in the output branch anew when needed.
1364 // Nota bene: the extra ",0" after the invocation of SetAddress, is because that method returns void and
1365 // we need an int for the expander list.
1366 int expander[] = {(fBranches[S] && fBranchAddresses[S] != GetData(values)
1367 ? fBranches[S]->SetAddress(GetData(values)),
1368 fBranchAddresses[S] = GetData(values), 0 : 0, 0)...,
1369 0};
1370 (void)expander; // avoid unused variable warnings for older compilers such as gcc 4.9
1371 }
1372
1373 template <std::size_t... S>
1374 void SetBranches(ColTypes &... values, std::index_sequence<S...> /*dummy*/)
1375 {
1376 // create branches in output tree (and fill fBoolArrays for RVec<bool> columns)
1377 int expander[] = {
1378 (SetBranchesHelper(fBoolArrays, fInputTree, *fOutputTree, fInputBranchNames[S], fOutputBranchNames[S],
1379 fBranches[S], fBranchAddresses[S], &values, fOutputBranches),
1380 0)...,
1381 0};
1382 (void)expander; // avoid unused variable warnings for older compilers such as gcc 4.9
1383 }
1384
1385 template <std::size_t... S>
1386 void UpdateBoolArrays(ColTypes &...values, std::index_sequence<S...> /*dummy*/)
1387 {
1388 int expander[] = {(UpdateBoolArray(fBoolArrays, values, fOutputBranchNames[S], *fOutputTree), 0)..., 0};
1389 (void)expander; // avoid unused variable warnings for older compilers such as gcc 4.9
1390 }
1391
1392 void Initialize()
1393 {
1394 fOutputFile.reset(
1395 TFile::Open(fFileName.c_str(), fOptions.fMode.c_str(), /*ftitle=*/"",
1397 if(!fOutputFile)
1398 throw std::runtime_error("Snapshot: could not create output file " + fFileName);
1399
1400 TDirectory *outputDir = fOutputFile.get();
1401 if (!fDirName.empty()) {
1402 TString checkupdate = fOptions.fMode;
1403 checkupdate.ToLower();
1404 if (checkupdate == "update")
1405 outputDir = fOutputFile->mkdir(fDirName.c_str(), "", true); // do not overwrite existing directory
1406 else
1407 outputDir = fOutputFile->mkdir(fDirName.c_str());
1408 }
1409
1410 fOutputTree =
1411 std::make_unique<TTree>(fTreeName.c_str(), fTreeName.c_str(), fOptions.fSplitLevel, /*dir=*/outputDir);
1412
1413 if (fOptions.fAutoFlush)
1414 fOutputTree->SetAutoFlush(fOptions.fAutoFlush);
1415 }
1416
1417 void Finalize()
1418 {
1419 R__ASSERT(fOutputTree != nullptr);
1420 R__ASSERT(fOutputFile != nullptr);
1421
1422 // use AutoSave to flush TTree contents because TTree::Write writes in gDirectory, not in fDirectory
1423 fOutputTree->AutoSave("flushbaskets");
1424 // must destroy the TTree first, otherwise TFile will delete it too leading to a double delete
1425 fOutputTree.reset();
1426 fOutputFile->Close();
1427 }
1428
1429 std::string GetActionName() { return "Snapshot"; }
1430
1431 std::function<void(unsigned int)> GetDataBlockCallback() final
1432 {
1433 return [this](unsigned int) mutable { fBranchAddressesNeedReset = true; };
1434 }
1435};
1436
1437/// Helper object for a multi-thread Snapshot action
1438template <typename... ColTypes>
1439class SnapshotHelperMT : public RActionImpl<SnapshotHelperMT<ColTypes...>> {
1440 const unsigned int fNSlots;
1441 std::unique_ptr<ROOT::Experimental::TBufferMerger> fMerger; // must use a ptr because TBufferMerger is not movable
1442 std::vector<std::shared_ptr<ROOT::Experimental::TBufferMergerFile>> fOutputFiles;
1443 std::vector<std::unique_ptr<TTree>> fOutputTrees;
1444 std::vector<int> fBranchAddressesNeedReset; // vector<bool> does not allow concurrent writing of different elements
1445 const std::string fFileName; // name of the output file name
1446 const std::string fDirName; // name of TFile subdirectory in which output must be written (possibly empty)
1447 const std::string fTreeName; // name of output tree
1448 const RSnapshotOptions fOptions; // struct holding options to pass down to TFile and TTree in this action
1449 const ColumnNames_t fInputBranchNames; // This contains the resolved aliases
1450 const ColumnNames_t fOutputBranchNames;
1451 std::vector<TTree *> fInputTrees; // Current input trees. Set at initialization time (`InitTask`)
1452 std::vector<BoolArrayMap> fBoolArrays; // Per-thread storage for C arrays of bools to be written out
1453 // Addresses of branches in output per slot, non-null only for the ones holding C arrays
1454 std::vector<std::vector<TBranch *>> fBranches;
1455 // Addresses associated to output branches per slot, non-null only for the ones holding C arrays
1456 std::vector<std::vector<void *>> fBranchAddresses;
1457 std::vector<RBranchSet> fOutputBranches;
1458
1459public:
1460 using ColumnTypes_t = TypeList<ColTypes...>;
1461 SnapshotHelperMT(const unsigned int nSlots, std::string_view filename, std::string_view dirname,
1462 std::string_view treename, const ColumnNames_t &vbnames, const ColumnNames_t &bnames,
1463 const RSnapshotOptions &options)
1464 : fNSlots(nSlots), fOutputFiles(fNSlots), fOutputTrees(fNSlots), fBranchAddressesNeedReset(fNSlots, 1),
1465 fFileName(filename), fDirName(dirname), fTreeName(treename), fOptions(options), fInputBranchNames(vbnames),
1466 fOutputBranchNames(ReplaceDotWithUnderscore(bnames)), fInputTrees(fNSlots), fBoolArrays(fNSlots),
1467 fBranches(fNSlots, std::vector<TBranch *>(vbnames.size(), nullptr)),
1468 fBranchAddresses(fNSlots, std::vector<void *>(vbnames.size(), nullptr)), fOutputBranches(fNSlots)
1469 {
1470 ValidateSnapshotOutput(fOptions, fTreeName, fFileName);
1471 }
1472 SnapshotHelperMT(const SnapshotHelperMT &) = delete;
1473 SnapshotHelperMT(SnapshotHelperMT &&) = default;
1474
1475 void InitTask(TTreeReader *r, unsigned int slot)
1476 {
1477 ::TDirectory::TContext c; // do not let tasks change the thread-local gDirectory
1478 if (!fOutputFiles[slot]) {
1479 // first time this thread executes something, let's create a TBufferMerger output directory
1480 fOutputFiles[slot] = fMerger->GetFile();
1481 }
1482 TDirectory *treeDirectory = fOutputFiles[slot].get();
1483 if (!fDirName.empty()) {
1484 // call returnExistingDirectory=true since MT can end up making this call multiple times
1485 treeDirectory = fOutputFiles[slot]->mkdir(fDirName.c_str(), "", true);
1486 }
1487 // re-create output tree as we need to create its branches again, with new input variables
1488 // TODO we could instead create the output tree and its branches, change addresses of input variables in each task
1489 fOutputTrees[slot] =
1490 std::make_unique<TTree>(fTreeName.c_str(), fTreeName.c_str(), fOptions.fSplitLevel, /*dir=*/treeDirectory);
1491 fOutputTrees[slot]->SetBit(TTree::kEntriesReshuffled);
1492 // TODO can be removed when RDF supports interleaved TBB task execution properly, see ROOT-10269
1493 fOutputTrees[slot]->SetImplicitMT(false);
1494 if (fOptions.fAutoFlush)
1495 fOutputTrees[slot]->SetAutoFlush(fOptions.fAutoFlush);
1496 if (r) {
1497 // not an empty-source RDF
1498 fInputTrees[slot] = r->GetTree();
1499 }
1500 fBranchAddressesNeedReset[slot] = 1; // reset first event flag for this slot
1501 }
1502
1503 void FinalizeTask(unsigned int slot)
1504 {
1505 if (fOutputTrees[slot]->GetEntries() > 0)
1506 fOutputFiles[slot]->Write();
1507 // clear now to avoid concurrent destruction of output trees and input tree (which has them listed as fClones)
1508 fOutputTrees[slot].reset(nullptr);
1509 fOutputBranches[slot].Clear();
1510 }
1511
1512 void Exec(unsigned int slot, ColTypes &... values)
1513 {
1514 using ind_t = std::index_sequence_for<ColTypes...>;
1515 if (fBranchAddressesNeedReset[slot] == 0) {
1516 UpdateCArraysPtrs(slot, values..., ind_t{});
1517 } else {
1518 SetBranches(slot, values..., ind_t{});
1519 fBranchAddressesNeedReset[slot] = 0;
1520 }
1521 UpdateBoolArrays(slot, values..., ind_t{});
1522 fOutputTrees[slot]->Fill();
1523 auto entries = fOutputTrees[slot]->GetEntries();
1524 auto autoFlush = fOutputTrees[slot]->GetAutoFlush();
1525 if ((autoFlush > 0) && (entries % autoFlush == 0))
1526 fOutputFiles[slot]->Write();
1527 }
1528
1529 template <std::size_t... S>
1530 void UpdateCArraysPtrs(unsigned int slot, ColTypes &... values, std::index_sequence<S...> /*dummy*/)
1531 {
1532 // This code deals with branches which hold C arrays of variable size. It can happen that the buffers
1533 // associated to those is re-allocated. As a result the value of the pointer can change therewith
1534 // leaving associated to the branch of the output tree an invalid pointer.
1535 // With this code, we set the value of the pointer in the output branch anew when needed.
1536 // Nota bene: the extra ",0" after the invocation of SetAddress, is because that method returns void and
1537 // we need an int for the expander list.
1538 (void)slot; // avoid bogus 'unused parameter' warning
1539 int expander[] = {(fBranches[slot][S] && fBranchAddresses[slot][S] != GetData(values)
1540 ? fBranches[slot][S]->SetAddress(GetData(values)),
1541 fBranchAddresses[slot][S] = GetData(values), 0 : 0, 0)...,
1542 0};
1543 (void)expander; // avoid unused variable warnings for older compilers such as gcc 4.9
1544 }
1545
1546 template <std::size_t... S>
1547 void SetBranches(unsigned int slot, ColTypes &... values, std::index_sequence<S...> /*dummy*/)
1548 {
1549 // hack to call TTree::Branch on all variadic template arguments
1550 int expander[] = {(SetBranchesHelper(fBoolArrays[slot], fInputTrees[slot], *fOutputTrees[slot],
1551 fInputBranchNames[S], fOutputBranchNames[S], fBranches[slot][S],
1552 fBranchAddresses[slot][S], &values, fOutputBranches[slot]),
1553 0)...,
1554 0};
1555 (void)expander; // avoid unused variable warnings for older compilers such as gcc 4.9
1556 (void)slot; // avoid unused variable warnings in gcc6.2
1557 }
1558
1559 template <std::size_t... S>
1560 void UpdateBoolArrays(unsigned int slot, ColTypes &... values, std::index_sequence<S...> /*dummy*/)
1561 {
1562 (void)slot; // avoid bogus 'unused parameter' warning
1563 int expander[] = {
1564 (UpdateBoolArray(fBoolArrays[slot], values, fOutputBranchNames[S], *fOutputTrees[slot]), 0)..., 0};
1565 (void)expander; // avoid unused variable warnings for older compilers such as gcc 4.9
1566 }
1567
1568 void Initialize()
1569 {
1570 const auto cs = ROOT::CompressionSettings(fOptions.fCompressionAlgorithm, fOptions.fCompressionLevel);
1571 auto out_file = TFile::Open(fFileName.c_str(), fOptions.fMode.c_str(), /*ftitle=*/fFileName.c_str(), cs);
1572 if(!out_file)
1573 throw std::runtime_error("Snapshot: could not create output file " + fFileName);
1574 fMerger = std::make_unique<ROOT::Experimental::TBufferMerger>(std::unique_ptr<TFile>(out_file));
1575 }
1576
1577 void Finalize()
1578 {
1579 const bool allNullFiles =
1580 std::all_of(fOutputFiles.begin(), fOutputFiles.end(),
1581 [](const std::shared_ptr<ROOT::Experimental::TBufferMergerFile> &ptr) { return ptr == nullptr; });
1582 R__ASSERT(!allNullFiles);
1583
1584 auto fileWritten = false;
1585 for (auto &file : fOutputFiles) {
1586 if (file) {
1587 file->Write();
1588 file->Close();
1589 fileWritten = true;
1590 }
1591 }
1592
1593 if (!fileWritten) {
1594 Warning("Snapshot",
1595 "No input entries (input TTree was empty or no entry passed the Filters). Output TTree is empty.");
1596 }
1597
1598 // flush all buffers to disk by destroying the TBufferMerger
1599 fOutputFiles.clear();
1600 fMerger.reset();
1601 }
1602
1603 std::string GetActionName() { return "Snapshot"; }
1604
1605 std::function<void(unsigned int)> GetDataBlockCallback() final
1606 {
1607 return [this](unsigned int slot) mutable { fBranchAddressesNeedReset[slot] = 1; };
1608 }
1609};
1610
1611template <typename Acc, typename Merge, typename R, typename T, typename U,
1612 bool MustCopyAssign = std::is_same<R, U>::value>
1613class AggregateHelper : public RActionImpl<AggregateHelper<Acc, Merge, R, T, U, MustCopyAssign>> {
1614 Acc fAggregate;
1615 Merge fMerge;
1616 const std::shared_ptr<U> fResult;
1617 Results<U> fAggregators;
1618
1619public:
1620 using ColumnTypes_t = TypeList<T>;
1621 AggregateHelper(Acc &&f, Merge &&m, const std::shared_ptr<U> &result, const unsigned int nSlots)
1622 : fAggregate(std::move(f)), fMerge(std::move(m)), fResult(result), fAggregators(nSlots, *result)
1623 {
1624 }
1625 AggregateHelper(AggregateHelper &&) = default;
1626 AggregateHelper(const AggregateHelper &) = delete;
1627
1628 void InitTask(TTreeReader *, unsigned int) {}
1629
1630 template <bool MustCopyAssign_ = MustCopyAssign, typename std::enable_if<MustCopyAssign_, int>::type = 0>
1631 void Exec(unsigned int slot, const T &value)
1632 {
1633 fAggregators[slot] = fAggregate(fAggregators[slot], value);
1634 }
1635
1636 template <bool MustCopyAssign_ = MustCopyAssign, typename std::enable_if<!MustCopyAssign_, int>::type = 0>
1637 void Exec(unsigned int slot, const T &value)
1638 {
1639 fAggregate(fAggregators[slot], value);
1640 }
1641
1642 void Initialize() { /* noop */}
1643
1644 template <typename MergeRet = typename CallableTraits<Merge>::ret_type,
1645 bool MergeAll = std::is_same<void, MergeRet>::value>
1646 typename std::enable_if<MergeAll, void>::type Finalize()
1647 {
1648 fMerge(fAggregators);
1649 *fResult = fAggregators[0];
1650 }
1651
1652 template <typename MergeRet = typename CallableTraits<Merge>::ret_type,
1653 bool MergeTwoByTwo = std::is_same<U, MergeRet>::value>
1654 typename std::enable_if<MergeTwoByTwo, void>::type Finalize(...) // ... needed to let compiler distinguish overloads
1655 {
1656 for (const auto &acc : fAggregators)
1657 *fResult = fMerge(*fResult, acc);
1658 }
1659
1660 U &PartialUpdate(unsigned int slot) { return fAggregators[slot]; }
1661
1662 std::string GetActionName() { return "Aggregate"; }
1663};
1664
1665} // end of NS RDF
1666} // end of NS Internal
1667} // end of NS ROOT
1668
1669/// \endcond
1670
1671#endif
PyObject * fCallable
double
size_t fSize
Handle_t Display_t
Display handle.
Definition GuiTypes.h:27
ROOT::R::TRInterface & r
Definition Object.C:4
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define h(i)
Definition RSha256.hxx:106
static const double x2[5]
static const double x1[5]
static const double x3[11]
double Double_t
Definition RtypesCore.h:59
unsigned long long ULong64_t
Definition RtypesCore.h:74
#define R__ASSERT(e)
Definition TError.h:120
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:231
char name[80]
Definition TGX11.cxx:110
int type
Definition TGX11.cxx:121
Binding & operator=(OUT(*fun)(void))
typedef void((*Func_t)())
This class is the textual representation of the content of a columnar dataset.
Definition RDisplay.hxx:65
typename RemoveFirstParameter< T >::type RemoveFirstParameter_t
A "std::vector"-like collection of values implementing handy operation to analyse them.
Definition RVec.hxx:296
iterator end() noexcept
Definition RVec.hxx:422
iterator begin() noexcept
Definition RVec.hxx:419
A TTree is a list of TBranches.
Definition TBranch.h:89
virtual const char * GetClassName() const
Return the name of the user class whose content is stored in this branch, if any.
Definition TBranch.cxx:1324
virtual char * GetAddress() const
Definition TBranch.h:208
Int_t GetSplitLevel() const
Definition TBranch.h:246
virtual Int_t GetBasketSize() const
Definition TBranch.h:213
TObjArray * GetListOfLeaves()
Definition TBranch.h:243
TClassRef is used to implement a permanent reference to a TClass object.
Definition TClassRef.h:28
virtual void SetOwner(Bool_t enable=kTRUE)
Set whether this collection is the owner (enable==true) of its content.
Small helper to keep current directory context.
Definition TDirectory.h:52
Describe directory structure in memory.
Definition TDirectory.h:45
virtual TDirectory * mkdir(const char *name, const char *title="", Bool_t returnExistingDirectory=kFALSE)
Create a sub-directory "a" or a hierarchy of sub-directories "a/b/c/...".
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Create / open a file.
Definition TFile.cxx:3997
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
1-D histogram with a double per channel (see TH1 documentation)}
Definition TH1.h:618
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:58
A TLeaf describes individual elements of a TBranch See TBranch structure in TTree.
Definition TLeaf.h:57
A doubly linked list.
Definition TList.h:44
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition TNamed.cxx:164
virtual const char * GetTitle() const
Returns title of object.
Definition TNamed.h:48
virtual const char * GetName() const
Returns name of object.
Definition TNamed.h:47
TObject * UncheckedAt(Int_t i) const
Definition TObjArray.h:90
virtual Int_t Write(const char *name=0, Int_t option=0, Int_t bufsize=0)
Write this object to the current directory.
Definition TObject.cxx:798
Basic string class.
Definition TString.h:136
void ToLower()
Change string to lower-case.
Definition TString.cxx:1145
A simple, robust and fast interface to read values from ROOT columnar datasets such as TTree,...
Definition TTreeReader.h:44
A TTree represents a columnar dataset.
Definition TTree.h:79
virtual TBranch * FindBranch(const char *name)
Return the branch that correspond to the path 'branchname', which can include the name of the tree or...
Definition TTree.cxx:4828
virtual TBranch * GetBranch(const char *name)
Return pointer to the branch with the given name in this tree or its friends.
Definition TTree.cxx:5275
virtual Int_t SetBranchAddress(const char *bname, void *add, TBranch **ptr=0)
Change branch address, dealing with clone trees properly.
Definition TTree.cxx:8349
TBranch * Branch(const char *name, T *obj, Int_t bufsize=32000, Int_t splitlevel=99)
Add a new branch, and infer the data type from the type of obj being passed.
Definition TTree.h:350
@ kEntriesReshuffled
If set, signals that this TTree is the output of the processing of another TTree, and the entries are...
Definition TTree.h:258
RooCmdArg Columns(Int_t ncol)
Double_t x[n]
Definition legend1.C:17
#define F(x, y, z)
CPYCPPYY_EXTERN bool Exec(const std::string &cmd)
Definition API.cxx:333
std::vector< std::string > ColumnNames_t
std::unique_ptr< RMergeableValue< T > > GetMergeableValue(RResultPtr< T > &rptr)
Retrieve a mergeable value from an RDataFrame action.
std::vector< std::string > ReplaceDotWithUnderscore(const std::vector< std::string > &columnNames)
Replace occurrences of '.
Definition RDFUtils.cxx:299
void ValidateSnapshotOutput(const RSnapshotOptions &opts, const std::string &treeName, const std::string &fileName)
void SetBranchesHelper(BoolArrayMap &boolArrays, TTree *inputTree, TTree &outputTree, const std::string &inName, const std::string &outName, TBranch *&branch, void *&branchAddress, ROOT::VecOps::RVec< T > *ab, RBranchSet &outputBranches)
char TypeName2ROOTTypeName(const std::string &b)
Convert type name (e.g.
Definition RDFUtils.cxx:255
std::map< std::string, BoolArray > BoolArrayMap
Definition RVec.hxx:72
double T(double x)
ROOT type_traits extensions.
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
@ kSTLvector
Definition ESTLType.h:30
int CompressionSettings(RCompressionSetting::EAlgorithm algorithm, int compressionLevel)
RooArgSet S(const RooAbsArg &v1)
ROOT::ESTLType IsSTLCont(std::string_view type)
type : type name: vector<list<classA,allocator>,allocator> result: 0 : not stl container code of cont...
void Initialize(Bool_t useTMVAStyle=kTRUE)
Definition tmvaglob.cxx:176
Definition file.py:1
const char * Size
Definition TXMLSetup.cxx:56
A collection of options to steer the creation of the dataset on file.
int fAutoFlush
AutoFlush value for output tree.
std::string fMode
Mode of creation of output file.
ECAlgo fCompressionAlgorithm
Compression algorithm of output file.
int fSplitLevel
Split level of output tree.
int fCompressionLevel
Compression level of output file.
Lightweight storage for a collection of types.
auto * m
Definition textangle.C:8
auto * l
Definition textangle.C:4
void ws()
Definition ws.C:66