Implement a custom action to fill THns.
This tutorial shows how to implement a custom action. As an example, we build a helper for filling THns.
Author: Enrico Guiraud, Danilo Piparo (CERN)
This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Monday, March 27, 2023 at 09:46 AM.
This is a custom action which respects a well defined interface. It supports parallelism, in the sense that it behaves correctly if implicit multi threading is enabled. We template it on:
%%cpp -d
HnT(s)
// Note the plural: in presence of a MT execution, internally more than a single THnT is created.
template <typename T, unsigned int NDIM>
class THnHelper : public ROOT::Detail::RDF::RActionImpl<THnHelper<T, NDIM>> {
public:
/// This is a handy, expressive shortcut.
using THn_t = THnT<T>;
/// This type is a requirement for every helper.
using Result_t = THn_t;
private:
std::vector<std::shared_ptr<THn_t>> fHistos; // one per data processing slot
public:
/// This constructor takes all the parameters necessary to build the THnTs. In addition, it requires the names of
/// the columns which will be used.
THnHelper(std::string_view name, std::string_view title, std::array<int, NDIM> nbins, std::array<double, NDIM> xmins,
std::array<double, NDIM> xmax)
{
const auto nSlots = ROOT::IsImplicitMTEnabled() ? ROOT::GetThreadPoolSize() : 1;
for (auto i : ROOT::TSeqU(nSlots)) {
fHistos.emplace_back(std::make_shared<THn_t>(std::string(name).c_str(), std::string(title).c_str(),
NDIM, nbins.data(), xmins.data(), xmax.data()));
(void)i;
}
}
THnHelper(THnHelper &&) = default;
THnHelper(const THnHelper &) = delete;
std::shared_ptr<THn_t> GetResultPtr() const { return fHistos[0]; }
void Initialize() {}
void InitTask(TTreeReader *, unsigned int) {}
/// This is a method executed at every entry
template <typename... ColumnTypes>
void Exec(unsigned int slot, ColumnTypes... values)
{
// Since THnT<T>::Fill expects a double*, we build it passing through a std::array.
std::array<double, sizeof...(ColumnTypes)> valuesArr{static_cast<double>(values)...};
fHistos[slot]->Fill(valuesArr.data());
}
/// This method is called at the end of the event loop. It is used to merge all the internal THnTs which
/// were used in each of the data processing slots.
void Finalize()
{
auto &res = fHistos[0];
for (auto slot : ROOT::TSeqU(1, fHistos.size())) {
res->Add(fHistos[slot].get());
}
}
std::string GetActionName(){
return "THnHelper";
}
};
input_line_43:1:1: error: unknown type name 'HnT' HnT(s) ^ input_line_43:1:7: error: expected ';' after top level declarator HnT(s) ^ ;
We enable implicit parallelism
ROOT::EnableImplicitMT();
We create an empty RDataFrame which contains 4 columns filled with random numbers. The type of the numbers held by the columns are: double, double, float, int.
ROOT::RDataFrame d(128);
auto genD = []() { return gRandom->Uniform(-5, 5); };
auto genF = [&genD]() { return (float)genD(); };
auto genI = [&genD]() { return (int)genD(); };
auto dd = d.Define("x0", genD).Define("x1", genD).Define("x2", genF).Define("x3", genI);
Our Helper type: templated on the internal THnT type, the size, the types of the columns we'll use to fill.
using Helper_t = THnHelper<float, 4>;
Helper_t helper{"myThN", // Name
"A THn with 4 dimensions", // Title
{4, 4, 8, 2}, // NBins
{-10., -10, -4., -6.}, // Axes min values
{10., 10, 5., 7.}}; // Axes max values
input_line_54:1:18: error: no template named 'THnHelper' using Helper_t = THnHelper<float, 4>;void __cling_Un1Qu321(void* vpClingValue) { ^ input_line_54:4:1: error: unknown type name 'Helper_t' Helper_t helper{"myThN", // Name ^
We book the action: it will be treated during the event loop.
auto myTHnT = dd.Book<double, double, float, int>(std::move(helper), {"x0", "x1", "x2", "x3"});
myTHnT->Print();
input_line_55:2:2: error: Syntax error auto myTHnT = dd.Book<double, double, float, int>(std::move(helper), {"x0", "x1", "x2", "x3"}); ^ FunctionDecl 0x7fcbf7ec1078 <input_line_55:1:1, line:6:1> line:1:6 __cling_Un1Qu322 'void (void *)' |-ParmVarDecl 0x7fcbf7ec0fc0 <col:23, col:29> col:29 vpClingValue 'void *' |-CompoundStmt 0x7fcbf7ec1710 <col:43, line:6:1> | |-DeclStmt 0x7fcbf7ec1648 <line:2:2, col:96> | | `-VarDecl 0x7fcbf7ec1180 <col:2, col:95> col:7 used myTHnT 'auto' cinit | | `-CallExpr 0x7fcbf7ec1618 <col:16, col:95> '<dependent type>' | | |-UnresolvedMemberExpr 0x7fcbf7ec1268 <col:16, col:50> '<bound member function type>' lvalue | | | `-DeclRefExpr 0x7fcbf7ec11e8 <col:16> 'ROOT::RDF::RInterface<ROOT::Detail::RDF::RLoopManager, void>':'ROOT::RDF::RInterface<ROOT::Detail::RDF::RLoopManager, void>' lvalue Var 0x7fcbf67b5ab0 'dd' 'ROOT::RDF::RInterface<ROOT::Detail::RDF::RLoopManager, void>':'ROOT::RDF::RInterface<ROOT::Detail::RDF::RLoopManager, void>' | | |-CallExpr 0x7fcbf7ec1510 <col:52, col:68> '<dependent type>' | | | |-UnresolvedLookupExpr 0x7fcbf7ec1370 <col:52, col:57> '<overloaded function type>' lvalue (no ADL) = 'move' 0x3341ab8 0x2d6cfd0 0x3be21b8 0x3e9f680 0x3e81e20 | | | `-DeclRefExpr 0x7fcbf7ec14d0 <col:62> '<dependent type>' lvalue Var 0x7fcbf7ec13e0 'helper' '<dependent type>' | | `-InitListExpr 0x7fcbf7ec15b8 <col:71, col:94> 'void' | | |-StringLiteral 0x7fcbf7ec1538 <col:72> 'const char [3]' lvalue "x0" | | |-StringLiteral 0x7fcbf7ec1558 <col:78> 'const char [3]' lvalue "x1" | | |-StringLiteral 0x7fcbf7ec1578 <col:84> 'const char [3]' lvalue "x2" | | `-StringLiteral 0x7fcbf7ec1598 <col:90> 'const char [3]' lvalue "x3" | |-CallExpr 0x7fcbf7ec16e8 <line:4:1, col:15> '<dependent type>' | | `-CXXDependentScopeMemberExpr 0x7fcbf7ec16a0 <col:1, col:9> '<dependent type>' lvalue ->Print | | `-DeclRefExpr 0x7fcbf7ec1660 <col:1> 'auto' lvalue Var 0x7fcbf7ec1180 'myTHnT' 'auto' | `-NullStmt 0x7fcbf7ec1708 <line:5:1> `-AnnotateAttr 0x7fcbf7ec1448 <<invalid sloc>> Implicit R"ATTRDUMP(__ResolveAtRuntime)ATTRDUMP" <<<NULL>>>
Draw all canvases
gROOT->GetListOfCanvases()->Draw()