Project
Loading...
Searching...
No Matches
RootTreeReader.h
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11#ifndef FRAMEWORK_ROOTTREEREADER_H
12#define FRAMEWORK_ROOTTREEREADER_H
13
18
20#include "Framework/Output.h"
24#include "Framework/Logger.h"
25#include "Headers/DataHeader.h"
26#include <TChain.h>
27#include <TTree.h>
28#include <TBranch.h>
29#include <TClass.h>
30#include <vector>
31#include <string>
32#include <stdexcept> // std::runtime_error
33#include <type_traits>
34#include <memory> // std::make_unique
35#include <functional> // std::function
36#include <utility> // std::forward
37
38namespace o2::framework
39{
40
41namespace rtr
42{
43// The class 'Output' has a unique header stack member which makes the class not
44// usable as key, in particular there is no copy constructor. Thats why a dedicated
45// structure is used which can be transformed into Output. Moreover it also handles the
46// conversion from OutputStack to Output
60} // namespace rtr
61
140template <typename KeyType>
142{
143 public:
145 using key_type = KeyType;
146 // The RootTreeWriter/Reader support storage of binary data as a vector of char.
147 // This allows to store e.g. raw data or some intermediate binary data alongside
148 // with ROOT objects like the MC labels.
149 using BinaryDataStoreType = std::vector<char>;
150
152 enum struct PublishingMode {
154 Single,
156 Loop,
157 };
158
169 std::function<bool(std::string_view name, ProcessingContext& context, Output const&, char* data)> hook;
170 };
171
172 // the key must not be of type const char* to make sure that the variable argument
173 // list of the constructor can be parsed
174 static_assert(std::is_same<KeyType, const char*>::value == false, "the key type must not be const char*");
175
178 ConstructorArg(key_type _key, const char* _name)
179 : key(_key), name(_name) {}
180 ConstructorArg(key_type _key, std::string const& _name)
181 : key(_key), name(_name) {}
183 std::string name;
184 };
185
186 // the vector of arguments is filled during the build up of the branch configuration and
187 // passed to the construction of the constructed mixin class
188 using ConstructorArgs = std::vector<ConstructorArg>;
189
199 {
200 public:
201 static const size_t STAGE = 0;
204 virtual ~BranchConfigurationInterface() = default;
205
207 virtual void setup(TTree&, SpecialPublishHook* h = nullptr) {}
213 virtual void exec(ProcessingContext& ctx, int entry, std::function<o2::header::Stack()> stackcreator) {}
214
215 private:
216 };
217
220 template <typename DataT, typename BASE>
222 {
223 public:
224 using PrevT = BASE;
225 using value_type = DataT;
227 static const size_t STAGE = BASE::STAGE + 1;
230 : PrevT(args), mKey(args[STAGE - 1].key), mName(args[STAGE - 1].name)
231 {
232 }
233 ~BranchConfigurationElement() override = default;
234
237 void exec(ProcessingContext& ctx, int entry, std::function<o2::header::Stack()> stackcreator) override
238 {
239 process(ctx, entry, stackcreator);
240 }
241
244 void setup(TTree& tree, SpecialPublishHook* publishhook = nullptr) override
245 {
246 setupInstance(tree, publishhook);
247 }
248
251 void setupInstance(TTree& tree, SpecialPublishHook* publishhook = nullptr)
252 {
253 // recursing through the tree structure by simply using method of the previous type,
254 // i.e. the base class method.
255 if constexpr (STAGE > 1) {
256 PrevT::setupInstance(tree, publishhook);
257 }
258 mPublishHook = publishhook;
259
260 // right now we allow the same key to appear for multiple branches
261 mBranch = tree.GetBranch(mName.c_str());
262 if (mBranch) {
263 std::string sizebranchName = std::string(mName) + "Size";
264 auto sizebranch = tree.GetBranch(sizebranchName.c_str());
265 auto* classinfo = TClass::GetClass(mBranch->GetClassName());
266 if constexpr (not std::is_void<value_type>::value) {
267 // check if the configured type matches the stored type
268 auto* storedclass = TClass::GetClass(typeid(value_type));
269 if (classinfo != storedclass) {
270 throw std::runtime_error(std::string("Configured type ") +
271 (storedclass != nullptr ? storedclass->GetName() : typeid(value_type).name()) +
272 " does not match the stored data type " +
273 (classinfo != nullptr ? classinfo->GetName() : "") +
274 " in branch " + mName);
275 }
276 }
277 if (!sizebranch) {
278 if (classinfo == nullptr) {
279 throw std::runtime_error(std::string("can not find class description for branch ") + mName);
280 }
281 LOG(info) << "branch set up: " << mName;
282 } else {
283 if (classinfo == nullptr || classinfo != TClass::GetClass(typeid(BinaryDataStoreType))) {
284 throw std::runtime_error("mismatching class type, expecting std::vector<char> for binary branch");
285 }
286 mSizeBranch = sizebranch;
287 LOG(info) << "binary branch set up: " << mName;
288 }
289 mClassInfo = classinfo;
290 } else {
291 throw std::runtime_error(std::string("can not find branch ") + mName);
292 }
293 }
294
296 void process(ProcessingContext& context, int entry, std::function<o2::header::Stack()>& stackcreator)
297 {
298 // recursing through the tree structure by simply using method of the previous type,
299 // i.e. the base class method.
300 if constexpr (STAGE > 1) {
301 PrevT::process(context, entry, stackcreator);
302 }
303
304 auto snapshot = [&context, &stackcreator](const KeyType& key, const auto& object) {
305 context.outputs().snapshot(Output{key.origin, key.description, key.subSpec, std::move(stackcreator())}, object);
306 };
307
308 // A tree can have no entry at all, which is what a timeframe without a single collision
309 // looks like. Publish a default-constructed object in that case, so that the consumers
310 // downstream still see the timeframe instead of getting nothing at all. Everything below
311 // stays the same, including a registered publishing hook, which needs a valid object.
312 char* data = nullptr;
313 if (entry >= 0) {
314 mBranch->SetAddress(&data);
315 mBranch->GetEntry(entry);
316 } else {
317 data = reinterpret_cast<char*>(mClassInfo->New());
318 if (data == nullptr) {
319 LOG(error) << "branch " << mName << ": cannot create an empty " << mClassInfo->GetName() << ", nothing published";
320 return;
321 }
322 }
323
324 // execute hook if it was registered; if this return true do not proceed further
325 if (mPublishHook != nullptr && (*mPublishHook).hook(mName, context, Output{mKey.origin, mKey.description, mKey.subSpec, std::move(stackcreator())}, data)) {
326
327 }
328 // try to figureout when we need to do something special
329 else {
330 if (mSizeBranch != nullptr) {
331 size_t datasize = 0;
332 if (entry >= 0) {
333 mSizeBranch->SetAddress(&datasize);
334 mSizeBranch->GetEntry(entry);
335 }
336 auto* buffer = reinterpret_cast<BinaryDataStoreType*>(data);
337 if (buffer->size() == datasize) {
338 LOG(debug) << "branch " << mName << ": publishing binary chunk of " << datasize << " bytes(s)";
339 snapshot(mKey, std::move(*buffer));
340 } else {
341 LOG(error) << "branch " << mName << ": inconsitent size of binary chunk "
342 << buffer->size() << " vs " << datasize;
344 snapshot(mKey, empty);
345 }
346 } else {
347 if constexpr (std::is_void<value_type>::value == true) {
348 // the default branch configuration publishes the object ROOT serialized
349 snapshot(mKey, std::move(ROOTSerializedByClass(*data, mClassInfo)));
350 } else {
351 // if type is specified in the branch configuration, the allocator API decides
352 // upon serialization
353 snapshot(mKey, *reinterpret_cast<value_type*>(data));
354 }
355 }
356 }
357 // cleanup the memory
358 auto* delfunc = mClassInfo->GetDelete();
359 if (delfunc) {
360 (*delfunc)(data);
361 }
362 if (entry >= 0) {
363 mBranch->DropBaskets("all");
364 }
365 }
366
367 private:
368 key_type mKey;
369 std::string mName;
370 TBranch* mBranch = nullptr;
371 TBranch* mSizeBranch = nullptr;
372 TClass* mClassInfo = nullptr;
373 SpecialPublishHook* mPublishHook = nullptr;
374 };
375
380 template <typename T>
382 using type = T;
383 template <typename U>
384 BranchDefinition(U _key, const char* _name)
385 : key(_key), name(_name)
386 {
387 }
388 template <typename U>
389 BranchDefinition(U _key, std::string const& _name)
390 : key(_key), name(_name)
391 {
392 }
393 template <typename U>
394 BranchDefinition(U _key, std::string&& _name)
395 : key(_key), name(std::move(_name))
396 {
397 }
398
400 std::string name;
401 };
402
405
410 template <typename... Args>
411 GenericRootTreeReader(const char* treename, // name of the tree to read from
412 Args&&... args) // file names, followed by branch info
413 : mInput(treename)
414 {
415 mInput.SetCacheSize(0);
416 parseConstructorArgs<0>(std::forward<Args>(args)...);
417 mBranchConfiguration->setup(mInput, mPublishHook);
418 }
419
421 void addFile(const char* fileName)
422 {
423 mInput.AddFile(fileName);
424 mNEntries = mInput.GetEntries();
425 }
426
429 bool next()
430 {
431 if (mNEntries == 0) {
432 // The tree has no entry at all. Publish one empty entry and stop, in every publishing
433 // mode: looping over nothing would never produce anything to publish.
434 if (mNofPublished >= 0) {
435 return false;
436 }
437 ++mNofPublished;
438 return true;
439 }
440 if ((mReadEntry + 1) >= mNEntries) {
441 if (mPublishingMode == PublishingMode::Single) {
442 // stop here
443 if (mReadEntry < mNEntries) {
444 mReadEntry = mNEntries;
445 }
446 return false;
447 }
448 // start over in loop mode
449 mReadEntry = -1;
450 }
451 if (mMaxEntries > 0 && (mNofPublished + 1) >= mMaxEntries) {
452 if (mReadEntry < mNEntries) {
453 mReadEntry = mNEntries;
454 }
455 return false;
456 }
457 ++mReadEntry;
458 ++mNofPublished;
459 return true;
460 }
461
464 {
465 next();
466 return *this;
467 }
469 self_type& operator++(int) = delete;
470
474
482 template <typename ContextType, typename... HeaderTypes>
483 bool operator()(ContextType& context,
484 HeaderTypes&&... headers) const
485 {
486 if (mNEntries == 0) {
487 if (mNofPublished != 0) { // next() has to have selected the one empty entry
488 return false;
489 }
490 } else if (mReadEntry >= mNEntries || (mMaxEntries > 0 && mNofPublished >= mMaxEntries)) {
491 return false;
492 }
493
494 auto stackcreator = [&headers...]() {
495 return o2::header::Stack{std::forward<HeaderTypes>(headers)...};
496 };
497
498 mBranchConfiguration->exec(context, mReadEntry, stackcreator);
499 return true;
500 }
501
503 int getCount() const
504 {
505 return mNofPublished + 1;
506 }
507
508 private:
509 // special helper to get the char argument from the argument pack
510 template <typename T, typename... Args>
511 const char* getCharArg(T arg, Args&&...)
512 {
513 static_assert(std::is_same<T, const char*>::value, "missing branch name after publishing key, use const char* argument right after the key");
514 return arg;
515 }
516
518 template <size_t skip, typename U, typename... Args>
519 void parseConstructorArgs(U key, Args&&... args)
520 {
521 // all argument parsing is done in the creation of the branch configuration
522 mBranchConfiguration = createBranchConfiguration<0, BranchConfigurationInterface>({}, std::forward<U>(key), std::forward<Args>(args)...);
523 }
524
528 template <size_t skip, typename BASE, typename U, typename... Args>
529 std::unique_ptr<BranchConfigurationInterface> createBranchConfiguration(ConstructorArgs&& cargs, U def, Args&&... args)
530 {
531 if constexpr (skip > 0) {
532 return createBranchConfiguration<skip - 1, BASE>(std::move(cargs), std::forward<Args>(args)...);
533 } else if constexpr (std::is_same<U, const char*>::value) {
534 addFile(def);
535 } else if constexpr (std::is_same<U, int>::value) {
536 mMaxEntries = def;
537 } else if constexpr (std::is_same<U, PublishingMode>::value) {
538 mPublishingMode = def;
539 } else if constexpr (std::is_same<U, SpecialPublishHook*>::value) {
540 mPublishHook = def;
541 } else if constexpr (is_specialization_v<U, BranchDefinition>) {
542 cargs.emplace_back(key_type(def.key), def.name);
543 using type = BranchConfigurationElement<typename U::type, BASE>;
544 return std::move(createBranchConfiguration<0, type>(std::move(cargs), std::forward<Args>(args)...));
545 } else if constexpr (sizeof...(Args) > 0) {
546 const char* arg = getCharArg(std::forward<Args>(args)...);
547 if (arg != nullptr && *arg != 0) {
548 cargs.emplace_back(key_type(def), arg);
549 using type = BranchConfigurationElement<void, BASE>;
550 return std::move(createBranchConfiguration<1, type>(std::move(cargs), std::forward<Args>(args)...));
551 }
552 throw std::runtime_error("expecting valid branch name string after key");
553 } else {
554 static_assert(always_static_assert<U>::value, "argument mismatch, define branches either as argument pairs of key and branchname or using the BranchDefinition helper struct");
555 }
556 return createBranchConfiguration<0, BASE>(std::move(cargs), std::forward<Args>(args)...);
557 }
558
561 template <size_t skip, typename T>
562 std::unique_ptr<BranchConfigurationInterface> createBranchConfiguration(ConstructorArgs&& cargs)
563 {
564 static_assert(skip == 0);
565 return std::move(std::make_unique<T>(cargs));
566 }
567
569 TChain mInput;
571 std::unique_ptr<BranchConfigurationInterface> mBranchConfiguration;
573 int mNEntries = 0;
575 int mReadEntry = -1;
577 int mNofPublished = -1;
579 int mMaxEntries = -1;
581 PublishingMode mPublishingMode = PublishingMode::Single;
583 SpecialPublishHook* mPublishHook = nullptr;
584};
585
587
588} // namespace o2::framework
589#endif
std::ostringstream debug
StringRef key
Class for time synchronization of RawReader instances.
void snapshot(const Output &spec, T const &object)
void process(ProcessingContext &context, int entry, std::function< o2::header::Stack()> &stackcreator)
Run the reader, first recursively for all lower stages, and then the current stage.
void exec(ProcessingContext &ctx, int entry, std::function< o2::header::Stack()> stackcreator) override
void setupInstance(TTree &tree, SpecialPublishHook *publishhook=nullptr)
void setup(TTree &tree, SpecialPublishHook *publishhook=nullptr) override
virtual void setup(TTree &, SpecialPublishHook *h=nullptr)
Setup the branch configuration, namely get the branch and class information from the tree.
virtual void exec(ProcessingContext &ctx, int entry, std::function< o2::header::Stack()> stackcreator)
bool operator()(ContextType &context, HeaderTypes &&... headers) const
self_type & operator++(int)=delete
postfix increment forbidden
void addFile(const char *fileName)
add a file as source for the tree
self_type & operator++()
prefix increment, move to the next entry
o2::framework::ROOTSerialized< char, TClass > ROOTSerializedByClass
PublishingMode
Publishing mode determines what to do when the number of entries in the tree is reached.
GenericRootTreeReader(const char *treename, Args &&... args)
GenericRootTreeReader()
default constructor
int getCount() const
return the number of published entries
std::vector< ConstructorArg > ConstructorArgs
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
GLuint buffer
Definition glcorearb.h:655
GLuint entry
Definition glcorearb.h:5735
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLboolean * data
Definition glcorearb.h:298
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint object
Definition glcorearb.h:4041
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
Lifetime
Possible Lifetime of objects being exchanged by the DPL.
Definition Lifetime.h:18
void empty(int)
helper structure to hold the constructor arguments for BranchConfigurationElement stages
ConstructorArg(key_type _key, const char *_name)
ConstructorArg(key_type _key, std::string const &_name)
std::function< bool(std::string_view name, ProcessingContext &context, Output const &, char *data)> hook
header::DataOrigin origin
Definition Output.h:28
header::DataHeader::SubSpecificationType subSpec
DefaultKey(const Output &desc)
header::DataDescription description
uint32_t SubSpecificationType
Definition DataHeader.h:622
a move-only header stack with serialized headers This is the flat buffer where all the headers in a m...
Definition Stack.h:33
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))