JAPAn
Just Another Parity Analyzer
Loading...
Searching...
No Matches
QwRootFile.h
Go to the documentation of this file.
1/*!
2 * \file QwRootFile.h
3 * \brief ROOT file and tree management wrapper classes
4 */
5
6#pragma once
7
8// System headers
9#include <algorithm>
10#include <cctype>
11#include <cstdint>
12#include <sstream>
13#include <stdexcept>
14#include <string>
15#include <typeindex>
16#include <unordered_map>
17#include <vector>
18#include <unistd.h>
19using std::type_info;
21// ROOT headers
22#include "TFile.h"
23#include "TTree.h"
24#include "TKey.h"
25#include "TPRegexp.h"
26#include "TSystem.h"
27#include "TString.h"
28
29// RNTuple headers (modern ROOT namespace) - only if supported
30#ifdef HAS_RNTUPLE_SUPPORT
31#include "ROOT/RNTuple.hxx"
32#include "ROOT/RNTupleModel.hxx"
33#include "ROOT/RField.hxx"
34#include "ROOT/RNTupleWriter.hxx"
35#include "ROOT/RNTupleWriteOptions.hxx"
36#endif
37
38// Qweak headers
39#include "QwOptions.h"
40#include "TMapFile.h"
41
42// If one defines more than this number of words in the full ntuple,
43// the results are going to get very very crazy.
44#define BRANCH_VECTOR_MAX_SIZE 25000
45
46/**
47 * \class QwRootTreeBranchVector
48 * \ingroup QwAnalysis
49 * \brief A helper class to manage a vector of branch entries for ROOT trees
50 *
51 * This class provides functionality to manage a collection of branch entries,
52 * including their names, types, offsets, and sizes. It supports adding new entries,
53 * accessing entries by index or name, and generating leaf lists for ROOT trees.
54 */
56public:
57 struct Entry {
58 std::string name;
59 std::size_t offset;
60 std::size_t size;
61 char type;
62 };
63
64 using size_type = std::size_t;
65
67
68 void reserve(size_type count) {
69 m_entries.reserve(count);
70 m_buffer.reserve(sizeof(double)*count);
71 }
72
74 m_entries.shrink_to_fit();
75 m_buffer.shrink_to_fit();
76 }
77
78 void clear() {
79 m_entries.clear();
80 m_buffer.clear();
81 }
82
83 size_type size() const noexcept { return m_entries.size(); }
84 bool empty() const noexcept { return m_entries.empty(); }
85
86 template <typename T = uint8_t>
87 const T& operator[](size_type index) const {
88 return value<T>(index);
89 }
90
91 template <typename T = uint8_t>
93 return value<T>(index);
94 }
95
96 template <typename T>
97 T& value(size_type index) {
98 auto& entry = m_entries.at(index);
99 return *reinterpret_cast<T*>(m_buffer.data() + entry.offset);
100 }
101
102 template <typename T>
103 const T& value(size_type index) const {
104 const auto& entry = m_entries.at(index);
105 return *reinterpret_cast<const T*>(m_buffer.data() + entry.offset);
106 }
107
108 // Explicit SetValue overloads with type checking to prevent automatic conversions
109 // (presence of multiple overloads ensures a compilation error if the types do not match)
110 void SetValue(size_type index, Double_t val) {
111 const auto& entry = m_entries.at(index);
112 if (entry.type != 'D') {
113 throw std::invalid_argument("Type mismatch: entry type '" + std::string(1, entry.type) + "' cannot store double value '" + entry.name + "'");
114 }
115 this->value<Double_t>(index) = val;
116 }
117
118 void SetValue(size_type index, Float_t val) {
119 const auto& entry = m_entries.at(index);
120 if (entry.type != 'F') {
121 throw std::invalid_argument("Type mismatch: entry type '" + std::string(1, entry.type) + "' cannot store float value '" + entry.name + "'");
122 }
123 this->value<Float_t>(index) = val;
124 }
125
126 void SetValue(size_type index, Int_t val) {
127 const auto& entry = m_entries.at(index);
128 if (entry.type != 'I') {
129 throw std::invalid_argument("Type mismatch: entry type '" + std::string(1, entry.type) + "' cannot store int value '" + entry.name + "'");
130 }
131 this->value<Int_t>(index) = val;
132 }
133
134 void SetValue(size_type index, Long64_t val) {
135 const auto& entry = m_entries.at(index);
136 if (entry.type != 'L') {
137 throw std::invalid_argument("Type mismatch: entry type '" + std::string(1, entry.type) + "' cannot store long long value '" + entry.name + "'");
138 }
139 this->value<Long64_t>(index) = val;
140 }
141
142 void SetValue(size_type index, Short_t val) {
143 const auto& entry = m_entries.at(index);
144 if (entry.type != 'S') {
145 throw std::invalid_argument("Type mismatch: entry type '" + std::string(1, entry.type) + "' cannot store short value '" + entry.name + "'");
146 }
147 this->value<Short_t>(index) = val;
148 }
149
150 // Unsigned type overloads
151 void SetValue(size_type index, UShort_t val) {
152 const auto& entry = m_entries.at(index);
153 if (entry.type != 's') {
154 throw std::invalid_argument("Type mismatch: entry type '" + std::string(1, entry.type) + "' cannot store short value '" + entry.name + "'");
155 }
156 this->value<UShort_t>(index) = val;
157 }
158
159 void SetValue(size_type index, UInt_t val) {
160 const auto& entry = m_entries.at(index);
161 if (entry.type != 'i') {
162 throw std::invalid_argument("Type mismatch: entry type '" + std::string(1, entry.type) + "' cannot store unsigned int value '" + entry.name + "'");
163 }
164 this->value<UInt_t>(index) = val;
165 }
166
167 void SetValue(size_type index, ULong64_t val) {
168 const auto& entry = m_entries.at(index);
169 if (entry.type != 'l') {
170 throw std::invalid_argument("Type mismatch: entry type '" + std::string(1, entry.type) + "' cannot store long long value '" + entry.name + "'");
171 }
172 this->value<ULong64_t>(index) = val;
173 }
174
175 void* data() noexcept { return m_buffer.data(); }
176 const void* data() const noexcept { return m_buffer.data(); }
177 size_type data_size() const noexcept { return m_buffer.size(); }
178
179 template <typename T>
180 T& back() {
181 if (m_entries.empty()) {
182 throw std::out_of_range("QwRootTreeBranchVector::back() called on empty container");
183 }
184 const auto& last_entry = m_entries.back();
185 return *reinterpret_cast<T*>(m_buffer.data() + last_entry.offset);
186 }
187
188 template <typename T>
189 const T& back() const {
190 if (m_entries.empty()) {
191 throw std::out_of_range("QwRootTreeBranchVector::back() called on empty container");
192 }
193 const auto& last_entry = m_entries.back();
194 return *reinterpret_cast<const T*>(m_buffer.data() + last_entry.offset);
195 }
196
197 void push_back(const std::string& name, const char type = 'D') {
198 const std::size_t entry_size = GetTypeSize(type);
199 const std::size_t offset = AlignOffset(m_buffer.size());
200
201 if (offset > m_buffer.capacity()) {
202 throw std::out_of_range("QwRootTreeBranchVector::push_back() requires buffer resize beyond reserved capacity");
203 }
204 if (offset > m_buffer.size()) {
205 m_buffer.resize(offset, 0u);
206 }
207
208 Entry entry{name, offset, entry_size, type};
209 m_entries.push_back(entry);
210
211 const std::size_t required = offset + entry_size;
212 if (required > m_buffer.capacity()) {
213 throw std::out_of_range("QwRootTreeBranchVector::push_back() requires buffer resize beyond reserved capacity");
214 }
215 if (required > m_buffer.size()) {
216 m_buffer.resize(required, 0u);
217 }
218 }
219
220 // Overload for TString (ROOT's string class)
221 void push_back(const TString& name, const char type = 'D') {
222 push_back(std::string(name.Data()), type);
223 }
224
225 // Overload for const char* (string literals)
226 void push_back(const char* name, const char type = 'D') {
227 push_back(std::string(name), type);
228 }
229
230 std::string LeafList(size_type start_index = 0) const {
231 static const std::string separator = ":";
232 std::ostringstream stream;
233 bool first = true;
234 for (size_type index = start_index; index < m_entries.size(); ++index) {
235 const auto& entry = m_entries[index];
236 if (!first) {
237 stream << separator;
238 }
239 stream << entry.name << "/" << entry.type;
240 first = false;
241 }
242 return stream.str();
243 }
244
245 std::string Dump(size_type start_index = 0, size_type end_index = 0) const {
246 std::ostringstream stream;
247 stream << "QwRootTreeBranchVector: " << m_entries.size() << " entries, "
248 << m_buffer.size() << " bytes\n";
249 size_t end_offset = (end_index == 0 || end_index > m_entries.size()) ?
250 m_buffer.size()
251 : m_entries[end_index - 1].offset + m_entries[end_index - 1].size;
252 stream << "QwRootTreeBranchVector: buffer at 0x" << std::hex << (void*) &m_buffer[0] << '\n';
253 stream << "QwRootTreeBranchVector: entries at 0x" << std::hex << (void*) &m_entries[0] << '\n';
254 for (size_t offset = m_entries[start_index].offset; offset < end_offset; offset += 4) {
255 stream << std::dec
256 << " [" << offset << "] "
257 << std::hex
258 << " offset=0x" << offset
259 << " (0x" << std::setw(4) << std::setfill('0')
260 << offset - m_entries[start_index].offset << ")"
261 << " buff=";
262 // Little-endian
263 for (std::size_t byte = 0; byte < 4; ++byte) {
264 stream << std::hex << std::setw(2) << std::setfill('0')
265 << static_cast<unsigned int>(m_buffer[offset + byte])
266 << " ";
267 }
268 stream << '\n';
269 }
270 end_index = (end_index == 0 || end_index > m_entries.size()) ?
271 m_entries.size()
272 : end_index;
273 for (size_type index = start_index; index < end_index; ++index) {
274 const auto& entry = m_entries[index];
275 stream << std::dec
276 << " [" << index << "] "
277 << std::hex
278 << " offset=0x" << entry.offset
279 << " (0x" << std::setw(4) << std::setfill('0')
280 << entry.offset - m_entries[start_index].offset << ")"
281 << " size=0x" << entry.size
282 << " buff=0x";
283 // Little-endian
284 for (std::size_t byte = GetTypeSize(entry.type); byte > 0; --byte) {
285 stream << std::hex << std::setw(2) << std::setfill('0')
286 << static_cast<unsigned int>((m_buffer.data() + entry.offset)[byte - 1]);
287 }
288 stream << std::dec
289 << " name=" << entry.name << "/" << entry.type
290 << " value=" << FormatValue(entry, index);
291 stream << '\n';
292 }
293 return stream.str();
294 }
295
296private:
297 static std::size_t GetTypeSize(char type) {
298 switch (type) {
299 case 'D':
300 return sizeof(double);
301 case 'F':
302 return sizeof(float);
303 case 'L':
304 return sizeof(long long);
305 case 'l':
306 return sizeof(unsigned long long);
307 case 'I':
308 return sizeof(int);
309 case 'i':
310 return sizeof(unsigned int);
311 case 'S':
312 return sizeof(short);
313 case 's':
314 return sizeof(unsigned short);
315 default:
316 throw std::invalid_argument("Unsupported branch type code: " + std::string(1, type));
317 }
318 }
319
320 static std::size_t AlignOffset(std::size_t offset) {
321 const std::size_t alignment = 4u;
322 return (offset + (alignment - 1u)) & ~(alignment - 1u);
323 }
324
325 std::vector<Entry> m_entries;
326 std::vector<std::uint8_t> m_buffer;
327
328 std::string FormatValue(const Entry& entry, size_type index) const {
329 switch (entry.type) {
330 case 'D':
331 return FormatNumeric(value<double>(index));
332 case 'F':
333 return FormatNumeric(value<float>(index));
334 case 'L':
335 return FormatNumeric(value<long long>(index));
336 case 'l':
338 case 'I':
339 return FormatNumeric(value<int>(index));
340 case 'i':
341 return FormatNumeric(value<unsigned int>(index));
342 case 'S':
343 return FormatNumeric(value<short>(index));
344 case 's':
346 default:
347 return "<unknown>";
348 }
349 }
350
351 template <typename T>
352 static std::string FormatNumeric(T input) {
353 std::ostringstream stream;
354 stream << input;
355 return stream.str();
356 }
357};
358
359/**
360 * \class QwRootTree
361 * \ingroup QwAnalysis
362 * \brief Wrapper class for ROOT tree management with vector-based data storage
363 *
364 * Provides functionality to write to ROOT trees using vectors of doubles,
365 * with support for branch construction, event filtering, and tree sharing.
366 * Handles both new tree creation and attachment to existing trees, enabling
367 * multiple subsystems to contribute data to a single ROOT tree.
368 */
370
371 public:
372
373 /// Constructor with name, and description
374 QwRootTree(const std::string& name, const std::string& desc, const std::string& prefix = "")
375 : fName(name),fDesc(desc),fPrefix(prefix),fType("type undefined"),
377 // Construct tree
379 }
380
381 /// Constructor with existing tree
382 QwRootTree(const QwRootTree* tree, const std::string& prefix = "")
383 : fName(tree->GetName()),fDesc(tree->GetDesc()),fPrefix(prefix),fType("type undefined"),
385 QwMessage << "Existing tree: " << tree->GetName() << ", " << tree->GetDesc() << QwLog::endl;
386 fTree = tree->fTree;
387 }
388
389 /// Constructor with name, description, and object
390 template < class T >
391 QwRootTree(const std::string& name, const std::string& desc, T& object, const std::string& prefix = "")
392 : fName(name),fDesc(desc),fPrefix(prefix),fType("type undefined"),
394 // Construct tree
396
397 // Construct branch
399
400 // Construct branches and vector
402 }
403
404 /// Constructor with existing tree, and object
405 template < class T >
406 QwRootTree(const QwRootTree* tree, T& object, const std::string& prefix = "")
407 : fName(tree->GetName()),fDesc(tree->GetDesc()),fPrefix(prefix),fType("type undefined"),
409 QwMessage << "Existing tree: " << tree->GetName() << ", " << tree->GetDesc() << QwLog::endl;
410 fTree = tree->fTree;
411
412 // Construct branches and vector
414 }
415
416 /// Destructor
417 virtual ~QwRootTree() { }
418
419
420 private:
421
422 static const TString kUnitsName;
423 static Double_t kUnitsValue[];
424
425 /// Construct the tree
427 QwMessage << "New tree: " << fName << ", " << fDesc << QwLog::endl;
428
429 fTree = new TTree(fName.c_str(), fDesc.c_str());
430
431 // Ensure tree is in the current directory
432 if (gDirectory) {
433 fTree->SetDirectory(gDirectory);
434
435 } else {
436
437 }
438 }
439
441 std::string name = "units";
442 fTree->Branch(name.c_str(), &kUnitsValue, kUnitsName);
443 }
444
445 /// Construct index from this tree to another tree
447 std::string name = "previous_entry_in_" + to->fName;
448 fTree->Branch(name.c_str(), &(to->fCurrentEvent));
449 }
450
451 /// Construct the branches and vector for generic objects
452 template < class T >
453 void ConstructBranchAndVector(T& object) {
454 // Reserve space for the branch vector
456 // Associate branches with vector
457 TString prefix = Form("%s",fPrefix.c_str());
458 object.ConstructBranchAndVector(fTree, prefix, fVector);
459
460 // Store the type of object
461 fType = typeid(object).name();
462
463 // Check memory reservation
464 if (fVector.size() > BRANCH_VECTOR_MAX_SIZE) {
465 QwError << "The branch vector is too large: " << fVector.size() << " leaves! "
466 << "The maximum size is " << BRANCH_VECTOR_MAX_SIZE << "."
467 << QwLog::endl;
468 exit(-1);
469 }
470 }
471
472
473 public:
474
475 /// Fill the branches for generic objects
476 template < class T >
477 void FillTreeBranches(const T& object) {
478 if (typeid(object).name() == fType) {
479 // Fill the branch vector
480 object.FillTreeVector(fVector);
481 } else {
482 QwError << "Attempting to fill tree vector for type " << fType << " with "
483 << "object of type " << typeid(object).name() << QwLog::endl;
484 exit(-1);
485 }
486 }
487
488 Long64_t AutoSave(Option_t *option){
489 return fTree->AutoSave(option);
490 }
491
492 /// Fill the tree
493 Int_t Fill() {
495
496 // Tree prescaling
497 if (fNumEventsCycle > 0) {
500 return 0;
501 }
502
503 // Fill the tree
504 Int_t retval = fTree->Fill();
505 // Check for errors
506 if (retval < 0) {
507 QwError << "Writing tree failed! Check disk space or quota." << QwLog::endl;
508 exit(retval);
509 }
510 return retval;
511 }
512
513
514 /// Print the tree name and description
515 void Print() const {
516 QwMessage << GetName() << ", " << GetType();
517 if (fPrefix != "")
518 QwMessage << " (prefix " << GetPrefix() << ")";
520 }
521
522 /// Get the tree pointer for low level operations
523 TTree* GetTree() const { return fTree; };
524
525
526 friend class QwRootFile;
527
528 private:
529
530 /// Tree pointer
531 TTree* fTree;
532 /// Vector of leaves
534
535
536 /// Name, description
537 const std::string fName;
538 const std::string fDesc;
539 const std::string fPrefix;
540
541 /// Get the name of the tree
542 const std::string& GetName() const { return fName; };
543 /// Get the description of the tree
544 const std::string& GetDesc() const { return fDesc; };
545 /// Get the description of the tree
546 const std::string& GetPrefix() const { return fPrefix; };
547
548
549 /// Object type
550 std::string fType;
551
552 /// Get the object type
553 std::string GetType() const { return fType; };
554
555
556 /// Tree prescaling parameters
561
562 /// Set tree prescaling parameters
563 void SetPrescaling(UInt_t num_to_save, UInt_t num_to_skip) {
564 fNumEventsToSave = num_to_save;
565 fNumEventsToSkip = num_to_skip;
567 }
568
569
570 /// Maximum tree size, autoflush and autosave
571 Long64_t fMaxTreeSize;
572 Long64_t fAutoFlush;
573 Long64_t fAutoSave;
575
576 /// Set maximum tree size
577 void SetMaxTreeSize(Long64_t maxsize = 1900000000) {
578 fMaxTreeSize = maxsize;
579 if (fTree) fTree->SetMaxTreeSize(maxsize);
580 }
581
582 /// Set autoflush size
583 void SetAutoFlush(Long64_t autoflush = 30000000) {
584 fAutoFlush = autoflush;
585 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,26,00)
586 if (fTree) fTree->SetAutoFlush(autoflush);
587 #endif
588 }
589
590 /// Set autosave size
591 void SetAutoSave(Long64_t autosave = 300000000) {
592 fAutoSave = autosave;
593 if (fTree) fTree->SetAutoSave(autosave);
594 }
595
596 /// Set basket size
597 void SetBasketSize(Int_t basketsize = 16000) {
598 fBasketSize = basketsize;
599 if (fTree) fTree->SetBasketSize("*",basketsize);
600 }
601
602 //Set circular buffer size for the memory resident tree
603 void SetCircular(Long64_t buff = 100000) {
604 if (fTree) fTree->SetCircular(buff);
605 }
606};
607
608#ifdef HAS_RNTUPLE_SUPPORT
609/**
610 * \class QwRootNTuple
611 * \ingroup QwAnalysis
612 * \brief A wrapper class for a ROOT RNTuple
613 *
614 * This class provides the functionality to write to ROOT RNTuples using a vector
615 * of doubles, similar to QwRootTree but using the newer RNTuple format.
616 */
617class QwRootNTuple {
618
619 public:
620
621 /// Constructor with name and description
622 QwRootNTuple(const std::string& name, const std::string& desc, const std::string& prefix = "")
623 : fName(name), fDesc(desc), fPrefix(prefix), fType("type undefined"),
624 fCurrentEvent(0), fNumEventsCycle(0), fNumEventsToSave(0), fNumEventsToSkip(0) {
625 // Create RNTuple model
626 fModel = ROOT::RNTupleModel::Create();
627 }
628
629 /// Constructor with name, description, and object
630 template < class T >
631 QwRootNTuple(const std::string& name, const std::string& desc, T& object, const std::string& prefix = "")
632 : fName(name), fDesc(desc), fPrefix(prefix), fType("type undefined"),
633 fCurrentEvent(0), fNumEventsCycle(0), fNumEventsToSave(0), fNumEventsToSkip(0) {
634 // Create RNTuple model
635 fModel = ROOT::RNTupleModel::Create();
636
637 // Construct fields and vector
638 ConstructFieldsAndVector(object);
639 }
640
641 /// Destructor
642 virtual ~QwRootNTuple() {
643 Close();
644 }
645
646 /// Close and finalize the RNTuple writer
647 void Close() {
648 if (fWriter) {
649 // Explicitly commit any remaining data and close the writer
650 // This ensures all data is written to the file before destruction
651 fWriter.reset(); // This calls the destructor which should finalize the RNTuple
652 }
653 }
654
655 private:
656
657 /// Construct the fields and vector for generic objects
658 template < class T >
659 void ConstructFieldsAndVector(T& object) {
660 // Reserve space for the field vector
661 fVector.reserve(BRANCH_VECTOR_MAX_SIZE);
662
663 // Associate fields with vector - now using shared field pointers
664 TString prefix = Form("%s", fPrefix.c_str());
665 object.ConstructNTupleAndVector(fModel, prefix, fVector, fFieldPtrs);
666
667 // Store the type of object
668 fType = typeid(object).name();
669
670 // Check memory reservation
671 if (fVector.size() > BRANCH_VECTOR_MAX_SIZE) {
672 QwError << "The field vector is too large: " << fVector.size() << " fields! "
673 << "The maximum size is " << BRANCH_VECTOR_MAX_SIZE << "."
674 << QwLog::endl;
675 exit(-1);
676 }
677
678 // Shrink memory reservation
679 fVector.shrink_to_fit();
680 }
681
682 public:
683
684 /// Initialize the RNTuple writer with a file
685 void InitializeWriter(TFile* file) {
686 if (!fModel) {
687 QwError << "RNTuple model not created for " << fName << QwLog::endl;
688 return;
689 }
690
691 // Before creating the writer, ensure all fields are added to the model
692 if (fVector.empty()) {
693 QwError << "No fields defined in RNTuple model for " << fName << QwLog::endl;
694 return;
695 }
696
697 try {
698 // Create write options with LZ4 compression for best performance
699 ROOT::RNTupleWriteOptions options;
700 options.SetCompression(ROOT::RCompressionSetting::EAlgorithm::kLZ4, 4);
701
702 // Create the writer with the model (transfers ownership)
703 // Use Append to add RNTuple to existing TFile
704 fWriter = ROOT::RNTupleWriter::Append(std::move(fModel), fName, *file, options);
705
706 QwMessage << "Created RNTuple '" << fName << "' with LZ4 compression in file " << file->GetName() << QwLog::endl;
707
708 } catch (const std::exception& e) {
709 QwError << "Failed to create RNTuple writer for '" << fName << "': " << e.what() << QwLog::endl;
710 }
711 }
712
713 /// Fill the fields for generic objects
714 template < class T >
715 void FillNTupleFields(const T& object) {
716 if (typeid(object).name() == fType) {
717 // Fill the field vector
718 object.FillNTupleVector(fVector);
719
720 // Use the shared field pointers which remain valid
721 if (fWriter) {
722 for (size_t i = 0; i < fVector.size() && i < fFieldPtrs.size(); ++i) {
723 if (fFieldPtrs[i]) {
724 *(fFieldPtrs[i]) = fVector[i];
725 }
726 }
727
728 // CRITICAL: Actually commit the data to the RNTuple
729 fWriter->Fill();
730
731 // Update event counter
732 fCurrentEvent++;
733 // RNTuple prescaling
734 if (fNumEventsCycle > 0) {
735 fCurrentEvent %= fNumEventsCycle;
736 }
737 } else {
738 QwError << "RNTuple writer not initialized for " << fName << QwLog::endl;
739 }
740 } else {
741 QwError << "Attempting to fill RNTuple vector for type " << fType << " with "
742 << "object of type " << typeid(object).name() << QwLog::endl;
743 exit(-1);
744 }
745 }
746
747 /// Fill the RNTuple (called by FillTree wrapper methods)
748 void Fill() {
749 // This method is now called indirectly - the actual filling happens in FillNTupleFields
750 // Just here for compatibility with the tree interface
751 }
752
753 /// Get the name of the RNTuple
754 const std::string& GetName() const { return fName; }
755 /// Get the description of the RNTuple
756 const std::string& GetDesc() const { return fDesc; }
757 /// Get the prefix of the RNTuple
758 const std::string& GetPrefix() const { return fPrefix; }
759 /// Get the object type
760 std::string GetType() const { return fType; }
761
762 /// Set prescaling parameters
763 void SetPrescaling(UInt_t num_to_save, UInt_t num_to_skip) {
764 fNumEventsToSave = num_to_save;
765 fNumEventsToSkip = num_to_skip;
766 fNumEventsCycle = fNumEventsToSave + fNumEventsToSkip;
767 }
768
769 /// Print the RNTuple name and description
770 void Print() const {
771 QwMessage << GetName() << ", " << GetType();
772 if (fPrefix != "")
773 QwMessage << " (prefix " << GetPrefix() << ")";
775 }
776
777 private:
778
779 /// RNTuple model and writer
780 std::unique_ptr<ROOT::RNTupleModel> fModel;
781 std::unique_ptr<ROOT::RNTupleWriter> fWriter;
782
783 /// Vector of values and shared field pointers (for RNTuple)
784 std::vector<Double_t> fVector;
785 std::vector<std::shared_ptr<Double_t>> fFieldPtrs;
786
787 /// Name, description, prefix
788 const std::string fName;
789 const std::string fDesc;
790 const std::string fPrefix;
791
792 /// Object type
793 std::string fType;
794
795 /// RNTuple prescaling parameters
796 UInt_t fCurrentEvent;
797 UInt_t fNumEventsCycle;
798 UInt_t fNumEventsToSave;
799 UInt_t fNumEventsToSkip;
800
801 friend class QwRootFile;
802};
803#endif // HAS_RNTUPLE_SUPPORT
804
805/**
806 * \class QwRootFile
807 * \ingroup QwAnalysis
808 * \brief A wrapper class for a ROOT file or memory mapped file
809 *
810 * This class functions as a wrapper around a ROOT TFile or a TMapFile. The
811 * common inheritance of both is only TObject, so there is a lot that we have
812 * to wrap (rather than inherit). Theoretically you could have both a TFile
813 * and a TMapFile represented by an object of this class at the same time, but
814 * that is untested.
815 *
816 * The functionality of writing to the file is done by templated functions.
817 * The objects that are passed to these functions have to provide the following
818 * functions:
819 * <ul>
820 * <li>ConstructHistograms, FillHistograms
821 * <li>ConstructBranchAndVector, FillTreeVector
822 * </ul>
823 *
824 * The class keeps track of the registered tree names, and the types of objects
825 * that have branches constructed in those trees (via QwRootTree). In most
826 * cases it should be possible to just call FillTreeBranches with only the object,
827 * although in rare cases this could be ambiguous.
828 *
829 * The proper way to register a tree is by either calling ConstructTreeBranches
830 * of NewTree first. Then FillTreeBranches will fill the vector, and FillTree
831 * will actually fill the tree. FillTree should be called only once.
832 */
834
835 public:
836
837 /// \brief Constructor with run label
838 QwRootFile(const TString& run_label);
839 /// \brief Destructor
840 virtual ~QwRootFile();
841
842
843 /// \brief Define the configuration options
844 static void DefineOptions(QwOptions &options);
845 /// \brief Process the configuration options
846 void ProcessOptions(QwOptions &options);
847 /// \brief Set default ROOT files dir
848 static void SetDefaultRootFileDir(const std::string& dir) {
850 }
851 /// \brief Set default ROOT file stem
852 static void SetDefaultRootFileStem(const std::string& stem) {
854 }
855
856
857 /// Is the ROOT file active?
858 Bool_t IsRootFile() const { return (fRootFile); };
859 /// Is the map file active?
860 Bool_t IsMapFile() const { return (fMapFile); };
861
862 /// \brief Construct indices from one tree to another tree
863 void ConstructIndices(const std::string& from, const std::string& to, bool reverse = true);
864
865 /// \brief Construct the tree branches of a generic object
866 template < class T >
867 void ConstructTreeBranches(const std::string& name, const std::string& desc, T& object, const std::string& prefix = "");
868 /// \brief Fill the tree branches of a generic object by tree name
869 template < class T >
870 void FillTreeBranches(const std::string& name, const T& object);
871 /// \brief Fill the tree branches of a generic object by type only
872 template < class T >
873 void FillTreeBranches(const T& object);
874
875#ifdef HAS_RNTUPLE_SUPPORT
876 /// \brief Construct the RNTuple fields of a generic object
877 template < class T >
878 void ConstructNTupleFields(const std::string& name, const std::string& desc, T& object, const std::string& prefix = "");
879 /// \brief Fill the RNTuple fields of a generic object by name
880 template < class T >
881 void FillNTupleFields(const std::string& name, const T& object);
882 /// \brief Fill the RNTuple fields of a generic object by type only
883 template < class T >
884 void FillNTupleFields(const T& object);
885#endif // HAS_RNTUPLE_SUPPORT
886
887
888 template < class T >
889 Int_t WriteParamFileList(const TString& name, T& object);
890
891
892 /// \brief Construct the histograms of a generic object
893 template < class T >
894 void ConstructObjects(const std::string& name, T& object);
895
896 /// \brief Construct the histograms of a generic object
897 template < class T >
898 void ConstructHistograms(const std::string& name, T& object);
899 /// Fill histograms of the subsystem array
900 template < class T >
901 void FillHistograms(T& object) {
902 // Update regularly
903 static Int_t update_count = 0;
904 update_count++;
905 if ((fUpdateInterval > 0) && ( update_count % fUpdateInterval == 0)) Update();
906
907 // Debug directory registration
908 std::string type = typeid(object).name();
909 bool hasDir = HasDirByType(object);
910
911 if (! hasDir) return;
912 // Fill histograms
913 object.FillHistograms();
914 }
915
916
917 /// Create a new tree with name and description
918 void NewTree(const std::string& name, const std::string& desc) {
919 if (IsTreeDisabled(name)) return;
920 this->cd();
921 QwRootTree *tree = 0;
922 if (! HasTreeByName(name)) {
923 tree = new QwRootTree(name,desc);
924 } else {
925 tree = new QwRootTree(fTreeByName[name].front());
926 }
927 fTreeByName[name].push_back(tree);
928 }
929
930#ifdef HAS_RNTUPLE_SUPPORT
931 /// Create a new RNTuple with name and description
932 void NewNTuple(const std::string& name, const std::string& desc) {
933 if (IsTreeDisabled(name) || !fEnableRNTuples) return;
934 QwRootNTuple *ntuple = 0;
935 if (! HasNTupleByName(name)) {
936 ntuple = new QwRootNTuple(name, desc);
937 // Initialize the writer with our file
938 ntuple->InitializeWriter(fRootFile);
939 } else {
940 // For simplicity, don't support copying existing RNTuples yet
941 QwError << "Cannot create duplicate RNTuple: " << name << QwLog::endl;
942 return;
943 }
944 fNTupleByName[name].push_back(ntuple);
945 }
946#endif // HAS_RNTUPLE_SUPPORT
947
948 /// Get the tree with name
949 TTree* GetTree(const std::string& name) {
950 if (! HasTreeByName(name)) return 0;
951 else return fTreeByName[name].front()->GetTree();
952 }
953
954 /// Fill the tree with name
955 Int_t FillTree(const std::string& name) {
956 if (! HasTreeByName(name)) return 0;
957 else return fTreeByName[name].front()->Fill();
958 }
959
960 /// Fill all registered trees
961 Int_t FillTrees() {
962 // Loop over all registered tree names
963 Int_t retval = 0;
964 std::map< const std::string, std::vector<QwRootTree*> >::iterator iter;
965 for (iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++) {
966 retval += iter->second.front()->Fill();
967 }
968 return retval;
969 }
970
971#ifdef HAS_RNTUPLE_SUPPORT
972 /// Fill the RNTuple with name
973 void FillNTuple(const std::string& name) {
974 if (HasNTupleByName(name)) {
975 fNTupleByName[name].front()->Fill();
976 }
977 }
978#endif // HAS_RNTUPLE_SUPPORT
979
980#ifdef HAS_RNTUPLE_SUPPORT
981 /// Fill all registered RNTuples
982 void FillNTuples() {
983 // Loop over all registered RNTuple names
984 std::map< const std::string, std::vector<QwRootNTuple*> >::iterator iter;
985 for (iter = fNTupleByName.begin(); iter != fNTupleByName.end(); iter++) {
986 iter->second.front()->Fill();
987 }
988 }
989#endif // HAS_RNTUPLE_SUPPORT
990
991 /// Print registered trees
992 void PrintTrees() const {
993 QwMessage << "Trees: " << QwLog::endl;
994 // Loop over all registered tree names
995 std::map< const std::string, std::vector<QwRootTree*> >::const_iterator iter;
996 for (iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++) {
997 QwMessage << iter->first << ": " << iter->second.size()
998 << " objects registered" << QwLog::endl;
999 // Loop over all registered objects for this tree
1000 std::vector<QwRootTree*>::const_iterator tree;
1001 for (tree = iter->second.begin(); tree != iter->second.end(); tree++) {
1002 (*tree)->Print();
1003 }
1004 }
1005 }
1006 /// Print registered histogram directories
1007 void PrintDirs() const {
1008 QwMessage << "Dirs: " << QwLog::endl;
1009 // Loop ove rall registered directories
1010 std::map< const std::string, TDirectory* >::const_iterator iter;
1011 for (iter = fDirsByName.begin(); iter != fDirsByName.end(); iter++) {
1012 QwMessage << iter->first << QwLog::endl;
1013 }
1014 }
1015
1016
1017 /// Write any object to the ROOT file (only valid for TFile)
1018 template < class T >
1019 Int_t WriteObject(const T* obj, const char* name, Option_t* option = "", Int_t bufsize = 0) {
1020 Int_t retval = 0;
1021 // TMapFile has no support for WriteObject
1022 if (fRootFile) retval = fRootFile->WriteObject(obj,name,option,bufsize);
1023 return retval;
1024 }
1025
1026
1027 // Wrapped functionality
1028 void Update() {
1029 if (fMapFile) {
1030 QwMessage << "TMapFile memory resident size: "
1031 << ((int*)fMapFile->GetBreakval() - (int*)fMapFile->GetBaseAddr()) *
1032 4 / sizeof(int32_t) / 1024 / 1024 << " MiB"
1033 << QwLog::endl;
1034 fMapFile->Update();
1035 }else{
1036 // this option will allow for reading the tree during write
1037 Long64_t nBytes(0);
1038 for (auto iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++)
1039 nBytes += iter->second.front()->AutoSave("SaveSelf");
1040
1041 QwMessage << "TFile saved: "
1042 << nBytes/1000000 << "MB (inaccurate number)" //FIXME this calculation is inaccurate
1043 << QwLog::endl;
1044 }
1045 }
1046 void Print() { if (fMapFile) fMapFile->Print(); if (fRootFile) fRootFile->Print(); }
1047 void ls() { if (fMapFile) fMapFile->ls(); if (fRootFile) fRootFile->ls(); }
1048 void Map() { if (fRootFile) fRootFile->Map(); }
1049
1050 private:
1051 /// Recursively clear in-memory objects from a directory tree.
1052 /// This removes objects from the TDirectory's in-memory list without deleting
1053 /// their on-disk representation (keys). Used to prevent RNTupleWriter::Close()
1054 /// from creating duplicate histogram cycles when it internally calls TFile::Write().
1055 void ClearInMemoryObjects(TDirectory* dir) {
1056 if (!dir) return;
1057
1058 // First, collect subdirectory names
1059 std::vector<TString> subdirs;
1060 TIter next(dir->GetListOfKeys());
1061 TKey* key;
1062 while ((key = (TKey*)next())) {
1063 if (TString(key->GetClassName()) == "TDirectoryFile") {
1064 subdirs.push_back(key->GetName());
1065 }
1066 }
1067
1068 // Recursively clear subdirectories
1069 for (const auto& name : subdirs) {
1070 TDirectory* sub = dynamic_cast<TDirectory*>(dir->Get(name));
1071 if (sub) ClearInMemoryObjects(sub);
1072 }
1073
1074 // Clear this directory's in-memory object list
1075 // "nodelete" option: remove from list but don't delete the TKey entries
1076 TList* list = dir->GetList();
1077 if (list) {
1078 list->Clear("nodelete");
1079 }
1080 }
1081
1082 public:
1083 void Close() {
1084
1085 if (fRootFile) {
1086 // Step 1: Write all trees explicitly
1087 for (auto iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++) {
1088 if (!iter->second.empty() && iter->second.front()) {
1089 TTree* tree = iter->second.front()->GetTree();
1090 if (tree && tree->GetEntries() > 0) {
1091 tree->Write();
1092 }
1093 }
1094 }
1095
1096 // Step 2: Write all in-memory objects (histograms, etc.) to disk
1097 // Use kOverwrite to avoid creating duplicate cycles
1098 fRootFile->Write(0, TObject::kOverwrite);
1099
1100 // Check if we should make the file permanent AFTER writing
1101 // This ensures histograms are on disk and detectable by HasAnyFilled()
1103
1104 // Step 3: CRITICAL FIX for RNTuple histogram duplication
1105 // Clear all in-memory objects from the TFile's directory structure.
1106 // This prevents RNTupleWriter::Close() from re-writing histograms,
1107 // which would create duplicate cycles. The histograms are already
1108 // safely written to disk in Step 2.
1109#ifdef HAS_RNTUPLE_SUPPORT
1110 if (!fNTupleByName.empty()) {
1112 }
1113#endif // HAS_RNTUPLE_SUPPORT
1114 }
1115
1116#ifdef HAS_RNTUPLE_SUPPORT
1117 // Step 4: Close all RNTuples
1118 // Now that in-memory histograms are cleared, the RNTupleWriter destructor
1119 // won't create duplicate histogram cycles when it internally writes to TFile
1120 for (auto& pair : fNTupleByName) {
1121 for (auto& ntuple : pair.second) {
1122 if (ntuple) ntuple->Close();
1123 }
1124 }
1125#endif // HAS_RNTUPLE_SUPPORT
1126
1127 // Step 5: Close the file
1128 if (fRootFile) {
1129 fRootFile->Close();
1130 }
1131
1132 if (fMapFile) fMapFile->Close();
1133 }
1134
1135 // Wrapped functionality
1136 Bool_t cd(const char* path = 0) {
1137 Bool_t status = kTRUE;
1138 if (fMapFile) status &= fMapFile->cd(path);
1139 if (fRootFile) status &= fRootFile->cd(path);
1140 return status;
1141 }
1142
1143 // Wrapped functionality
1144 TDirectory* mkdir(const char* name, const char* title = "") {
1145 // TMapFile has no support for mkdir
1146 if (fRootFile) return fRootFile->mkdir(name, title);
1147 else return 0;
1148 }
1149
1150 // Wrapped functionality
1151 Int_t Write(const char* name = 0, Int_t option = 0, Int_t bufsize = 0) {
1152 Int_t retval = 0;
1153 // TMapFile has no support for Write
1154 if (fRootFile) retval = fRootFile->Write(name, option, bufsize);
1155 return retval;
1156 }
1157
1158
1159 private:
1160
1161 /// Private default constructor
1163
1164
1165 /// ROOT file
1167
1168 /// ROOT files dir
1170 /// Default ROOT files dir
1171 static std::string fDefaultRootFileDir;
1172
1173 /// ROOT file stem
1175 /// Default ROOT file stem
1176 static std::string fDefaultRootFileStem;
1177
1178 /// While the file is open, give it a temporary filename. Perhaps
1179 /// change to a permanent name when closing the file.
1183
1184 /// Search for non-empty trees or histograms in the file
1185 Bool_t HasAnyFilled(void);
1186 Bool_t HasAnyFilled(TDirectory* d);
1187
1188 /// Map file
1189 TMapFile* fMapFile;
1197
1198
1199
1200 private:
1201
1202 /// List of excluded trees
1203 std::vector< TPRegexp > fDisabledTrees;
1204 std::vector< TPRegexp > fDisabledHistos;
1205
1206 /// Add regexp to list of disabled trees names
1207 void DisableTree(const TString& regexp) {
1208 fDisabledTrees.push_back(regexp);
1209 }
1210 /// Does this tree name match a disabled tree name?
1211 bool IsTreeDisabled(const std::string& name) {
1212 for (size_t i = 0; i < fDisabledTrees.size(); i++)
1213 if (fDisabledTrees.at(i).Match(name)) return true;
1214 return false;
1215 }
1216 /// Add regexp to list of disabled histogram directories
1217 void DisableHisto(const TString& regexp) {
1218 fDisabledHistos.push_back(regexp);
1219 }
1220 /// Does this histogram directory match a disabled histogram directory?
1221 bool IsHistoDisabled(const std::string& name) {
1222 for (size_t i = 0; i < fDisabledHistos.size(); i++)
1223 if (fDisabledHistos.at(i).Match(name)) return true;
1224 return false;
1225 }
1226
1227
1228 private:
1229
1230 /// Tree names, addresses, and types
1231 std::map< const std::string, std::vector<QwRootTree*> > fTreeByName;
1232 std::map< const void* , std::vector<QwRootTree*> > fTreeByAddr;
1233 std::map< const std::type_index , std::vector<QwRootTree*> > fTreeByType;
1234 // ... Are type_index objects really unique? Let's hope so.
1235
1236#ifdef HAS_RNTUPLE_SUPPORT
1237 /// RNTuple names, addresses, and types
1238 std::map< const std::string, std::vector<QwRootNTuple*> > fNTupleByName;
1239 std::map< const void* , std::vector<QwRootNTuple*> > fNTupleByAddr;
1240 std::map< const std::type_index , std::vector<QwRootNTuple*> > fNTupleByType;
1241
1242 /// RNTuple support flag
1243 Bool_t fEnableRNTuples;
1244#endif // HAS_RNTUPLE_SUPPORT
1245
1246 /// Is a tree registered for this name
1247 bool HasTreeByName(const std::string& name) {
1248 if (fTreeByName.count(name) == 0) return false;
1249 else return true;
1250 }
1251 /// Is a tree registered for this type
1252 template < class T >
1253 bool HasTreeByType(const T& object) {
1254 const std::type_index type = typeid(object);
1255 if (fTreeByType.count(type) == 0) return false;
1256 else return true;
1257 }
1258 /// Is a tree registered for this object
1259 template < class T >
1260 bool HasTreeByAddr(const T& object) {
1261 const void* addr = static_cast<const void*>(&object);
1262 if (fTreeByAddr.count(addr) == 0) return false;
1263 else return true;
1264 }
1265
1266#ifdef HAS_RNTUPLE_SUPPORT
1267 /// Is an RNTuple registered for this name
1268 bool HasNTupleByName(const std::string& name) {
1269 if (fNTupleByName.count(name) == 0) return false;
1270 else return true;
1271 }
1272 /// Is an RNTuple registered for this type
1273 template < class T >
1274 bool HasNTupleByType(const T& object) {
1275 const std::type_index type = typeid(object);
1276 if (fNTupleByType.count(type) == 0) return false;
1277 else return true;
1278 }
1279 /// Is an RNTuple registered for this object
1280 template < class T >
1281 bool HasNTupleByAddr(const T& object) {
1282 const void* addr = static_cast<const void*>(&object);
1283 if (fNTupleByAddr.count(addr) == 0) return false;
1284 else return true;
1285 }
1286#endif // HAS_RNTUPLE_SUPPORT
1287
1288 /// Directories
1289 std::map< const std::string, TDirectory* > fDirsByName;
1290 std::map< const std::string, std::vector<std::string> > fDirsByType;
1291
1292 /// Is a tree registered for this name
1293 bool HasDirByName(const std::string& name) {
1294 if (fDirsByName.count(name) == 0) return false;
1295 else return true;
1296 }
1297 /// Is a directory registered for this type
1298 template < class T >
1299 bool HasDirByType(const T& object) {
1300 std::string type = typeid(object).name();
1301 if (fDirsByType.count(type) == 0) return false;
1302 else return true;
1303 }
1304
1305
1306 private:
1307
1308 /// Prescaling of events written to tree
1315
1316 /// Maximum tree size
1317 static const Long64_t kMaxTreeSize;
1318 static const Int_t kMaxMapFileSize;
1319};
1320
1321/**
1322 * Construct the indices from one tree to another tree, and optionally in reverse as well.
1323 * @param from Name of tree where index will be created
1324 * @param to Name of tree to which index will point
1325 * @param reverse Flag to create indices in both direction
1326 */
1327inline void QwRootFile::ConstructIndices(const std::string& from, const std::string& to, bool reverse)
1328{
1329 // Return if we do not want this tree information
1330 if (IsTreeDisabled(from)) return;
1331 if (IsTreeDisabled(to)) return;
1332
1333 // If the trees are defined
1334 if (fTreeByName.count(from) > 0 && fTreeByName.count(to) > 0) {
1335
1336 // Construct index from the first tree to the second tree
1337 fTreeByName[from].front()->ConstructIndexTo(fTreeByName[to].front());
1338
1339 // Construct index from the second tree back to the first tree
1340 if (reverse)
1341 fTreeByName[to].front()->ConstructIndexTo(fTreeByName[from].front());
1342 }
1343}
1344
1345/**
1346 * Construct the tree branches of a generic object
1347 * @param name Name for tree
1348 * @param desc Description for tree
1349 * @param object Subsystem array
1350 * @param prefix Prefix for the tree
1351 */
1352template < class T >
1354 const std::string& name,
1355 const std::string& desc,
1356 T& object,
1357 const std::string& prefix)
1358{
1359 // Return if we do not want this tree information
1360 if (IsTreeDisabled(name)) return;
1361
1362 // Pointer to new tree
1363 QwRootTree* tree = 0;
1364
1365 // If the tree does not exist yet, create it
1366 if (fTreeByName.count(name) == 0) {
1367
1368 // Go to top level directory
1369
1370 this->cd();
1371
1372 // New tree with name, description, object, prefix
1373 tree = new QwRootTree(name, desc, object, prefix);
1374
1375 // Settings only relevant for new trees
1376 if (name == "evt")
1378 else if (name == "mul")
1380
1381 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,26,00)
1382 tree->SetAutoFlush(fAutoFlush);
1383 #endif
1384 tree->SetAutoSave(fAutoSave);
1387
1388 if (fCircularBufferSize > 0)
1390
1391 } else {
1392
1393 // New tree based on existing tree
1394 tree = new QwRootTree(fTreeByName[name].front(), object, prefix);
1395 }
1396
1397 // Add the branches to the list of trees by name, object, type
1398 const void* addr = static_cast<const void*>(&object);
1399 const std::type_index type = typeid(object);
1400 fTreeByName[name].push_back(tree);
1401 fTreeByAddr[addr].push_back(tree);
1402 fTreeByType[type].push_back(tree);
1403}
1404
1405
1406/**
1407 * Fill the tree branches of a generic object by name
1408 * @param name Name for tree
1409 * @param object Subsystem array
1410 */
1411template < class T >
1413 const std::string& name,
1414 const T& object)
1415{
1416 // If this name has no registered trees
1417 if (! HasTreeByName(name)) return;
1418 // If this type has no registered trees
1419 if (! HasTreeByType(object)) return;
1420
1421 // Get the address of the object
1422 const void* addr = static_cast<const void*>(&object);
1423
1424 // Fill the trees with the correct address
1425 for (size_t tree = 0; tree < fTreeByAddr[addr].size(); tree++) {
1426 if (fTreeByAddr[addr].at(tree)->GetName() == name) {
1427 fTreeByAddr[addr].at(tree)->FillTreeBranches(object);
1428 }
1429 }
1430}
1431
1432
1433/**
1434 * Fill the tree branches of a generic object by type only
1435 * @param object Subsystem array
1436 */
1437template < class T >
1439 const T& object)
1440{
1441 // If this address has no registered trees
1442 if (! HasTreeByAddr(object)) return;
1443
1444 // Get the address of the object
1445 const void* addr = static_cast<const void*>(&object);
1446
1447 // Fill the trees with the correct address
1448 for (size_t tree = 0; tree < fTreeByAddr[addr].size(); tree++) {
1449 fTreeByAddr[addr].at(tree)->FillTreeBranches(object);
1450 }
1451}
1452
1453
1454#ifdef HAS_RNTUPLE_SUPPORT
1455/**
1456 * Construct the RNTuple fields of a generic object
1457 * @param name Name for RNTuple
1458 * @param desc Description for RNTuple
1459 * @param object Subsystem array
1460 * @param prefix Prefix for the RNTuple
1461 */
1462template < class T >
1463void QwRootFile::ConstructNTupleFields(
1464 const std::string& name,
1465 const std::string& desc,
1466 T& object,
1467 const std::string& prefix)
1468{
1469 // Return if RNTuples are disabled
1470 if (!fEnableRNTuples) return;
1471
1472 // Pointer to new RNTuple
1473 QwRootNTuple* ntuple = 0;
1474
1475 // If the RNTuple does not exist yet, create it
1476 if (fNTupleByName.count(name) == 0) {
1477
1478 // New RNTuple with name, description, object, prefix
1479 ntuple = new QwRootNTuple(name, desc, object, prefix);
1480
1481 // Initialize the writer with our file
1482 ntuple->InitializeWriter(fRootFile);
1483
1484 // Settings only relevant for new RNTuples
1485 if (name == "evt")
1486 ntuple->SetPrescaling(fNumMpsEventsToSave, fNumMpsEventsToSkip);
1487 else if (name == "mul")
1488 ntuple->SetPrescaling(fNumHelEventsToSave, fNumHelEventsToSkip);
1489
1490 } else {
1491
1492 // For simplicity, don't support multiple RNTuples with same name yet
1493 QwError << "Cannot create duplicate RNTuple: " << name << QwLog::endl;
1494 return;
1495 }
1496
1497 // Add the fields to the list of RNTuples by name, object, type
1498 const void* addr = static_cast<const void*>(&object);
1499 const std::type_index type = typeid(object);
1500 fNTupleByName[name].push_back(ntuple);
1501 fNTupleByAddr[addr].push_back(ntuple);
1502 fNTupleByType[type].push_back(ntuple);
1503}
1504
1505
1506/**
1507 * Fill the RNTuple fields of a generic object by name
1508 * @param name Name for RNTuple
1509 * @param object Subsystem array
1510 */
1511template < class T >
1512void QwRootFile::FillNTupleFields(
1513 const std::string& name,
1514 const T& object)
1515{
1516 // If this name has no registered RNTuples
1517 if (! HasNTupleByName(name)) return;
1518 // If this type has no registered RNTuples
1519 if (! HasNTupleByType(object)) return;
1520
1521 // Get the address of the object
1522 const void* addr = static_cast<const void*>(&object);
1523
1524 // Fill the RNTuples with the correct address
1525 for (size_t ntuple = 0; ntuple < fNTupleByAddr[addr].size(); ntuple++) {
1526 if (fNTupleByAddr[addr].at(ntuple)->GetName() == name) {
1527 fNTupleByAddr[addr].at(ntuple)->FillNTupleFields(object);
1528 }
1529 }
1530}
1531
1532
1533/**
1534 * Fill the RNTuple fields of a generic object by type only
1535 * @param object Subsystem array
1536 */
1537template < class T >
1538void QwRootFile::FillNTupleFields(
1539 const T& object)
1540{
1541 // If this address has no registered RNTuples
1542 if (! HasNTupleByAddr(object)) return;
1543
1544 // Get the address of the object
1545 const void* addr = static_cast<const void*>(&object);
1546
1547 // Fill the RNTuples with the correct address
1548 for (size_t ntuple = 0; ntuple < fNTupleByAddr[addr].size(); ntuple++) {
1549 fNTupleByAddr[addr].at(ntuple)->FillNTupleFields(object);
1550 }
1551}
1552#endif // HAS_RNTUPLE_SUPPORT
1553
1554
1555/**
1556 * Construct the objects directory of a generic object
1557 * @param name Name for objects directory
1558 * @param object Subsystem array
1559 */
1560template < class T >
1561void QwRootFile::ConstructObjects(const std::string& name, T& object)
1562{
1563 // Create the objects in a directory
1564 if (fRootFile) {
1565 std::string type = typeid(object).name();
1566 fDirsByName[name] =
1567 fRootFile->GetDirectory(("/" + name).c_str()) ?
1568 fRootFile->GetDirectory(("/" + name).c_str()) :
1569 fRootFile->GetDirectory("/")->mkdir(name.c_str());
1570 fDirsByType[type].push_back(name);
1571 object.ConstructObjects(fDirsByName[name]);
1572 }
1573
1574 // No support for directories in a map file
1575 if (fMapFile) {
1576 QwMessage << "QwRootFile::ConstructObjects::detectors address "
1577 << &object
1578 << " and its name " << name
1579 << QwLog::endl;
1580
1581 std::string type = typeid(object).name();
1582 fDirsByName[name] = fMapFile->GetDirectory()->mkdir(name.c_str());
1583 fDirsByType[type].push_back(name);
1584 object.ConstructObjects();
1585 }
1586}
1587
1588
1589/**
1590 * Construct the histogram of a generic object
1591 * @param name Name for histogram directory
1592 * @param object Subsystem array
1593 */
1594template < class T >
1595void QwRootFile::ConstructHistograms(const std::string& name, T& object)
1596{
1597 // Return if we do not want this histogram information
1598 if (IsHistoDisabled(name)) return;
1599
1600 // Create the histograms in a directory
1601 if (fRootFile) {
1602 std::string type = typeid(object).name();
1603 fDirsByName[name] =
1604 fRootFile->GetDirectory(("/" + name).c_str()) ?
1605 fRootFile->GetDirectory(("/" + name).c_str()) :
1606 fRootFile->GetDirectory("/")->mkdir(name.c_str());
1607 fDirsByType[type].push_back(name);
1608
1609 object.ConstructHistograms(fDirsByName[name]);
1610 }
1611
1612 // No support for directories in a map file
1613 if (fMapFile) {
1614 QwMessage << "QwRootFile::ConstructHistograms::detectors address "
1615 << &object
1616 << " and its name " << name
1617 << QwLog::endl;
1618
1619 std::string type = typeid(object).name();
1620 fDirsByName[name] = fMapFile->GetDirectory()->mkdir(name.c_str());
1621 fDirsByType[type].push_back(name);
1622 //object.ConstructHistograms(fDirsByName[name]);
1623 object.ConstructHistograms();
1624 }
1625}
1626
1627
1628template < class T >
1629Int_t QwRootFile::WriteParamFileList(const TString &name, T& object)
1630{
1631 Int_t retval = 0;
1632 if (fRootFile) {
1633 TList *param_list = (TList*) fRootFile->FindObjectAny(name);
1634 if (not param_list) {
1635 retval = fRootFile->WriteObject(object.GetParamFileNameList(name), name);
1636 }
1637 }
1638 return retval;
1639}
An options class which parses command line, config file and environment.
#define QwError
Predefined log drain for errors.
Definition QwLog.h:39
#define QwMessage
Predefined log drain for regular messages.
Definition QwLog.h:49
#define BRANCH_VECTOR_MAX_SIZE
Definition QwRootFile.h:44
unsigned long long ULong64_t
Definition QwBlinder.h:41
static std::ostream & endl(std::ostream &)
End of the line.
Definition QwLog.cc:297
Command-line and configuration file options processor.
Definition QwOptions.h:141
A helper class to manage a vector of branch entries for ROOT trees.
Definition QwRootFile.h:55
const void * data() const noexcept
Definition QwRootFile.h:176
void SetValue(size_type index, Long64_t val)
Definition QwRootFile.h:134
void push_back(const char *name, const char type='D')
Definition QwRootFile.h:226
void reserve(size_type count)
Definition QwRootFile.h:68
std::size_t size_type
Definition QwRootFile.h:64
size_type size() const noexcept
Definition QwRootFile.h:83
void SetValue(size_type index, Int_t val)
Definition QwRootFile.h:126
T & value(size_type index)
Definition QwRootFile.h:97
static std::string FormatNumeric(T input)
Definition QwRootFile.h:352
std::vector< Entry > m_entries
Definition QwRootFile.h:325
std::string LeafList(size_type start_index=0) const
Definition QwRootFile.h:230
QwRootTreeBranchVector()=default
T & operator[](size_type index)
Definition QwRootFile.h:92
void SetValue(size_type index, Short_t val)
Definition QwRootFile.h:142
std::string FormatValue(const Entry &entry, size_type index) const
Definition QwRootFile.h:328
bool empty() const noexcept
Definition QwRootFile.h:84
void SetValue(size_type index, ULong64_t val)
Definition QwRootFile.h:167
void SetValue(size_type index, UInt_t val)
Definition QwRootFile.h:159
size_type data_size() const noexcept
Definition QwRootFile.h:177
std::vector< std::uint8_t > m_buffer
Definition QwRootFile.h:326
const T & operator[](size_type index) const
Definition QwRootFile.h:87
static std::size_t GetTypeSize(char type)
Definition QwRootFile.h:297
void push_back(const std::string &name, const char type='D')
Definition QwRootFile.h:197
std::string Dump(size_type start_index=0, size_type end_index=0) const
Definition QwRootFile.h:245
static std::size_t AlignOffset(std::size_t offset)
Definition QwRootFile.h:320
void SetValue(size_type index, Float_t val)
Definition QwRootFile.h:118
void SetValue(size_type index, UShort_t val)
Definition QwRootFile.h:151
void SetValue(size_type index, Double_t val)
Definition QwRootFile.h:110
const T & value(size_type index) const
Definition QwRootFile.h:103
void * data() noexcept
Definition QwRootFile.h:175
const T & back() const
Definition QwRootFile.h:189
void push_back(const TString &name, const char type='D')
Definition QwRootFile.h:221
Wrapper class for ROOT tree management with vector-based data storage.
Definition QwRootFile.h:369
QwRootTree(const std::string &name, const std::string &desc, T &object, const std::string &prefix="")
Constructor with name, description, and object.
Definition QwRootFile.h:391
Long64_t AutoSave(Option_t *option)
Definition QwRootFile.h:488
Int_t Fill()
Fill the tree.
Definition QwRootFile.h:493
void ConstructNewTree()
Construct the tree.
Definition QwRootFile.h:426
UInt_t fCurrentEvent
Tree prescaling parameters.
Definition QwRootFile.h:557
virtual ~QwRootTree()
Destructor.
Definition QwRootFile.h:417
const std::string fPrefix
Definition QwRootFile.h:539
UInt_t fNumEventsToSave
Definition QwRootFile.h:559
void SetAutoFlush(Long64_t autoflush=30000000)
Set autoflush size.
Definition QwRootFile.h:583
UInt_t fNumEventsToSkip
Definition QwRootFile.h:560
void Print() const
Print the tree name and description.
Definition QwRootFile.h:515
Long64_t fAutoFlush
Definition QwRootFile.h:572
Long64_t fMaxTreeSize
Maximum tree size, autoflush and autosave.
Definition QwRootFile.h:571
const std::string fName
Name, description.
Definition QwRootFile.h:537
std::string fType
Object type.
Definition QwRootFile.h:550
static const TString kUnitsName
Definition QwRootFile.h:422
void ConstructUnitsBranch()
Definition QwRootFile.h:440
void SetCircular(Long64_t buff=100000)
Definition QwRootFile.h:603
void SetMaxTreeSize(Long64_t maxsize=1900000000)
Set maximum tree size.
Definition QwRootFile.h:577
UInt_t fNumEventsCycle
Definition QwRootFile.h:558
QwRootTree(const std::string &name, const std::string &desc, const std::string &prefix="")
Constructor with name, and description.
Definition QwRootFile.h:374
void SetAutoSave(Long64_t autosave=300000000)
Set autosave size.
Definition QwRootFile.h:591
const std::string & GetPrefix() const
Get the description of the tree.
Definition QwRootFile.h:546
QwRootTreeBranchVector fVector
Vector of leaves.
Definition QwRootFile.h:533
void ConstructBranchAndVector(T &object)
Construct the branches and vector for generic objects.
Definition QwRootFile.h:453
Int_t fBasketSize
Definition QwRootFile.h:574
static Double_t kUnitsValue[]
Definition QwRootFile.h:20
TTree * GetTree() const
Get the tree pointer for low level operations.
Definition QwRootFile.h:523
void ConstructIndexTo(QwRootTree *to)
Construct index from this tree to another tree.
Definition QwRootFile.h:446
const std::string & GetDesc() const
Get the description of the tree.
Definition QwRootFile.h:544
TTree * fTree
Tree pointer.
Definition QwRootFile.h:531
friend class QwRootFile
Definition QwRootFile.h:526
void SetBasketSize(Int_t basketsize=16000)
Set basket size.
Definition QwRootFile.h:597
const std::string fDesc
Definition QwRootFile.h:538
void FillTreeBranches(const T &object)
Fill the branches for generic objects.
Definition QwRootFile.h:477
QwRootTree(const QwRootTree *tree, T &object, const std::string &prefix="")
Constructor with existing tree, and object.
Definition QwRootFile.h:406
QwRootTree(const QwRootTree *tree, const std::string &prefix="")
Constructor with existing tree.
Definition QwRootFile.h:382
const std::string & GetName() const
Get the name of the tree.
Definition QwRootFile.h:542
Long64_t fAutoSave
Definition QwRootFile.h:573
void SetPrescaling(UInt_t num_to_save, UInt_t num_to_skip)
Set tree prescaling parameters.
Definition QwRootFile.h:563
std::string GetType() const
Get the object type.
Definition QwRootFile.h:553
Bool_t IsRootFile() const
Is the ROOT file active?
Definition QwRootFile.h:858
bool HasTreeByType(const T &object)
Is a tree registered for this type.
std::vector< TPRegexp > fDisabledTrees
List of excluded trees.
std::map< const std::string, std::vector< std::string > > fDirsByType
Int_t fBasketSize
void NewTree(const std::string &name, const std::string &desc)
Create a new tree with name and description.
Definition QwRootFile.h:918
TString fRootFileStem
ROOT file stem.
void Print()
Int_t FillTree(const std::string &name)
Fill the tree with name.
Definition QwRootFile.h:955
TFile * fRootFile
ROOT file.
Bool_t IsMapFile() const
Is the map file active?
Definition QwRootFile.h:860
bool HasDirByType(const T &object)
Is a directory registered for this type.
virtual ~QwRootFile()
Destructor.
TString fRootFileDir
ROOT files dir.
void DisableTree(const TString &regexp)
Add regexp to list of disabled trees names.
Int_t fCompressionAlgorithm
Bool_t cd(const char *path=0)
Bool_t HasAnyFilled(void)
Search for non-empty trees or histograms in the file.
Int_t Write(const char *name=0, Int_t option=0, Int_t bufsize=0)
void PrintDirs() const
Print registered histogram directories.
static std::string fDefaultRootFileDir
Default ROOT files dir.
void PrintTrees() const
Print registered trees.
Definition QwRootFile.h:992
static const Int_t kMaxMapFileSize
Int_t WriteObject(const T *obj, const char *name, Option_t *option="", Int_t bufsize=0)
Write any object to the ROOT file (only valid for TFile)
std::map< const std::string, TDirectory * > fDirsByName
Directories.
void ProcessOptions(QwOptions &options)
Process the configuration options.
QwRootFile(const TString &run_label)
Constructor with run label.
Definition QwRootFile.cc:25
UInt_t fNumHelEventsToSkip
bool IsHistoDisabled(const std::string &name)
Does this histogram directory match a disabled histogram directory?
Int_t fAutoSave
std::map< const std::string, std::vector< QwRootTree * > > fTreeByName
Tree names, addresses, and types.
TDirectory * mkdir(const char *name, const char *title="")
Int_t fCompressionLevel
void Close()
void DisableHisto(const TString &regexp)
Add regexp to list of disabled histogram directories.
void Update()
static void SetDefaultRootFileStem(const std::string &stem)
Set default ROOT file stem.
Definition QwRootFile.h:852
static void SetDefaultRootFileDir(const std::string &dir)
Set default ROOT files dir.
Definition QwRootFile.h:848
void ConstructTreeBranches(const std::string &name, const std::string &desc, T &object, const std::string &prefix="")
Construct the tree branches of a generic object.
Bool_t fUseTemporaryFile
UInt_t fNumMpsEventsToSave
static std::string fDefaultRootFileStem
Default ROOT file stem.
void FillHistograms(T &object)
Fill histograms of the subsystem array.
Definition QwRootFile.h:901
TString fPermanentName
bool IsTreeDisabled(const std::string &name)
Does this tree name match a disabled tree name?
Int_t FillTrees()
Fill all registered trees.
Definition QwRootFile.h:961
std::map< const std::type_index, std::vector< QwRootTree * > > fTreeByType
static void DefineOptions(QwOptions &options)
Define the configuration options.
UInt_t fNumHelEventsToSave
TTree * GetTree(const std::string &name)
Get the tree with name.
Definition QwRootFile.h:949
bool HasTreeByAddr(const T &object)
Is a tree registered for this object.
Int_t fAutoFlush
TMapFile * fMapFile
Map file.
bool HasDirByName(const std::string &name)
Is a tree registered for this name.
Int_t fUpdateInterval
void FillTreeBranches(const std::string &name, const T &object)
Fill the tree branches of a generic object by tree name.
Int_t WriteParamFileList(const TString &name, T &object)
QwRootFile()
Private default constructor.
void ConstructHistograms(const std::string &name, T &object)
Construct the histograms of a generic object.
void ClearInMemoryObjects(TDirectory *dir)
UInt_t fCircularBufferSize
std::map< const void *, std::vector< QwRootTree * > > fTreeByAddr
void ConstructObjects(const std::string &name, T &object)
Construct the histograms of a generic object.
static const Long64_t kMaxTreeSize
Maximum tree size.
Bool_t fMakePermanent
std::vector< TPRegexp > fDisabledHistos
UInt_t fNumMpsEventsToSkip
Prescaling of events written to tree.
void ConstructIndices(const std::string &from, const std::string &to, bool reverse=true)
Construct indices from one tree to another tree.
UInt_t fCurrentEvent
bool HasTreeByName(const std::string &name)
Is a tree registered for this name.
Bool_t fEnableMapFile