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