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 \author Enrico Guiraud, CERN
4 \author Danilo Piparo, CERN
5 \date 2016-12
6 \author Vincenzo Eduardo Padulano
7 \date 2020-06
8*/
9
10/*************************************************************************
11 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. *
12 * All rights reserved. *
13 * *
14 * For the licensing terms see $ROOTSYS/LICENSE. *
15 * For the list of contributors see $ROOTSYS/README/CREDITS. *
16 *************************************************************************/
17
18#ifndef ROOT_RDFOPERATIONS
19#define ROOT_RDFOPERATIONS
20
21#include "ROOT/RVec.hxx"
22#include "ROOT/RDF/Utils.hxx"
23#include "ROOT/TypeTraits.hxx"
24#include "ROOT/RDF/RDisplay.hxx"
25#include "RtypesCore.h"
26#include "TH1.h"
27#include "TH3.h"
28#include "TGraph.h"
29#include "TGraphAsymmErrors.h"
30#include "TObject.h"
33#include <cmath>
34#include <cstddef>
35#include <cstdio>
36
37#include "RConfigure.h" // for R__HAS_ROOT7
38#ifdef R__HAS_ROOT7
39#include <ROOT/RHist.hxx>
41#include <ROOT/RHistEngine.hxx>
43#include <ROOT/RProfile.hxx>
46#include <ROOT/RWeight.hxx>
47#endif
48
49#include <algorithm>
50#include <array>
51#include <iterator>
52#include <limits>
53#include <memory>
54#include <mutex>
55#include <stdexcept>
56#include <string>
57#include <string_view>
58#include <tuple>
59#include <type_traits>
60#include <utility> // std::index_sequence
61#include <vector>
62#include <numeric> // std::accumulate in MeanHelper
63
64class TCollection;
65class TStatistic;
66class TTreeReader;
67namespace ROOT::RDF {
68class RCutFlowReport;
69} // namespace ROOT::RDF
70
71/// \cond HIDDEN_SYMBOLS
72
73namespace ROOT {
74namespace Internal {
75namespace RDF {
76using namespace ROOT::TypeTraits;
77using namespace ROOT::VecOps;
78using namespace ROOT::RDF;
79using namespace ROOT::Detail::RDF;
80
81using Hist_t = ::TH1D;
82
83/// The container type for each thread's partial result in an action helper
84// We have to avoid to instantiate std::vector<bool> as that makes it impossible to return a reference to one of
85// the thread-local results. In addition, a common definition for the type of the container makes it easy to swap
86// the type of the underlying container if e.g. we see problems with false sharing of the thread-local results..
87template <typename T>
88using Results = std::conditional_t<std::is_same<T, bool>::value, std::deque<T>, std::vector<T>>;
89
90template <typename F>
91class R__CLING_PTRCHECK(off) ForeachSlotHelper : public RActionImpl<ForeachSlotHelper<F>> {
92 F fCallable;
93
94public:
96 ForeachSlotHelper(F &&f) : fCallable(f) {}
97 ForeachSlotHelper(ForeachSlotHelper &&) = default;
98 ForeachSlotHelper(const ForeachSlotHelper &) = delete;
99
100 void InitTask(TTreeReader *, unsigned int) {}
101
102 template <typename... Args>
103 void Exec(unsigned int slot, Args &&... args)
104 {
105 // check that the decayed types of Args are the same as the branch types
106 static_assert(std::is_same<TypeList<std::decay_t<Args>...>, ColumnTypes_t>::value, "");
107 fCallable(slot, std::forward<Args>(args)...);
108 }
109
110 void Initialize() { /* noop */}
111
112 void Finalize() { /* noop */}
113
114 std::string GetActionName() { return "ForeachSlot"; }
115};
116
117class R__CLING_PTRCHECK(off) CountHelper : public RActionImpl<CountHelper> {
118 std::shared_ptr<ULong64_t> fResultCount;
119 Results<ULong64_t> fCounts;
120
121public:
122 using ColumnTypes_t = TypeList<>;
123 CountHelper(const std::shared_ptr<ULong64_t> &resultCount, const unsigned int nSlots);
124 CountHelper(CountHelper &&) = default;
125 CountHelper(const CountHelper &) = delete;
126 void InitTask(TTreeReader *, unsigned int) {}
127 void Exec(unsigned int slot);
128 void Initialize() { /* noop */}
129 void Finalize();
130
131 // Helper functions for RMergeableValue
132 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
133 {
134 return std::make_unique<RMergeableCount>(*fResultCount);
135 }
136
137 ULong64_t &PartialUpdate(unsigned int slot);
138
139 std::string GetActionName() { return "Count"; }
140
141 CountHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
142 {
143 auto &result = *static_cast<std::shared_ptr<ULong64_t> *>(newResult);
144 return CountHelper(result, fCounts.size());
145 }
146};
147
148template <typename RNode_t>
149class R__CLING_PTRCHECK(off) ReportHelper : public RActionImpl<ReportHelper<RNode_t>> {
150 std::shared_ptr<RCutFlowReport> fReport;
151 /// Non-owning pointer, never null. As usual, the node is owned by its children nodes (and therefore indirectly by
152 /// the RAction corresponding to this action helper).
153 RNode_t *fNode;
154 bool fReturnEmptyReport;
155
156public:
157 using ColumnTypes_t = TypeList<>;
158 ReportHelper(const std::shared_ptr<RCutFlowReport> &report, RNode_t *node, bool emptyRep)
159 : fReport(report), fNode(node), fReturnEmptyReport(emptyRep){};
160 ReportHelper(ReportHelper &&) = default;
161 ReportHelper(const ReportHelper &) = delete;
162 void InitTask(TTreeReader *, unsigned int) {}
163 void Exec(unsigned int /* slot */) {}
164 void Initialize() { /* noop */}
165 void Finalize()
166 {
167 if (!fReturnEmptyReport)
168 fNode->Report(*fReport);
169 }
170
171 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
172 {
173 auto cutinfo_vec = fReport->fCutInfos;
174 return std::make_unique<RMergeableReport>(*fReport, cutinfo_vec);
175 }
176
177 std::string GetActionName() { return "Report"; }
178
179 ReportHelper MakeNew(void *newResult, std::string_view variation = "nominal")
180 {
181 auto &&result = *static_cast<std::shared_ptr<RCutFlowReport> *>(newResult);
182 return ReportHelper{result,
183 std::static_pointer_cast<RNode_t>(fNode->GetVariedFilter(std::string(variation))).get(),
184 fReturnEmptyReport};
185 }
186};
187
188/// This helper fills TH1Ds for which no axes were specified by buffering the fill values to pick good axes limits.
189///
190/// TH1Ds have an automatic mechanism to pick good limits based on the first N entries they were filled with, but
191/// that does not work in multi-thread event loops as it might yield histograms with incompatible binning in each
192/// thread, making it impossible to merge the per-thread results.
193/// Instead, this helper delays the decision on the axes limits until all threads have done processing, synchronizing
194/// the decision on the limits as part of the merge operation.
195class R__CLING_PTRCHECK(off) BufferedFillHelper : public RActionImpl<BufferedFillHelper> {
196 // this sets a total initial size of 16 MB for the buffers (can increase)
197 static constexpr unsigned int fgTotalBufSize = 2097152;
198 using BufEl_t = double;
199 using Buf_t = std::vector<BufEl_t>;
200
201 std::vector<Buf_t> fBuffers;
202 std::vector<Buf_t> fWBuffers;
203 std::shared_ptr<Hist_t> fResultHist;
204 unsigned int fNSlots;
205 unsigned int fBufSize;
206 /// Histograms containing "snapshots" of partial results. Non-null only if a registered callback requires it.
207 Results<std::unique_ptr<Hist_t>> fPartialHists;
208 Buf_t fMin;
209 Buf_t fMax;
210
211 void UpdateMinMax(unsigned int slot, double v);
212
213public:
214 BufferedFillHelper(const std::shared_ptr<Hist_t> &h, const unsigned int nSlots);
215 BufferedFillHelper(BufferedFillHelper &&) = default;
216 BufferedFillHelper(const BufferedFillHelper &) = delete;
217 void InitTask(TTreeReader *, unsigned int) {}
218 void Exec(unsigned int slot, double v);
219 void Exec(unsigned int slot, double v, double w);
220
221 template <typename T, std::enable_if_t<IsDataContainer<T>::value, int> = 0>
222 void Exec(unsigned int slot, const T &vs)
223 {
224 auto &thisBuf = fBuffers[slot];
225 // range-based for results in warnings on some compilers due to vector<bool>'s custom reference type
226 for (auto v = vs.begin(); v != vs.end(); ++v) {
227 UpdateMinMax(slot, *v);
228 thisBuf.emplace_back(*v); // TODO: Can be optimised in case T == BufEl_t
229 }
230 }
231
232 template <typename T, typename W, std::enable_if_t<IsDataContainer<T>::value && IsDataContainer<W>::value, int> = 0>
233 void Exec(unsigned int slot, const T &vs, const W &ws)
234 {
235 auto &thisBuf = fBuffers[slot];
236
237 for (auto &v : vs) {
238 UpdateMinMax(slot, v);
239 thisBuf.emplace_back(v);
240 }
241
242 auto &thisWBuf = fWBuffers[slot];
243 for (auto &w : ws) {
244 thisWBuf.emplace_back(w); // TODO: Can be optimised in case T == BufEl_t
245 }
246 }
247
248 template <typename T, typename W, std::enable_if_t<IsDataContainer<T>::value && !IsDataContainer<W>::value, int> = 0>
249 void Exec(unsigned int slot, const T &vs, const W w)
250 {
251 auto &thisBuf = fBuffers[slot];
252 for (auto &v : vs) {
253 UpdateMinMax(slot, v);
254 thisBuf.emplace_back(v); // TODO: Can be optimised in case T == BufEl_t
255 }
256
257 auto &thisWBuf = fWBuffers[slot];
258 thisWBuf.insert(thisWBuf.end(), vs.size(), w);
259 }
260
261 template <typename T, typename W, std::enable_if_t<IsDataContainer<W>::value && !IsDataContainer<T>::value, int> = 0>
262 void Exec(unsigned int slot, const T v, const W &ws)
263 {
264 UpdateMinMax(slot, v);
265 auto &thisBuf = fBuffers[slot];
266 thisBuf.insert(thisBuf.end(), ws.size(), v);
267
268 auto &thisWBuf = fWBuffers[slot];
269 thisWBuf.insert(thisWBuf.end(), ws.begin(), ws.end());
270 }
271
272 Hist_t &PartialUpdate(unsigned int);
273
274 void Initialize() { /* noop */}
275
276 void Finalize();
277
278 // Helper functions for RMergeableValue
279 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
280 {
281 return std::make_unique<RMergeableFill<Hist_t>>(*fResultHist);
282 }
283
284 std::string GetActionName()
285 {
286 return std::string(fResultHist->IsA()->GetName()) + "\\n" + std::string(fResultHist->GetName());
287 }
288
289 BufferedFillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
290 {
291 auto &result = *static_cast<std::shared_ptr<Hist_t> *>(newResult);
292 result->Reset();
293 result->SetDirectory(nullptr);
294 return BufferedFillHelper(result, fNSlots);
295 }
296};
297
298// class which wraps a pointer and implements a no-op increment operator
299template <typename T>
300class ScalarConstIterator {
301 const T *obj_;
302
303public:
304 using iterator_category = std::forward_iterator_tag;
305 using difference_type = std::ptrdiff_t;
306 using value_type = T;
307 using pointer = T *;
308 using reference = T &;
309 ScalarConstIterator(const T *obj) : obj_(obj) {}
310 const T &operator*() const { return *obj_; }
311 ScalarConstIterator<T> &operator++() { return *this; }
312};
313
314// return unchanged value for scalar
315template <typename T>
316auto MakeBegin(const T &val)
317{
318 if constexpr (IsDataContainer<T>::value) {
319 return std::begin(val);
320 } else {
321 return ScalarConstIterator<T>(&val);
322 }
323}
324
325// return container size for containers, and 1 for scalars
326template <typename T>
327std::size_t GetSize(const T &val)
328{
329 if constexpr (IsDataContainer<T>::value) {
330 return std::size(val);
331 } else {
332 return 1;
333 }
334}
335
336// trait class to implement looping over data containers
337template <typename Helper>
338class R__CLING_PTRCHECK(off) ExecLoopTrait {
339private:
340 template <typename... Iterators>
341 void ExecLoop(unsigned int slot, std::size_t elements, Iterators... its)
342 {
343 for (std::size_t i = 0; i < elements; i++) {
344 Exec(slot, *its...);
345 (std::advance(its, 1), ...);
346 }
347 }
348
349public:
350 template <typename... ColumnTypes>
351 void Exec(unsigned int slot, const ColumnTypes &...columnValues)
352 {
353 if constexpr (std::disjunction_v<IsDataContainer<ColumnTypes>...>) {
354 constexpr std::array<bool, sizeof...(ColumnTypes)> isContainer{IsDataContainer<ColumnTypes>::value...};
355 constexpr std::size_t firstContainerIdx = FindIdxTrue(isContainer);
356 std::array<std::size_t, sizeof...(columnValues)> sizes = {{GetSize(columnValues)...}};
357 std::size_t elements = 0;
358 for (std::size_t i = 0; i < isContainer.size(); i++) {
359 if (isContainer[i]) {
360 if (i == firstContainerIdx) {
361 elements = sizes[i];
362 } else if (elements != sizes[i]) {
363 throw std::runtime_error("Cannot fill values in containers of different sizes.");
364 }
365 }
366 }
367 ExecLoop(slot, elements, MakeBegin(columnValues)...);
368 } else {
369 static_cast<Helper *>(this)->ExecSingle(slot, columnValues...);
370 }
371 }
372};
373
374// Helpers for dealing with histograms and similar:
375template <typename H, typename = decltype(std::declval<H>().Reset())>
376void ResetIfPossible(H *h)
377{
378 h->Reset();
379}
380
382void ResetIfPossible(...);
383
386
387/// The generic Fill helper: it calls Fill on per-thread objects and then Merge to produce a final result.
388/// For one-dimensional histograms, if no axes are specified, RDataFrame uses BufferedFillHelper instead.
389template <typename HIST = Hist_t>
390class R__CLING_PTRCHECK(off) FillHelper : public RActionImpl<FillHelper<HIST>> {
391 std::vector<HIST *> fObjects;
392
393 // Merge overload for types with Merge(TCollection*), like TH1s
394 template <typename H, typename = std::enable_if_t<std::is_base_of<TObject, H>::value, int>>
395 auto Merge(std::vector<H *> &objs, int /*toincreaseoverloadpriority*/)
396 -> decltype(objs[0]->Merge((TCollection *)nullptr), void())
397 {
398 TList l;
399 for (auto it = ++objs.begin(); it != objs.end(); ++it)
400 l.Add(*it);
401 objs[0]->Merge(&l);
402 }
403
404 // Merge overload for types with Merge(const std::vector&)
405 template <typename H>
406 auto Merge(std::vector<H *> &objs, double /*toloweroverloadpriority*/)
407 -> decltype(objs[0]->Merge(std::vector<HIST *>{}), void())
408 {
409 objs[0]->Merge({++objs.begin(), objs.end()});
410 }
411
412 // Merge overload to error out in case no valid HIST::Merge method was detected
413 template <typename T>
414 void Merge(T, ...)
415 {
416 static_assert(sizeof(T) < 0,
417 "The type passed to Fill does not provide a Merge(TCollection*) or Merge(const std::vector&) method.");
418 }
419
420 template <std::size_t ColIdx, typename End_t, typename... Its>
421 void ExecLoop(unsigned int slot, End_t end, Its... its)
422 {
423 for (auto *thisSlotH = fObjects[slot]; GetNthElement<ColIdx>(its...) != end; (std::advance(its, 1), ...)) {
424 thisSlotH->Fill(*its...);
425 }
426 }
427
428public:
429 FillHelper(FillHelper &&) = default;
430 FillHelper(const FillHelper &) = delete;
431
432 FillHelper(const std::shared_ptr<HIST> &h, const unsigned int nSlots) : fObjects(nSlots, nullptr)
433 {
434 fObjects[0] = h.get();
435 // Initialize all other slots
436 for (unsigned int i = 1; i < nSlots; ++i) {
437 fObjects[i] = new HIST(*fObjects[0]);
438 UnsetDirectoryIfPossible(fObjects[i]);
439 }
440 }
441
442 void InitTask(TTreeReader *, unsigned int) {}
443
444 // no container arguments
445 template <typename... ValTypes, std::enable_if_t<!std::disjunction<IsDataContainer<ValTypes>...>::value, int> = 0>
446 auto Exec(unsigned int slot, const ValTypes &...x) -> decltype(fObjects[slot]->Fill(x...), void())
447 {
448 fObjects[slot]->Fill(x...);
449 }
450
451 // at least one container argument
452 template <typename... Xs, std::enable_if_t<std::disjunction<IsDataContainer<Xs>...>::value, int> = 0>
453 auto Exec(unsigned int slot, const Xs &...xs) -> decltype(fObjects[slot]->Fill(*MakeBegin(xs)...), void())
454 {
455 // array of bools keeping track of which inputs are containers
456 constexpr std::array<bool, sizeof...(Xs)> isContainer{IsDataContainer<Xs>::value...};
457
458 // index of the first container input
459 constexpr std::size_t colidx = FindIdxTrue(isContainer);
460 // if this happens, there is a bug in the implementation
461 static_assert(colidx < sizeof...(Xs), "Error: index of collection-type argument not found.");
462
463 // get the end iterator to the first container
464 auto const xrefend = std::end(GetNthElement<colidx>(xs...));
465
466 // array of container sizes (1 for scalars)
467 std::array<std::size_t, sizeof...(xs)> sizes = {{GetSize(xs)...}};
468
469 for (std::size_t i = 0; i < sizeof...(xs); ++i) {
470 if (isContainer[i] && sizes[i] != sizes[colidx]) {
471 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
472 }
473 }
474
475 ExecLoop<colidx>(slot, xrefend, MakeBegin(xs)...);
476 }
477
478 template <typename T = HIST>
479 void Exec(...)
480 {
481 static_assert(sizeof(T) < 0,
482 "When filling an object with RDataFrame (e.g. via a Fill action) the number or types of the "
483 "columns passed did not match the signature of the object's `Fill` method.");
484 }
485
486 void Initialize() { /* noop */}
487
488 void Finalize()
489 {
490 if (fObjects.size() == 1)
491 return;
492
493 Merge(fObjects, /*toselectcorrectoverload=*/0);
494
495 // delete the copies we created for the slots other than the first
496 for (auto it = ++fObjects.begin(); it != fObjects.end(); ++it)
497 delete *it;
498 }
499
500 HIST &PartialUpdate(unsigned int slot) { return *fObjects[slot]; }
501
502 // Helper functions for RMergeableValue
503 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
504 {
505 return std::make_unique<RMergeableFill<HIST>>(*fObjects[0]);
506 }
507
508 // if the fObjects vector type is derived from TObject, return the name of the object
509 template <typename T = HIST, std::enable_if_t<std::is_base_of<TObject, T>::value, int> = 0>
510 std::string GetActionName()
511 {
512 return std::string(fObjects[0]->IsA()->GetName()) + "\\n" + std::string(fObjects[0]->GetName());
513 }
514
515 // if fObjects is not derived from TObject, indicate it is some other object
516 template <typename T = HIST, std::enable_if_t<!std::is_base_of<TObject, T>::value, int> = 0>
517 std::string GetActionName()
518 {
519 return "Fill custom object";
520 }
521
522 template <typename H = HIST>
523 FillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
524 {
525 auto &result = *static_cast<std::shared_ptr<H> *>(newResult);
526 ResetIfPossible(result.get());
528 return FillHelper(result, fObjects.size());
529 }
530};
531
532#ifdef R__HAS_ROOT7
533template <typename BinContentType, bool WithWeight = false>
534class R__CLING_PTRCHECK(off) RHistFillHelper : public RActionImpl<RHistFillHelper<BinContentType, WithWeight>>,
535 public ExecLoopTrait<RHistFillHelper<BinContentType, WithWeight>> {
536public:
538
539private:
540 std::unique_ptr<ROOT::Experimental::RHistConcurrentFiller<BinContentType>> fFiller;
541 std::vector<std::shared_ptr<ROOT::Experimental::RHistFillContext<BinContentType>>> fContexts;
542
543public:
544 RHistFillHelper(std::shared_ptr<ROOT::Experimental::RHist<BinContentType>> h, unsigned int nSlots)
545 : fFiller(new ROOT::Experimental::RHistConcurrentFiller<BinContentType>(h)), fContexts(nSlots)
546 {
547 for (unsigned int i = 0; i < nSlots; i++) {
548 fContexts[i] = fFiller->CreateFillContext();
549 }
550 }
551 RHistFillHelper(const RHistFillHelper &) = delete;
552 RHistFillHelper(RHistFillHelper &&) = default;
553 RHistFillHelper &operator=(const RHistFillHelper &) = delete;
554 RHistFillHelper &operator=(RHistFillHelper &&) = default;
555 ~RHistFillHelper() = default;
556
557 void Initialize() {}
558 void InitTask(TTreeReader *, unsigned int) {}
559
560 template <typename... ColumnTypes, const std::size_t... I>
561 void
562 ExecWithWeight(unsigned int slot, const std::tuple<const ColumnTypes &...> &columnValues, std::index_sequence<I...>)
563 {
564 // Build a tuple of const references with the actual arguments, stripping the weight and avoiding copies.
565 std::tuple<const std::tuple_element_t<I, std::tuple<ColumnTypes...>> &...> args(std::get<I>(columnValues)...);
566 ROOT::Experimental::RWeight weight(std::get<sizeof...(ColumnTypes) - 1>(columnValues));
567 fContexts[slot]->Fill(args, weight);
568 }
569
570 template <typename... ColumnTypes>
571 void ExecSingle(unsigned int slot, const ColumnTypes &...columnValues)
572 {
573 if constexpr (WithWeight) {
574 auto t = std::forward_as_tuple(columnValues...);
575 ExecWithWeight(slot, t, std::make_index_sequence<sizeof...(ColumnTypes) - 1>());
576 } else {
577 fContexts[slot]->Fill(columnValues...);
578 }
579 }
580
581 void Finalize()
582 {
583 for (auto &&context : fContexts) {
584 context->Flush();
585 }
586 }
587
588 RHistFillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
589 {
590 auto &result = *static_cast<std::shared_ptr<Result_t> *>(newResult);
591 result->Clear();
592 return RHistFillHelper(result, fContexts.size());
593 }
594
595 std::string GetActionName() { return "Hist"; }
596};
597
598template <typename BinContentType, bool WithWeight = false>
599class R__CLING_PTRCHECK(off) RHistEngineFillHelper
600 : public RActionImpl<RHistEngineFillHelper<BinContentType, WithWeight>>,
601 public ExecLoopTrait<RHistEngineFillHelper<BinContentType, WithWeight>> {
602public:
604
605private:
606 std::shared_ptr<Result_t> fHist;
607
608public:
609 RHistEngineFillHelper(std::shared_ptr<ROOT::Experimental::RHistEngine<BinContentType>> h) : fHist(h) {}
610 RHistEngineFillHelper(const RHistEngineFillHelper &) = delete;
611 RHistEngineFillHelper(RHistEngineFillHelper &&) = default;
612 RHistEngineFillHelper &operator=(const RHistEngineFillHelper &) = delete;
613 RHistEngineFillHelper &operator=(RHistEngineFillHelper &&) = default;
614 ~RHistEngineFillHelper() = default;
615
616 void Initialize() {}
617 void InitTask(TTreeReader *, unsigned int) {}
618
619 template <typename... ColumnTypes, const std::size_t... I>
620 void ExecWithWeight(const std::tuple<const ColumnTypes &...> &columnValues, std::index_sequence<I...>)
621 {
622 // Build a tuple of const references with the actual arguments, stripping the weight and avoiding copies.
623 std::tuple<const std::tuple_element_t<I, std::tuple<ColumnTypes...>> &...> args(std::get<I>(columnValues)...);
624 ROOT::Experimental::RWeight weight(std::get<sizeof...(ColumnTypes) - 1>(columnValues));
625 fHist->FillAtomic(args, weight);
626 }
627
628 template <typename... ColumnTypes>
629 void ExecSingle(unsigned int, const ColumnTypes &...columnValues)
630 {
631 if constexpr (WithWeight) {
632 auto t = std::forward_as_tuple(columnValues...);
633 ExecWithWeight(t, std::make_index_sequence<sizeof...(ColumnTypes) - 1>());
634 } else {
635 fHist->FillAtomic(columnValues...);
636 }
637 }
638
639 void Finalize() {}
640
641 RHistEngineFillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
642 {
643 auto &result = *static_cast<std::shared_ptr<Result_t> *>(newResult);
644 result->Clear();
645 return RHistEngineFillHelper(result);
646 }
647
648 std::string GetActionName() { return "Hist"; }
649};
650
651template <bool WithWeight = false>
652class R__CLING_PTRCHECK(off) RProfileFillHelper : public RActionImpl<RProfileFillHelper<WithWeight>>,
653 public ExecLoopTrait<RProfileFillHelper<WithWeight>> {
654public:
655 using Result_t = ROOT::Experimental::RProfile;
656
657private:
658 std::unique_ptr<ROOT::Experimental::RProfileConcurrentFiller> fFiller;
659 std::vector<std::shared_ptr<ROOT::Experimental::RProfileFillContext>> fContexts;
660
661public:
662 RProfileFillHelper(std::shared_ptr<ROOT::Experimental::RProfile> p, unsigned int nSlots)
663 : fFiller(new ROOT::Experimental::RProfileConcurrentFiller(p)), fContexts(nSlots)
664 {
665 for (unsigned int i = 0; i < nSlots; i++) {
666 fContexts[i] = fFiller->CreateFillContext();
667 }
668 }
669 RProfileFillHelper(const RProfileFillHelper &) = delete;
670 RProfileFillHelper(RProfileFillHelper &&) = default;
671 RProfileFillHelper &operator=(const RProfileFillHelper &) = delete;
672 RProfileFillHelper &operator=(RProfileFillHelper &&) = default;
673 ~RProfileFillHelper() = default;
674
675 void Initialize() {}
676 void InitTask(TTreeReader *, unsigned int) {}
677
678 template <typename... ColumnTypes, const std::size_t... I>
679 void ExecWithValueAndWeight(unsigned int slot, const std::tuple<const ColumnTypes &...> &columnValues,
680 std::index_sequence<I...>)
681 {
682 // Build a tuple of const references with the actual arguments, stripping the value and weight and avoiding
683 // copies.
684 std::tuple<const std::tuple_element_t<I, std::tuple<ColumnTypes...>> &...> args(std::get<I>(columnValues)...);
685 const auto &value = std::get<sizeof...(ColumnTypes) - 2>(columnValues);
686 ROOT::Experimental::RWeight weight(std::get<sizeof...(ColumnTypes) - 1>(columnValues));
687 fContexts[slot]->Fill(args, value, weight);
688 }
689
690 template <typename... ColumnTypes, const std::size_t... I>
691 void
692 ExecWithValue(unsigned int slot, const std::tuple<const ColumnTypes &...> &columnValues, std::index_sequence<I...>)
693 {
694 // Build a tuple of const references with the actual arguments, stripping the value and avoiding copies.
695 std::tuple<const std::tuple_element_t<I, std::tuple<ColumnTypes...>> &...> args(std::get<I>(columnValues)...);
696 const auto &value = std::get<sizeof...(ColumnTypes) - 1>(columnValues);
697 fContexts[slot]->Fill(args, value);
698 }
699
700 template <typename... ColumnTypes>
701 void ExecSingle(unsigned int slot, const ColumnTypes &...columnValues)
702 {
703 auto t = std::forward_as_tuple(columnValues...);
704 if constexpr (WithWeight) {
705 ExecWithValueAndWeight(slot, t, std::make_index_sequence<sizeof...(ColumnTypes) - 2>());
706 } else {
707 ExecWithValue(slot, t, std::make_index_sequence<sizeof...(ColumnTypes) - 1>());
708 }
709 }
710
711 void Finalize()
712 {
713 for (auto &&context : fContexts) {
714 context->Flush();
715 }
716 }
717
718 RProfileFillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
719 {
720 auto &result = *static_cast<std::shared_ptr<Result_t> *>(newResult);
721 result->Clear();
722 return RProfileFillHelper(result, fContexts.size());
723 }
724
725 std::string GetActionName() { return "Profile"; }
726};
727#endif
728
729class R__CLING_PTRCHECK(off) FillTGraphHelper : public ROOT::Detail::RDF::RActionImpl<FillTGraphHelper> {
730public:
731 using Result_t = ::TGraph;
732
733private:
734 std::vector<::TGraph *> fGraphs;
735
736public:
737 FillTGraphHelper(FillTGraphHelper &&) = default;
738 FillTGraphHelper(const FillTGraphHelper &) = delete;
739
740 FillTGraphHelper(const std::shared_ptr<::TGraph> &g, const unsigned int nSlots) : fGraphs(nSlots, nullptr)
741 {
742 fGraphs[0] = g.get();
743 // Initialize all other slots
744 for (unsigned int i = 1; i < nSlots; ++i) {
745 fGraphs[i] = new TGraph(*fGraphs[0]);
746 }
747 }
748
749 void Initialize() {}
750 void InitTask(TTreeReader *, unsigned int) {}
751
752 // case: both types are container types
753 template <typename X0, typename X1,
754 std::enable_if_t<IsDataContainer<X0>::value && IsDataContainer<X1>::value, int> = 0>
755 void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s)
756 {
757 if (x0s.size() != x1s.size()) {
758 throw std::runtime_error("Cannot fill Graph with values in containers of different sizes.");
759 }
760 auto *thisSlotG = fGraphs[slot];
761 auto x0sIt = std::begin(x0s);
762 const auto x0sEnd = std::end(x0s);
763 auto x1sIt = std::begin(x1s);
764 for (; x0sIt != x0sEnd; x0sIt++, x1sIt++) {
765 thisSlotG->SetPoint(thisSlotG->GetN(), *x0sIt, *x1sIt);
766 }
767 }
768
769 // case: both types are non-container types, e.g. scalars
770 template <typename X0, typename X1,
771 std::enable_if_t<!IsDataContainer<X0>::value && !IsDataContainer<X1>::value, int> = 0>
772 void Exec(unsigned int slot, X0 x0, X1 x1)
773 {
774 auto thisSlotG = fGraphs[slot];
775 thisSlotG->SetPoint(thisSlotG->GetN(), x0, x1);
776 }
777
778 // case: types are combination of containers and non-containers
779 // this is not supported, error out
780 template <typename X0, typename X1, typename... ExtraArgsToLowerPriority>
781 void Exec(unsigned int, X0, X1, ExtraArgsToLowerPriority...)
782 {
783 throw std::runtime_error("Graph was applied to a mix of scalar values and collections. This is not supported.");
784 }
785
786 void Finalize()
787 {
788 const auto nSlots = fGraphs.size();
789 auto resGraph = fGraphs[0];
790 TList l;
791 l.SetOwner(); // The list will free the memory associated to its elements upon destruction
792 for (unsigned int slot = 1; slot < nSlots; ++slot) {
793 l.Add(fGraphs[slot]);
794 }
795 resGraph->Merge(&l);
796 }
797
798 // Helper functions for RMergeableValue
799 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
800 {
801 return std::make_unique<RMergeableFill<Result_t>>(*fGraphs[0]);
802 }
803
804 std::string GetActionName() { return "Graph"; }
805
806 Result_t &PartialUpdate(unsigned int slot) { return *fGraphs[slot]; }
807
808 FillTGraphHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
809 {
810 auto &result = *static_cast<std::shared_ptr<TGraph> *>(newResult);
811 result->Set(0);
812 return FillTGraphHelper(result, fGraphs.size());
813 }
814};
815
816class R__CLING_PTRCHECK(off) FillTGraphAsymmErrorsHelper
817 : public ROOT::Detail::RDF::RActionImpl<FillTGraphAsymmErrorsHelper> {
818public:
819 using Result_t = ::TGraphAsymmErrors;
820
821private:
822 std::vector<::TGraphAsymmErrors *> fGraphAsymmErrors;
823
824public:
825 FillTGraphAsymmErrorsHelper(FillTGraphAsymmErrorsHelper &&) = default;
826 FillTGraphAsymmErrorsHelper(const FillTGraphAsymmErrorsHelper &) = delete;
827
828 FillTGraphAsymmErrorsHelper(const std::shared_ptr<::TGraphAsymmErrors> &g, const unsigned int nSlots)
829 : fGraphAsymmErrors(nSlots, nullptr)
830 {
831 fGraphAsymmErrors[0] = g.get();
832 // Initialize all other slots
833 for (unsigned int i = 1; i < nSlots; ++i) {
834 fGraphAsymmErrors[i] = new TGraphAsymmErrors(*fGraphAsymmErrors[0]);
835 }
836 }
837
838 void Initialize() {}
839 void InitTask(TTreeReader *, unsigned int) {}
840
841 // case: all types are container types
842 template <
843 typename X, typename Y, typename EXL, typename EXH, typename EYL, typename EYH,
844 std::enable_if_t<IsDataContainer<X>::value && IsDataContainer<Y>::value && IsDataContainer<EXL>::value &&
845 IsDataContainer<EXH>::value && IsDataContainer<EYL>::value && IsDataContainer<EYH>::value,
846 int> = 0>
847 void
848 Exec(unsigned int slot, const X &xs, const Y &ys, const EXL &exls, const EXH &exhs, const EYL &eyls, const EYH &eyhs)
849 {
850 if ((xs.size() != ys.size()) || (xs.size() != exls.size()) || (xs.size() != exhs.size()) ||
851 (xs.size() != eyls.size()) || (xs.size() != eyhs.size())) {
852 throw std::runtime_error("Cannot fill GraphAsymmErrors with values in containers of different sizes.");
853 }
854 auto *thisSlotG = fGraphAsymmErrors[slot];
855 auto xsIt = std::begin(xs);
856 auto ysIt = std::begin(ys);
857 auto exlsIt = std::begin(exls);
858 auto exhsIt = std::begin(exhs);
859 auto eylsIt = std::begin(eyls);
860 auto eyhsIt = std::begin(eyhs);
861 while (xsIt != std::end(xs)) {
862 const auto n = thisSlotG->GetN(); // must use the same `n` for SetPoint and SetPointError
863 thisSlotG->SetPoint(n, *xsIt++, *ysIt++);
864 thisSlotG->SetPointError(n, *exlsIt++, *exhsIt++, *eylsIt++, *eyhsIt++);
865 }
866 }
867
868 // case: all types are non-container types, e.g. scalars
869 template <
870 typename X, typename Y, typename EXL, typename EXH, typename EYL, typename EYH,
871 std::enable_if_t<!IsDataContainer<X>::value && !IsDataContainer<Y>::value && !IsDataContainer<EXL>::value &&
872 !IsDataContainer<EXH>::value && !IsDataContainer<EYL>::value && !IsDataContainer<EYH>::value,
873 int> = 0>
874 void Exec(unsigned int slot, X x, Y y, EXL exl, EXH exh, EYL eyl, EYH eyh)
875 {
876 auto thisSlotG = fGraphAsymmErrors[slot];
877 const auto n = thisSlotG->GetN();
878 thisSlotG->SetPoint(n, x, y);
879 thisSlotG->SetPointError(n, exl, exh, eyl, eyh);
880 }
881
882 // case: types are combination of containers and non-containers
883 // this is not supported, error out
884 template <typename X, typename Y, typename EXL, typename EXH, typename EYL, typename EYH,
885 typename... ExtraArgsToLowerPriority>
886 void Exec(unsigned int, X, Y, EXL, EXH, EYL, EYH, ExtraArgsToLowerPriority...)
887 {
888 throw std::runtime_error(
889 "GraphAsymmErrors was applied to a mix of scalar values and collections. This is not supported.");
890 }
891
892 void Finalize()
893 {
894 const auto nSlots = fGraphAsymmErrors.size();
895 auto resGraphAsymmErrors = fGraphAsymmErrors[0];
896 TList l;
897 l.SetOwner(); // The list will free the memory associated to its elements upon destruction
898 for (unsigned int slot = 1; slot < nSlots; ++slot) {
899 l.Add(fGraphAsymmErrors[slot]);
900 }
901 resGraphAsymmErrors->Merge(&l);
902 }
903
904 // Helper functions for RMergeableValue
905 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
906 {
907 return std::make_unique<RMergeableFill<Result_t>>(*fGraphAsymmErrors[0]);
908 }
909
910 std::string GetActionName() { return "GraphAsymmErrors"; }
911
912 Result_t &PartialUpdate(unsigned int slot) { return *fGraphAsymmErrors[slot]; }
913
914 FillTGraphAsymmErrorsHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
915 {
916 auto &result = *static_cast<std::shared_ptr<TGraphAsymmErrors> *>(newResult);
917 result->Set(0);
918 return FillTGraphAsymmErrorsHelper(result, fGraphAsymmErrors.size());
919 }
920};
921
922/// A FillHelper for classes supporting the FillThreadSafe function.
923template <typename HIST>
924class R__CLING_PTRCHECK(off) ThreadSafeFillHelper : public RActionImpl<ThreadSafeFillHelper<HIST>> {
925 std::vector<std::shared_ptr<HIST>> fObjects;
926 std::vector<std::unique_ptr<std::mutex>> fMutexPtrs;
927
928 // This overload matches if the function exists:
929 template <typename T, typename... Args>
930 auto TryCallFillThreadSafe(T &object, std::mutex &, int /*dummy*/, Args... args)
931 -> decltype(ROOT::Internal::FillThreadSafe(object, args...), void())
932 {
933 ROOT::Internal::FillThreadSafe(object, args...);
934 }
935 // This one has lower precedence because of the dummy argument, and uses a lock
936 template <typename T, typename... Args>
937 auto TryCallFillThreadSafe(T &object, std::mutex &mutex, char /*dummy*/, Args... args)
938 {
939 std::scoped_lock lock{mutex};
940 object.Fill(args...);
941 }
942
943 template <std::size_t ColIdx, typename End_t, typename... Its>
944 void ExecLoop(unsigned int slot, End_t end, Its... its)
945 {
946 const auto localSlot = slot % fObjects.size();
947 for (; GetNthElement<ColIdx>(its...) != end; (std::advance(its, 1), ...)) {
948 TryCallFillThreadSafe(*fObjects[localSlot], *fMutexPtrs[localSlot], 0, *its...);
949 }
950 }
951
952public:
953 ThreadSafeFillHelper(ThreadSafeFillHelper &&) = default;
954 ThreadSafeFillHelper(const ThreadSafeFillHelper &) = delete;
955
956 ThreadSafeFillHelper(const std::shared_ptr<HIST> &h, const unsigned int nSlots)
957 {
958 fObjects.resize(nSlots);
959 fObjects.front() = h;
960
961 std::generate(fObjects.begin() + 1, fObjects.end(), [h]() {
962 auto hist = std::make_shared<HIST>(*h);
963 UnsetDirectoryIfPossible(hist.get());
964 return hist;
965 });
966 fMutexPtrs.resize(nSlots);
967 std::generate(fMutexPtrs.begin(), fMutexPtrs.end(), []() { return std::make_unique<std::mutex>(); });
968 }
969
970 void InitTask(TTreeReader *, unsigned int) {}
971
972 // no container arguments
973 template <typename... ValTypes, std::enable_if_t<!std::disjunction<IsDataContainer<ValTypes>...>::value, int> = 0>
974 void Exec(unsigned int slot, const ValTypes &...x)
975 {
976 const auto localSlot = slot % fObjects.size();
977 TryCallFillThreadSafe(*fObjects[localSlot], *fMutexPtrs[localSlot], 0, x...);
978 }
979
980 // at least one container argument
981 template <typename... Xs, std::enable_if_t<std::disjunction<IsDataContainer<Xs>...>::value, int> = 0>
982 void Exec(unsigned int slot, const Xs &...xs)
983 {
984 // array of bools keeping track of which inputs are containers
985 constexpr std::array<bool, sizeof...(Xs)> isContainer{IsDataContainer<Xs>::value...};
986
987 // index of the first container input
988 constexpr std::size_t colidx = FindIdxTrue(isContainer);
989 // if this happens, there is a bug in the implementation
990 static_assert(colidx < sizeof...(Xs), "Error: index of collection-type argument not found.");
991
992 // get the end iterator to the first container
993 auto const xrefend = std::end(GetNthElement<colidx>(xs...));
994
995 // array of container sizes (1 for scalars)
996 std::array<std::size_t, sizeof...(xs)> sizes = {{GetSize(xs)...}};
997
998 for (std::size_t i = 0; i < sizeof...(xs); ++i) {
999 if (isContainer[i] && sizes[i] != sizes[colidx]) {
1000 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
1001 }
1002 }
1003
1004 ExecLoop<colidx>(slot, xrefend, MakeBegin(xs)...);
1005 }
1006
1007 template <typename T = HIST>
1008 void Exec(...)
1009 {
1010 static_assert(sizeof(T) < 0,
1011 "When filling an object with RDataFrame (e.g. via a Fill action) the number or types of the "
1012 "columns passed did not match the signature of the object's `FillThreadSafe` method.");
1013 }
1014
1015 void Initialize() { /* noop */ }
1016
1017 void Finalize()
1018 {
1019 if (fObjects.size() > 1) {
1020 TList list;
1021 for (auto it = fObjects.cbegin() + 1; it != fObjects.end(); ++it) {
1022 list.Add(it->get());
1023 }
1024 fObjects[0]->Merge(&list);
1025 }
1026
1027 fObjects.resize(1);
1028 fMutexPtrs.clear();
1029 }
1030
1031 // Helper function for RMergeableValue
1032 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1033 {
1034 return std::make_unique<RMergeableFill<HIST>>(*fObjects[0]);
1035 }
1036
1037 // if the fObjects vector type is derived from TObject, return the name of the object
1038 template <typename T = HIST, std::enable_if_t<std::is_base_of<TObject, T>::value, int> = 0>
1039 std::string GetActionName()
1040 {
1041 return std::string(fObjects[0]->IsA()->GetName()) + "\\n" + std::string(fObjects[0]->GetName());
1042 }
1043
1044 template <typename H = HIST>
1045 ThreadSafeFillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1046 {
1047 auto &result = *static_cast<std::shared_ptr<H> *>(newResult);
1048 ResetIfPossible(result.get());
1050 return ThreadSafeFillHelper(result, fObjects.size());
1051 }
1052};
1053
1054// In case of the take helper we have 4 cases:
1055// 1. The column is not an RVec, the collection is not a vector
1056// 2. The column is not an RVec, the collection is a vector
1057// 3. The column is an RVec, the collection is not a vector
1058// 4. The column is an RVec, the collection is a vector
1059
1060template <typename V, typename COLL>
1061void FillColl(V&& v, COLL& c) {
1062 c.emplace_back(v);
1063}
1064
1065// Use push_back for bool since some compilers do not support emplace_back.
1066template <typename COLL>
1067void FillColl(bool v, COLL& c) {
1068 c.push_back(v);
1069}
1070
1071// Case 1.: The column is not an RVec, the collection is not a vector
1072// No optimisations, no transformations: just copies.
1073template <typename RealT_t, typename T, typename COLL>
1074class R__CLING_PTRCHECK(off) TakeHelper : public RActionImpl<TakeHelper<RealT_t, T, COLL>> {
1075 Results<std::shared_ptr<COLL>> fColls;
1076
1077public:
1078 using ColumnTypes_t = TypeList<T>;
1079 TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
1080 {
1081 fColls.emplace_back(resultColl);
1082 for (unsigned int i = 1; i < nSlots; ++i)
1083 fColls.emplace_back(std::make_shared<COLL>());
1084 }
1085 TakeHelper(TakeHelper &&);
1086 TakeHelper(const TakeHelper &) = delete;
1087
1088 void InitTask(TTreeReader *, unsigned int) {}
1089
1090 void Exec(unsigned int slot, T &v) { FillColl(v, *fColls[slot]); }
1091
1092 void Initialize() { /* noop */}
1093
1094 void Finalize()
1095 {
1096 auto rColl = fColls[0];
1097 for (unsigned int i = 1; i < fColls.size(); ++i) {
1098 const auto &coll = fColls[i];
1099 const auto end = coll->end();
1100 // Use an explicit loop here to prevent compiler warnings introduced by
1101 // clang's range-based loop analysis and vector<bool> references.
1102 for (auto j = coll->begin(); j != end; j++) {
1103 FillColl(*j, *rColl);
1104 }
1105 }
1106 }
1107
1108 COLL &PartialUpdate(unsigned int slot) { return *fColls[slot].get(); }
1109
1110 std::string GetActionName() { return "Take"; }
1111
1112 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1113 {
1114 auto &result = *static_cast<std::shared_ptr<COLL> *>(newResult);
1115 result->clear();
1116 return TakeHelper(result, fColls.size());
1117 }
1118};
1119
1120// Case 2.: The column is not an RVec, the collection is a vector
1121// Optimisations, no transformations: just copies.
1122template <typename RealT_t, typename T>
1123class R__CLING_PTRCHECK(off) TakeHelper<RealT_t, T, std::vector<T>>
1124 : public RActionImpl<TakeHelper<RealT_t, T, std::vector<T>>> {
1125 Results<std::shared_ptr<std::vector<T>>> fColls;
1126
1127public:
1128 using ColumnTypes_t = TypeList<T>;
1129 TakeHelper(const std::shared_ptr<std::vector<T>> &resultColl, const unsigned int nSlots)
1130 {
1131 fColls.emplace_back(resultColl);
1132 for (unsigned int i = 1; i < nSlots; ++i) {
1133 auto v = std::make_shared<std::vector<T>>();
1134 v->reserve(1024);
1135 fColls.emplace_back(v);
1136 }
1137 }
1138 TakeHelper(TakeHelper &&);
1139 TakeHelper(const TakeHelper &) = delete;
1140
1141 void InitTask(TTreeReader *, unsigned int) {}
1142
1143 void Exec(unsigned int slot, T &v) { FillColl(v, *fColls[slot]); }
1144
1145 void Initialize() { /* noop */}
1146
1147 // This is optimised to treat vectors
1148 void Finalize()
1149 {
1150 ULong64_t totSize = 0;
1151 for (auto &coll : fColls)
1152 totSize += coll->size();
1153 auto rColl = fColls[0];
1154 rColl->reserve(totSize);
1155 for (unsigned int i = 1; i < fColls.size(); ++i) {
1156 auto &coll = fColls[i];
1157 rColl->insert(rColl->end(), coll->begin(), coll->end());
1158 }
1159 }
1160
1161 std::vector<T> &PartialUpdate(unsigned int slot) { return *fColls[slot]; }
1162
1163 std::string GetActionName() { return "Take"; }
1164
1165 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1166 {
1167 auto &result = *static_cast<std::shared_ptr<std::vector<T>> *>(newResult);
1168 result->clear();
1169 return TakeHelper(result, fColls.size());
1170 }
1171};
1172
1173// Case 3.: The column is a RVec, the collection is not a vector
1174// No optimisations, transformations from RVecs to vectors
1175template <typename RealT_t, typename COLL>
1176class R__CLING_PTRCHECK(off) TakeHelper<RealT_t, RVec<RealT_t>, COLL>
1177 : public RActionImpl<TakeHelper<RealT_t, RVec<RealT_t>, COLL>> {
1178 Results<std::shared_ptr<COLL>> fColls;
1179
1180public:
1181 using ColumnTypes_t = TypeList<RVec<RealT_t>>;
1182 TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
1183 {
1184 fColls.emplace_back(resultColl);
1185 for (unsigned int i = 1; i < nSlots; ++i)
1186 fColls.emplace_back(std::make_shared<COLL>());
1187 }
1188 TakeHelper(TakeHelper &&);
1189 TakeHelper(const TakeHelper &) = delete;
1190
1191 void InitTask(TTreeReader *, unsigned int) {}
1192
1193 void Exec(unsigned int slot, RVec<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
1194
1195 void Initialize() { /* noop */}
1196
1197 void Finalize()
1198 {
1199 auto rColl = fColls[0];
1200 for (unsigned int i = 1; i < fColls.size(); ++i) {
1201 auto &coll = fColls[i];
1202 for (auto &v : *coll) {
1203 rColl->emplace_back(v);
1204 }
1205 }
1206 }
1207
1208 std::string GetActionName() { return "Take"; }
1209
1210 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1211 {
1212 auto &result = *static_cast<std::shared_ptr<COLL> *>(newResult);
1213 result->clear();
1214 return TakeHelper(result, fColls.size());
1215 }
1216};
1217
1218// Case 4.: The column is an RVec, the collection is a vector
1219// Optimisations, transformations from RVecs to vectors
1220template <typename RealT_t>
1221class R__CLING_PTRCHECK(off) TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>
1222 : public RActionImpl<TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>> {
1223
1224 Results<std::shared_ptr<std::vector<std::vector<RealT_t>>>> fColls;
1225
1226public:
1227 using ColumnTypes_t = TypeList<RVec<RealT_t>>;
1228 TakeHelper(const std::shared_ptr<std::vector<std::vector<RealT_t>>> &resultColl, const unsigned int nSlots)
1229 {
1230 fColls.emplace_back(resultColl);
1231 for (unsigned int i = 1; i < nSlots; ++i) {
1232 auto v = std::make_shared<std::vector<RealT_t>>();
1233 v->reserve(1024);
1234 fColls.emplace_back(v);
1235 }
1236 }
1237 TakeHelper(TakeHelper &&);
1238 TakeHelper(const TakeHelper &) = delete;
1239
1240 void InitTask(TTreeReader *, unsigned int) {}
1241
1242 void Exec(unsigned int slot, RVec<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
1243
1244 void Initialize() { /* noop */}
1245
1246 // This is optimised to treat vectors
1247 void Finalize()
1248 {
1249 ULong64_t totSize = 0;
1250 for (auto &coll : fColls)
1251 totSize += coll->size();
1252 auto rColl = fColls[0];
1253 rColl->reserve(totSize);
1254 for (unsigned int i = 1; i < fColls.size(); ++i) {
1255 auto &coll = fColls[i];
1256 rColl->insert(rColl->end(), coll->begin(), coll->end());
1257 }
1258 }
1259
1260 std::string GetActionName() { return "Take"; }
1261
1262 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1263 {
1264 auto &result = *static_cast<typename decltype(fColls)::value_type *>(newResult);
1265 result->clear();
1266 return TakeHelper(result, fColls.size());
1267 }
1268};
1269
1270// Extern templates for TakeHelper
1271// NOTE: The move-constructor of specializations declared as extern templates
1272// must be defined out of line, otherwise cling fails to find its symbol.
1273template <typename RealT_t, typename T, typename COLL>
1274TakeHelper<RealT_t, T, COLL>::TakeHelper(TakeHelper<RealT_t, T, COLL> &&) = default;
1275template <typename RealT_t, typename T>
1276TakeHelper<RealT_t, T, std::vector<T>>::TakeHelper(TakeHelper<RealT_t, T, std::vector<T>> &&) = default;
1277template <typename RealT_t, typename COLL>
1278TakeHelper<RealT_t, RVec<RealT_t>, COLL>::TakeHelper(TakeHelper<RealT_t, RVec<RealT_t>, COLL> &&) = default;
1279template <typename RealT_t>
1280TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>::TakeHelper(TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>> &&) = default;
1281
1282// External templates are disabled for gcc5 since this version wrongly omits the C++11 ABI attribute
1283#if __GNUC__ > 5
1284extern template class TakeHelper<bool, bool, std::vector<bool>>;
1285extern template class TakeHelper<unsigned int, unsigned int, std::vector<unsigned int>>;
1286extern template class TakeHelper<unsigned long, unsigned long, std::vector<unsigned long>>;
1287extern template class TakeHelper<unsigned long long, unsigned long long, std::vector<unsigned long long>>;
1288extern template class TakeHelper<int, int, std::vector<int>>;
1289extern template class TakeHelper<long, long, std::vector<long>>;
1290extern template class TakeHelper<long long, long long, std::vector<long long>>;
1291extern template class TakeHelper<float, float, std::vector<float>>;
1292extern template class TakeHelper<double, double, std::vector<double>>;
1293#endif
1294
1295template <typename ResultType>
1296class R__CLING_PTRCHECK(off) MinHelper : public RActionImpl<MinHelper<ResultType>> {
1297 std::shared_ptr<ResultType> fResultMin;
1298 Results<ResultType> fMins;
1299
1300public:
1301 MinHelper(MinHelper &&) = default;
1302 MinHelper(const std::shared_ptr<ResultType> &minVPtr, const unsigned int nSlots)
1303 : fResultMin(minVPtr), fMins(nSlots, std::numeric_limits<ResultType>::max())
1304 {
1305 }
1306
1307 void Exec(unsigned int slot, ResultType v) { fMins[slot] = std::min(v, fMins[slot]); }
1308
1309 void InitTask(TTreeReader *, unsigned int) {}
1310
1311 template <typename T, std::enable_if_t<IsDataContainer<T>::value, int> = 0>
1312 void Exec(unsigned int slot, const T &vs)
1313 {
1314 for (auto &&v : vs)
1315 fMins[slot] = std::min(static_cast<ResultType>(v), fMins[slot]);
1316 }
1317
1318 void Initialize() { /* noop */}
1319
1320 void Finalize()
1321 {
1322 *fResultMin = std::numeric_limits<ResultType>::max();
1323 for (auto &m : fMins)
1324 *fResultMin = std::min(m, *fResultMin);
1325 }
1326
1327 // Helper functions for RMergeableValue
1328 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1329 {
1330 return std::make_unique<RMergeableMin<ResultType>>(*fResultMin);
1331 }
1332
1333 ResultType &PartialUpdate(unsigned int slot) { return fMins[slot]; }
1334
1335 std::string GetActionName() { return "Min"; }
1336
1337 MinHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1338 {
1339 auto &result = *static_cast<std::shared_ptr<ResultType> *>(newResult);
1340 return MinHelper(result, fMins.size());
1341 }
1342};
1343
1344template <typename ResultType>
1345class R__CLING_PTRCHECK(off) MaxHelper : public RActionImpl<MaxHelper<ResultType>> {
1346 std::shared_ptr<ResultType> fResultMax;
1347 Results<ResultType> fMaxs;
1348
1349public:
1350 MaxHelper(MaxHelper &&) = default;
1351 MaxHelper(const MaxHelper &) = delete;
1352 MaxHelper(const std::shared_ptr<ResultType> &maxVPtr, const unsigned int nSlots)
1353 : fResultMax(maxVPtr), fMaxs(nSlots, std::numeric_limits<ResultType>::lowest())
1354 {
1355 }
1356
1357 void InitTask(TTreeReader *, unsigned int) {}
1358 void Exec(unsigned int slot, ResultType v) { fMaxs[slot] = std::max(v, fMaxs[slot]); }
1359
1360 template <typename T, std::enable_if_t<IsDataContainer<T>::value, int> = 0>
1361 void Exec(unsigned int slot, const T &vs)
1362 {
1363 for (auto &&v : vs)
1364 fMaxs[slot] = std::max(static_cast<ResultType>(v), fMaxs[slot]);
1365 }
1366
1367 void Initialize() { /* noop */}
1368
1369 void Finalize()
1370 {
1371 *fResultMax = std::numeric_limits<ResultType>::lowest();
1372 for (auto &m : fMaxs) {
1373 *fResultMax = std::max(m, *fResultMax);
1374 }
1375 }
1376
1377 // Helper functions for RMergeableValue
1378 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1379 {
1380 return std::make_unique<RMergeableMax<ResultType>>(*fResultMax);
1381 }
1382
1383 ResultType &PartialUpdate(unsigned int slot) { return fMaxs[slot]; }
1384
1385 std::string GetActionName() { return "Max"; }
1386
1387 MaxHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1388 {
1389 auto &result = *static_cast<std::shared_ptr<ResultType> *>(newResult);
1390 return MaxHelper(result, fMaxs.size());
1391 }
1392};
1393
1394template <typename ResultType>
1395class R__CLING_PTRCHECK(off) SumHelper : public RActionImpl<SumHelper<ResultType>> {
1396 std::shared_ptr<ResultType> fResultSum;
1397 Results<ResultType> fSums;
1398 Results<ResultType> fCompensations;
1399
1400 /// Evaluate neutral element for this type and the sum operation.
1401 /// This is assumed to be any_value - any_value if operator- is defined
1402 /// for the type, otherwise a default-constructed ResultType{} is used.
1403 template <typename T = ResultType>
1404 auto NeutralElement(const T &v, int /*overloadresolver*/) -> decltype(v - v)
1405 {
1406 return v - v;
1407 }
1408
1409 template <typename T = ResultType, typename Dummy = int>
1410 ResultType NeutralElement(const T &, Dummy) // this overload has lower priority thanks to the template arg
1411 {
1412 return ResultType{};
1413 }
1414
1415public:
1416 SumHelper(SumHelper &&) = default;
1417 SumHelper(const SumHelper &) = delete;
1418 SumHelper(const std::shared_ptr<ResultType> &sumVPtr, const unsigned int nSlots)
1419 : fResultSum(sumVPtr), fSums(nSlots, NeutralElement(*sumVPtr, -1)),
1420 fCompensations(nSlots, NeutralElement(*sumVPtr, -1))
1421 {
1422 }
1423 void InitTask(TTreeReader *, unsigned int) {}
1424
1425 void Exec(unsigned int slot, ResultType x)
1426 {
1427 // Kahan Sum:
1428 ResultType y = x - fCompensations[slot];
1429 ResultType t = fSums[slot] + y;
1430 fCompensations[slot] = (t - fSums[slot]) - y;
1431 fSums[slot] = t;
1432 }
1433
1434 template <typename T, std::enable_if_t<IsDataContainer<T>::value, int> = 0>
1435 void Exec(unsigned int slot, const T &vs)
1436 {
1437 for (auto &&v : vs) {
1438 Exec(slot, v);
1439 }
1440 }
1441
1442 void Initialize() { /* noop */}
1443
1444 void Finalize()
1445 {
1446 ResultType sum(NeutralElement(ResultType{}, -1));
1447 ResultType compensation(NeutralElement(ResultType{}, -1));
1448 ResultType y(NeutralElement(ResultType{}, -1));
1449 ResultType t(NeutralElement(ResultType{}, -1));
1450 for (auto &m : fSums) {
1451 // Kahan Sum:
1452 y = m - compensation;
1453 t = sum + y;
1454 compensation = (t - sum) - y;
1455 sum = t;
1456 }
1457 *fResultSum += sum;
1458 }
1459
1460 // Helper functions for RMergeableValue
1461 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1462 {
1463 return std::make_unique<RMergeableSum<ResultType>>(*fResultSum);
1464 }
1465
1466 ResultType &PartialUpdate(unsigned int slot) { return fSums[slot]; }
1467
1468 std::string GetActionName() { return "Sum"; }
1469
1470 SumHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1471 {
1472 auto &result = *static_cast<std::shared_ptr<ResultType> *>(newResult);
1473 *result = NeutralElement(*result, -1);
1474 return SumHelper(result, fSums.size());
1475 }
1476};
1477
1478class R__CLING_PTRCHECK(off) MeanHelper : public RActionImpl<MeanHelper> {
1479 std::shared_ptr<double> fResultMean;
1480 std::vector<ULong64_t> fCounts;
1481 std::vector<double> fSums;
1482 std::vector<double> fPartialMeans;
1483 std::vector<double> fCompensations;
1484
1485public:
1486 MeanHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
1487 MeanHelper(MeanHelper &&) = default;
1488 MeanHelper(const MeanHelper &) = delete;
1489 void InitTask(TTreeReader *, unsigned int) {}
1490 void Exec(unsigned int slot, double v);
1491
1492 template <typename T, std::enable_if_t<IsDataContainer<T>::value, int> = 0>
1493 void Exec(unsigned int slot, const T &vs)
1494 {
1495 for (auto &&v : vs) {
1496
1497 fCounts[slot]++;
1498 // Kahan Sum:
1499 double y = v - fCompensations[slot];
1500 double t = fSums[slot] + y;
1501 fCompensations[slot] = (t - fSums[slot]) - y;
1502 fSums[slot] = t;
1503 }
1504 }
1505
1506 void Initialize() { /* noop */}
1507
1508 void Finalize();
1509
1510 // Helper functions for RMergeableValue
1511 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1512 {
1513 const ULong64_t counts = std::accumulate(fCounts.begin(), fCounts.end(), 0ull);
1514 return std::make_unique<RMergeableMean>(*fResultMean, counts);
1515 }
1516
1517 double &PartialUpdate(unsigned int slot);
1518
1519 std::string GetActionName() { return "Mean"; }
1520
1521 MeanHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1522 {
1523 auto &result = *static_cast<std::shared_ptr<double> *>(newResult);
1524 return MeanHelper(result, fSums.size());
1525 }
1526};
1527
1528class R__CLING_PTRCHECK(off) StdDevHelper : public RActionImpl<StdDevHelper> {
1529 // Number of subsets of data
1530 unsigned int fNSlots;
1531 std::shared_ptr<double> fResultStdDev;
1532 // Number of element for each slot
1533 std::vector<ULong64_t> fCounts;
1534 // Mean of each slot
1535 std::vector<double> fMeans;
1536 // Squared distance from the mean
1537 std::vector<double> fDistancesfromMean;
1538
1539public:
1540 StdDevHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
1541 StdDevHelper(StdDevHelper &&) = default;
1542 StdDevHelper(const StdDevHelper &) = delete;
1543 void InitTask(TTreeReader *, unsigned int) {}
1544 void Exec(unsigned int slot, double v);
1545
1546 template <typename T, std::enable_if_t<IsDataContainer<T>::value, int> = 0>
1547 void Exec(unsigned int slot, const T &vs)
1548 {
1549 for (auto &&v : vs) {
1550 Exec(slot, v);
1551 }
1552 }
1553
1554 void Initialize() { /* noop */}
1555
1556 void Finalize();
1557
1558 // Helper functions for RMergeableValue
1559 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1560 {
1561 const ULong64_t counts = std::accumulate(fCounts.begin(), fCounts.end(), 0ull);
1562 const Double_t mean =
1563 std::inner_product(fMeans.begin(), fMeans.end(), fCounts.begin(), 0.) / static_cast<Double_t>(counts);
1564 return std::make_unique<RMergeableStdDev>(*fResultStdDev, counts, mean);
1565 }
1566
1567 std::string GetActionName() { return "StdDev"; }
1568
1569 StdDevHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1570 {
1571 auto &result = *static_cast<std::shared_ptr<double> *>(newResult);
1572 return StdDevHelper(result, fCounts.size());
1573 }
1574};
1575
1576class R__CLING_PTRCHECK(off) MedianHelper : public RActionImpl<MedianHelper> {
1577 std::shared_ptr<double> fResult;
1578 std::vector<std::vector<double>> fBuffers;
1579
1580public:
1581 MedianHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
1582 MedianHelper(MedianHelper &&) = default;
1583 MedianHelper &operator=(MedianHelper &&) = default;
1584 MedianHelper(const MedianHelper &) = delete;
1585 MedianHelper &operator=(const MedianHelper &other) = delete;
1586
1587 void InitTask(TTreeReader *, unsigned int) {}
1588 void Exec(unsigned int slot, double v);
1589
1590 template <typename T, std::enable_if_t<IsDataContainer<T>::value, int> = 0>
1591 void Exec(unsigned int slot, const T &vs)
1592 {
1593 fBuffers[slot].insert(fBuffers[slot].end(), std::begin(vs), std::end(vs));
1594 }
1595
1596 void Initialize() { /* noop */ }
1597
1598 void Finalize();
1599
1600 std::string GetActionName() { return "Median"; }
1601
1602 MedianHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1603 {
1604 auto &result = *static_cast<std::shared_ptr<double> *>(newResult);
1605 return MedianHelper(result, fBuffers.size());
1606 }
1607
1608 ~MedianHelper() = default;
1609};
1610
1611template <typename PrevNodeType>
1612class R__CLING_PTRCHECK(off) DisplayHelper : public RActionImpl<DisplayHelper<PrevNodeType>> {
1613private:
1615 std::shared_ptr<Display_t> fDisplayerHelper;
1616 std::shared_ptr<PrevNodeType> fPrevNode;
1617 size_t fEntriesToProcess;
1618
1619public:
1620 DisplayHelper(size_t nRows, const std::shared_ptr<Display_t> &d, const std::shared_ptr<PrevNodeType> &prevNode)
1621 : fDisplayerHelper(d), fPrevNode(prevNode), fEntriesToProcess(nRows)
1622 {
1623 }
1624 DisplayHelper(DisplayHelper &&) = default;
1625 DisplayHelper(const DisplayHelper &) = delete;
1626 void InitTask(TTreeReader *, unsigned int) {}
1627
1628 template <typename... Columns>
1629 void Exec(unsigned int, Columns &... columns)
1630 {
1631 if (fEntriesToProcess == 0)
1632 return;
1633
1634 fDisplayerHelper->AddRow(columns...);
1635 --fEntriesToProcess;
1636
1637 if (fEntriesToProcess == 0) {
1638 // No more entries to process. Send a one-time signal that this node
1639 // of the graph is done. It is important that the 'StopProcessing'
1640 // method is only called once from this helper, otherwise it would seem
1641 // like more than one operation has completed its work.
1642 fPrevNode->StopProcessing();
1643 }
1644 }
1645
1646 void Initialize() {}
1647
1648 void Finalize() {}
1649
1650 std::string GetActionName() { return "Display"; }
1651};
1652
1653template <typename Acc, typename Merge, typename R, typename T, typename U,
1654 bool MustCopyAssign = std::is_same<R, U>::value>
1655class R__CLING_PTRCHECK(off) AggregateHelper
1656 : public RActionImpl<AggregateHelper<Acc, Merge, R, T, U, MustCopyAssign>> {
1657 Acc fAggregate;
1658 Merge fMerge;
1659 std::shared_ptr<U> fResult;
1660 Results<U> fAggregators;
1661
1662public:
1663 using ColumnTypes_t = TypeList<T>;
1664
1665 AggregateHelper(Acc &&f, Merge &&m, const std::shared_ptr<U> &result, const unsigned int nSlots)
1666 : fAggregate(std::move(f)), fMerge(std::move(m)), fResult(result), fAggregators(nSlots, *result)
1667 {
1668 }
1669
1670 AggregateHelper(Acc &f, Merge &m, const std::shared_ptr<U> &result, const unsigned int nSlots)
1671 : fAggregate(f), fMerge(m), fResult(result), fAggregators(nSlots, *result)
1672 {
1673 }
1674
1675 AggregateHelper(AggregateHelper &&) = default;
1676 AggregateHelper(const AggregateHelper &) = delete;
1677
1678 void InitTask(TTreeReader *, unsigned int) {}
1679
1680 template <bool MustCopyAssign_ = MustCopyAssign, std::enable_if_t<MustCopyAssign_, int> = 0>
1681 void Exec(unsigned int slot, const T &value)
1682 {
1683 fAggregators[slot] = fAggregate(fAggregators[slot], value);
1684 }
1685
1686 template <bool MustCopyAssign_ = MustCopyAssign, std::enable_if_t<!MustCopyAssign_, int> = 0>
1687 void Exec(unsigned int slot, const T &value)
1688 {
1689 fAggregate(fAggregators[slot], value);
1690 }
1691
1692 void Initialize() { /* noop */}
1693
1694 template <typename MergeRet = typename CallableTraits<Merge>::ret_type,
1695 bool MergeAll = std::is_same<void, MergeRet>::value>
1696 std::enable_if_t<MergeAll, void> Finalize()
1697 {
1698 fMerge(fAggregators);
1699 *fResult = fAggregators[0];
1700 }
1701
1702 template <typename MergeRet = typename CallableTraits<Merge>::ret_type,
1703 bool MergeTwoByTwo = std::is_same<U, MergeRet>::value>
1704 std::enable_if_t<MergeTwoByTwo, void> Finalize(...) // ... needed to let compiler distinguish overloads
1705 {
1706 for (const auto &acc : fAggregators)
1707 *fResult = fMerge(*fResult, acc);
1708 }
1709
1710 U &PartialUpdate(unsigned int slot) { return fAggregators[slot]; }
1711
1712 std::string GetActionName() { return "Aggregate"; }
1713
1714 AggregateHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1715 {
1716 auto &result = *static_cast<std::shared_ptr<U> *>(newResult);
1717 return AggregateHelper(fAggregate, fMerge, result, fAggregators.size());
1718 }
1719};
1720
1721} // end of NS RDF
1722} // end of NS Internal
1723} // end of NS ROOT
1724
1725/// \endcond
1726
1727#endif
PyObject * fCallable
Handle_t Display_t
Display handle.
Definition GuiTypes.h:27
#define d(i)
Definition RSha256.hxx:102
#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
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Basic types used by ROOT and required by TInterpreter.
double Double_t
Double 8 bytes.
Definition RtypesCore.h:74
unsigned long long ULong64_t
Portable unsigned long integer 8 bytes.
Definition RtypesCore.h:85
#define X(type, name)
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char x1
TTime operator*(const TTime &t1, const TTime &t2)
Definition TTime.h:85
Base class for action helpers, see RInterface::Book() for more information.
A histogram data structure to bin data along multiple dimensions.
A histogram for aggregation of data along multiple dimensions.
Definition RHist.hxx:66
A profile histogram, computing statistical quantities of an additional variable per bin.
Definition RProfile.hxx:58
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:1509
Collection abstract base class.
Definition TCollection.h:65
TGraph with asymmetric error bars.
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:926
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:109
A doubly linked list.
Definition TList.h:38
void Add(TObject *obj) override
Definition TList.h:81
Statistical variable, defined by its mean and variance (RMS).
Definition TStatistic.h:33
A simple, robust and fast interface to read values from ROOT columnar datasets such as TTree,...
Definition TTreeReader.h:46
RooCmdArg Columns(Int_t ncol)
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
std::unique_ptr< RMergeableVariations< T > > GetMergeableValue(ROOT::RDF::Experimental::RResultMap< T > &rmap)
Retrieve mergeable values after calling ROOT::RDF::VariationsFor .
void ResetIfPossible(TStatistic *h)
constexpr std::size_t FindIdxTrue(const T &arr)
Definition Utils.hxx:237
void UnsetDirectoryIfPossible(TH1 *h)
auto FillThreadSafe(T &histo, Args... args) -> decltype(histo.FillThreadSafe(args...), void())
Entrypoint for thread-safe filling from RDataFrame.
Definition TH3.h:39
ROOT type_traits extensions.
void Initialize(Bool_t useTMVAStyle=kTRUE)
Definition tmvaglob.cxx:176
CPYRT_EXTERN bool Exec(const std::string &cmd)
Definition API.cxx:447
std::string GetName(TCppScope_t)
A weight for filling histograms.
Definition RWeight.hxx:17
Lightweight storage for a collection of types.
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2335