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 user-specified compression settings
699 ROOT::RNTupleWriteOptions options;
700 options.SetCompression(
701 static_cast<ROOT::RCompressionSetting::EAlgorithm::EValues>(fRNTupleCompressionAlgorithm),
702 fRNTupleCompressionLevel
703 );
704
705 // Create the writer with the model (transfers ownership)
706 // Use Append to add RNTuple to existing TFile
707 fWriter = ROOT::RNTupleWriter::Append(std::move(fModel), fName, *file, options);
708
709 const char* algo_name = "UNKNOWN";
710 switch(fRNTupleCompressionAlgorithm) {
711 case 1: algo_name = "ZLIB"; break;
712 case 2: algo_name = "LZMA"; break;
713 case 4: algo_name = "LZ4"; break;
714 case 5: algo_name = "ZSTD"; break;
715 }
716 QwMessage << "Created RNTuple '" << fName << "' with " << algo_name
717 << " compression (level " << fRNTupleCompressionLevel << ") in file "
718 << file->GetName() << QwLog::endl;
719
720 } catch (const std::exception& e) {
721 QwError << "Failed to create RNTuple writer for '" << fName << "': " << e.what() << QwLog::endl;
722 }
723 }
724
725 /// Fill the fields for generic objects
726 template < class T >
727 void FillNTupleFields(const T& object) {
728 if (typeid(object).name() == fType) {
729 // Fill the field vector
730 object.FillNTupleVector(fVector);
731
732 // Use the shared field pointers which remain valid
733 if (fWriter) {
734 for (size_t i = 0; i < fVector.size() && i < fFieldPtrs.size(); ++i) {
735 if (fFieldPtrs[i]) {
736 *(fFieldPtrs[i]) = fVector[i];
737 }
738 }
739
740 // CRITICAL: Actually commit the data to the RNTuple
741 fWriter->Fill();
742
743 // Update event counter
744 fCurrentEvent++;
745 // RNTuple prescaling
746 if (fNumEventsCycle > 0) {
747 fCurrentEvent %= fNumEventsCycle;
748 }
749 } else {
750 QwError << "RNTuple writer not initialized for " << fName << QwLog::endl;
751 }
752 } else {
753 QwError << "Attempting to fill RNTuple vector for type " << fType << " with "
754 << "object of type " << typeid(object).name() << QwLog::endl;
755 exit(-1);
756 }
757 }
758
759 /// Fill the RNTuple (called by FillTree wrapper methods)
760 void Fill() {
761 // This method is now called indirectly - the actual filling happens in FillNTupleFields
762 // Just here for compatibility with the tree interface
763 }
764
765 /// Get the name of the RNTuple
766 const std::string& GetName() const { return fName; }
767 /// Get the description of the RNTuple
768 const std::string& GetDesc() const { return fDesc; }
769 /// Get the prefix of the RNTuple
770 const std::string& GetPrefix() const { return fPrefix; }
771 /// Get the object type
772 std::string GetType() const { return fType; }
773
774 /// Set prescaling parameters
775 void SetPrescaling(UInt_t num_to_save, UInt_t num_to_skip) {
776 fNumEventsToSave = num_to_save;
777 fNumEventsToSkip = num_to_skip;
778 fNumEventsCycle = fNumEventsToSave + fNumEventsToSkip;
779 }
780
781 /// Print the RNTuple name and description
782 void Print() const {
783 QwMessage << GetName() << ", " << GetType();
784 if (fPrefix != "")
785 QwMessage << " (prefix " << GetPrefix() << ")";
787 }
788
789 private:
790
791 /// RNTuple model and writer
792 std::unique_ptr<ROOT::RNTupleModel> fModel;
793 std::unique_ptr<ROOT::RNTupleWriter> fWriter;
794
795 /// Vector of values and shared field pointers (for RNTuple)
796 std::vector<Double_t> fVector;
797 std::vector<std::shared_ptr<Double_t>> fFieldPtrs;
798
799 /// Name, description, prefix
800 const std::string fName;
801 const std::string fDesc;
802 const std::string fPrefix;
803
804 /// Object type
805 std::string fType;
806
807 /// RNTuple prescaling parameters
808 UInt_t fCurrentEvent;
809 UInt_t fNumEventsCycle;
810 UInt_t fNumEventsToSave;
811 UInt_t fNumEventsToSkip;
812
813 /// RNTuple compression settings
814 Int_t fRNTupleCompressionAlgorithm;
815 Int_t fRNTupleCompressionLevel;
816
817 friend class QwRootFile;
818};
819#endif // HAS_RNTUPLE_SUPPORT
820
821/**
822 * \class QwRootFile
823 * \ingroup QwAnalysis
824 * \brief A wrapper class for a ROOT file or memory mapped file
825 *
826 * This class functions as a wrapper around a ROOT TFile or a TMapFile. The
827 * common inheritance of both is only TObject, so there is a lot that we have
828 * to wrap (rather than inherit). Theoretically you could have both a TFile
829 * and a TMapFile represented by an object of this class at the same time, but
830 * that is untested.
831 *
832 * The functionality of writing to the file is done by templated functions.
833 * The objects that are passed to these functions have to provide the following
834 * functions:
835 * <ul>
836 * <li>ConstructHistograms, FillHistograms
837 * <li>ConstructBranchAndVector, FillTreeVector
838 * </ul>
839 *
840 * The class keeps track of the registered tree names, and the types of objects
841 * that have branches constructed in those trees (via QwRootTree). In most
842 * cases it should be possible to just call FillTreeBranches with only the object,
843 * although in rare cases this could be ambiguous.
844 *
845 * The proper way to register a tree is by either calling ConstructTreeBranches
846 * of NewTree first. Then FillTreeBranches will fill the vector, and FillTree
847 * will actually fill the tree. FillTree should be called only once.
848 */
850
851 public:
852
853 /// \brief Constructor with run label
854 QwRootFile(const TString& run_label);
855 /// \brief Destructor
856 virtual ~QwRootFile();
857
858
859 /// \brief Define the configuration options
860 static void DefineOptions(QwOptions &options);
861 /// \brief Process the configuration options
862 void ProcessOptions(QwOptions &options);
863 /// \brief Set default ROOT files dir
864 static void SetDefaultRootFileDir(const std::string& dir) {
866 }
867 /// \brief Set default ROOT file stem
868 static void SetDefaultRootFileStem(const std::string& stem) {
870 }
871
872
873 /// Is the ROOT file active?
874 Bool_t IsRootFile() const { return (fRootFile); };
875 /// Is the map file active?
876 Bool_t IsMapFile() const { return (fMapFile); };
877
878 /// \brief Construct indices from one tree to another tree
879 void ConstructIndices(const std::string& from, const std::string& to, bool reverse = true);
880
881 /// \brief Construct the tree branches of a generic object
882 template < class T >
883 void ConstructTreeBranches(const std::string& name, const std::string& desc, T& object, const std::string& prefix = "");
884 /// \brief Fill the tree branches of a generic object by tree name
885 template < class T >
886 void FillTreeBranches(const std::string& name, const T& object);
887 /// \brief Fill the tree branches of a generic object by type only
888 template < class T >
889 void FillTreeBranches(const T& object);
890
891#ifdef HAS_RNTUPLE_SUPPORT
892 /// \brief Construct the RNTuple fields of a generic object
893 template < class T >
894 void ConstructNTupleFields(const std::string& name, const std::string& desc, T& object, const std::string& prefix = "");
895 /// \brief Fill the RNTuple fields of a generic object by name
896 template < class T >
897 void FillNTupleFields(const std::string& name, const T& object);
898 /// \brief Fill the RNTuple fields of a generic object by type only
899 template < class T >
900 void FillNTupleFields(const T& object);
901#endif // HAS_RNTUPLE_SUPPORT
902
903
904 template < class T >
905 Int_t WriteParamFileList(const TString& name, T& object);
906
907
908 /// \brief Construct the histograms of a generic object
909 template < class T >
910 void ConstructObjects(const std::string& name, T& object);
911
912 /// \brief Construct the histograms of a generic object
913 template < class T >
914 void ConstructHistograms(const std::string& name, T& object);
915 /// Fill histograms of the subsystem array
916 template < class T >
917 void FillHistograms(T& object) {
918 // Update regularly
919 static Int_t update_count = 0;
920 update_count++;
921 if ((fUpdateInterval > 0) && ( update_count % fUpdateInterval == 0)) Update();
922
923 // Debug directory registration
924 std::string type = typeid(object).name();
925 bool hasDir = HasDirByType(object);
926
927 if (! hasDir) return;
928 // Fill histograms
929 object.FillHistograms();
930 }
931
932
933 /// Create a new tree with name and description
934 void NewTree(const std::string& name, const std::string& desc) {
935 if (IsTreeDisabled(name)) return;
936 this->cd();
937 QwRootTree *tree = 0;
938 if (! HasTreeByName(name)) {
939 tree = new QwRootTree(name,desc);
940 } else {
941 tree = new QwRootTree(fTreeByName[name].front());
942 }
943 fTreeByName[name].push_back(tree);
944 }
945
946#ifdef HAS_RNTUPLE_SUPPORT
947 /// Create a new RNTuple with name and description
948 void NewNTuple(const std::string& name, const std::string& desc) {
949 if (IsTreeDisabled(name) || !fEnableRNTuples) return;
950 QwRootNTuple *ntuple = 0;
951 if (! HasNTupleByName(name)) {
952 ntuple = new QwRootNTuple(name, desc);
953 // Set compression settings before initializing writer
954 ntuple->fRNTupleCompressionAlgorithm = fRNTupleCompressionAlgorithm;
955 ntuple->fRNTupleCompressionLevel = fRNTupleCompressionLevel;
956 // Initialize the writer with our file
957 ntuple->InitializeWriter(fRootFile);
958 } else {
959 // For simplicity, don't support copying existing RNTuples yet
960 QwError << "Cannot create duplicate RNTuple: " << name << QwLog::endl;
961 return;
962 }
963 fNTupleByName[name].push_back(ntuple);
964 }
965#endif // HAS_RNTUPLE_SUPPORT
966
967 /// Get the tree with name
968 TTree* GetTree(const std::string& name) {
969 if (! HasTreeByName(name)) return 0;
970 else return fTreeByName[name].front()->GetTree();
971 }
972
973 /// Fill the tree with name
974 Int_t FillTree(const std::string& name) {
975 if (! HasTreeByName(name)) return 0;
976 else return fTreeByName[name].front()->Fill();
977 }
978
979 /// Fill all registered trees
980 Int_t FillTrees() {
981 // Loop over all registered tree names
982 Int_t retval = 0;
983 std::map< const std::string, std::vector<QwRootTree*> >::iterator iter;
984 for (iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++) {
985 retval += iter->second.front()->Fill();
986 }
987 return retval;
988 }
989
990#ifdef HAS_RNTUPLE_SUPPORT
991 /// Fill the RNTuple with name
992 void FillNTuple(const std::string& name) {
993 if (HasNTupleByName(name)) {
994 fNTupleByName[name].front()->Fill();
995 }
996 }
997#endif // HAS_RNTUPLE_SUPPORT
998
999#ifdef HAS_RNTUPLE_SUPPORT
1000 /// Fill all registered RNTuples
1001 void FillNTuples() {
1002 // Loop over all registered RNTuple names
1003 std::map< const std::string, std::vector<QwRootNTuple*> >::iterator iter;
1004 for (iter = fNTupleByName.begin(); iter != fNTupleByName.end(); iter++) {
1005 iter->second.front()->Fill();
1006 }
1007 }
1008#endif // HAS_RNTUPLE_SUPPORT
1009
1010 /// Print registered trees
1011 void PrintTrees() const {
1012 QwMessage << "Trees: " << QwLog::endl;
1013 // Loop over all registered tree names
1014 std::map< const std::string, std::vector<QwRootTree*> >::const_iterator iter;
1015 for (iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++) {
1016 QwMessage << iter->first << ": " << iter->second.size()
1017 << " objects registered" << QwLog::endl;
1018 // Loop over all registered objects for this tree
1019 std::vector<QwRootTree*>::const_iterator tree;
1020 for (tree = iter->second.begin(); tree != iter->second.end(); tree++) {
1021 (*tree)->Print();
1022 }
1023 }
1024 }
1025 /// Print registered histogram directories
1026 void PrintDirs() const {
1027 QwMessage << "Dirs: " << QwLog::endl;
1028 // Loop ove rall registered directories
1029 std::map< const std::string, TDirectory* >::const_iterator iter;
1030 for (iter = fDirsByName.begin(); iter != fDirsByName.end(); iter++) {
1031 QwMessage << iter->first << QwLog::endl;
1032 }
1033 }
1034
1035
1036 /// Write any object to the ROOT file (only valid for TFile)
1037 template < class T >
1038 Int_t WriteObject(const T* obj, const char* name, Option_t* option = "", Int_t bufsize = 0) {
1039 Int_t retval = 0;
1040 // TMapFile has no support for WriteObject
1041 if (fRootFile) retval = fRootFile->WriteObject(obj,name,option,bufsize);
1042 return retval;
1043 }
1044
1045
1046 // Wrapped functionality
1047 void Update() {
1048 if (fMapFile) {
1049 QwMessage << "TMapFile memory resident size: "
1050 << ((int*)fMapFile->GetBreakval() - (int*)fMapFile->GetBaseAddr()) *
1051 4 / sizeof(int32_t) / 1024 / 1024 << " MiB"
1052 << QwLog::endl;
1053 fMapFile->Update();
1054 }else{
1055 // this option will allow for reading the tree during write
1056 Long64_t nBytes(0);
1057 for (auto iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++)
1058 nBytes += iter->second.front()->AutoSave("SaveSelf");
1059
1060 QwMessage << "TFile saved: "
1061 << nBytes/1000000 << "MB (inaccurate number)" //FIXME this calculation is inaccurate
1062 << QwLog::endl;
1063 }
1064 }
1065 void Print() { if (fMapFile) fMapFile->Print(); if (fRootFile) fRootFile->Print(); }
1066 void ls() { if (fMapFile) fMapFile->ls(); if (fRootFile) fRootFile->ls(); }
1067 void Map() { if (fRootFile) fRootFile->Map(); }
1068
1069 private:
1070 /// Recursively clear in-memory objects from a directory tree.
1071 /// This removes objects from the TDirectory's in-memory list without deleting
1072 /// their on-disk representation (keys). Used to prevent RNTupleWriter::Close()
1073 /// from creating duplicate histogram cycles when it internally calls TFile::Write().
1074 void ClearInMemoryObjects(TDirectory* dir) {
1075 if (!dir) return;
1076
1077 // First, collect subdirectory names
1078 std::vector<TString> subdirs;
1079 TIter next(dir->GetListOfKeys());
1080 TKey* key;
1081 while ((key = (TKey*)next())) {
1082 if (TString(key->GetClassName()) == "TDirectoryFile") {
1083 subdirs.push_back(key->GetName());
1084 }
1085 }
1086
1087 // Recursively clear subdirectories
1088 for (const auto& name : subdirs) {
1089 TDirectory* sub = dynamic_cast<TDirectory*>(dir->Get(name));
1090 if (sub) ClearInMemoryObjects(sub);
1091 }
1092
1093 // Clear this directory's in-memory object list
1094 // "nodelete" option: remove from list but don't delete the TKey entries
1095 TList* list = dir->GetList();
1096 if (list) {
1097 list->Clear("nodelete");
1098 }
1099 }
1100
1101 public:
1102 void Close() {
1103
1104 if (fRootFile) {
1105 // Step 1: Write all trees explicitly
1106 for (auto iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++) {
1107 if (!iter->second.empty() && iter->second.front()) {
1108 TTree* tree = iter->second.front()->GetTree();
1109 if (tree && tree->GetEntries() > 0) {
1110 tree->Write();
1111 }
1112 }
1113 }
1114
1115 // Step 2: Write all in-memory objects (histograms, etc.) to disk
1116 // Use kOverwrite to avoid creating duplicate cycles
1117 fRootFile->Write(0, TObject::kOverwrite);
1118
1119 // Check if we should make the file permanent AFTER writing
1120 // This ensures histograms are on disk and detectable by HasAnyFilled()
1122
1123 // Step 3: CRITICAL FIX for RNTuple histogram duplication
1124 // Clear all in-memory objects from the TFile's directory structure.
1125 // This prevents RNTupleWriter::Close() from re-writing histograms,
1126 // which would create duplicate cycles. The histograms are already
1127 // safely written to disk in Step 2.
1128#ifdef HAS_RNTUPLE_SUPPORT
1129 if (!fNTupleByName.empty()) {
1131 }
1132#endif // HAS_RNTUPLE_SUPPORT
1133 }
1134
1135#ifdef HAS_RNTUPLE_SUPPORT
1136 // Step 4: Close all RNTuples
1137 // Now that in-memory histograms are cleared, the RNTupleWriter destructor
1138 // won't create duplicate histogram cycles when it internally writes to TFile
1139 for (auto& pair : fNTupleByName) {
1140 for (auto& ntuple : pair.second) {
1141 if (ntuple) ntuple->Close();
1142 }
1143 }
1144#endif // HAS_RNTUPLE_SUPPORT
1145
1146 // Step 5: Close the file
1147 if (fRootFile) {
1148 fRootFile->Close();
1149 }
1150
1151 if (fMapFile) fMapFile->Close();
1152 }
1153
1154 // Wrapped functionality
1155 Bool_t cd(const char* path = 0) {
1156 Bool_t status = kTRUE;
1157 if (fMapFile) status &= fMapFile->cd(path);
1158 if (fRootFile) status &= fRootFile->cd(path);
1159 return status;
1160 }
1161
1162 // Wrapped functionality
1163 TDirectory* mkdir(const char* name, const char* title = "") {
1164 // TMapFile has no support for mkdir
1165 if (fRootFile) return fRootFile->mkdir(name, title);
1166 else return 0;
1167 }
1168
1169 // Wrapped functionality
1170 Int_t Write(const char* name = 0, Int_t option = 0, Int_t bufsize = 0) {
1171 Int_t retval = 0;
1172 // TMapFile has no support for Write
1173 if (fRootFile) retval = fRootFile->Write(name, option, bufsize);
1174 return retval;
1175 }
1176
1177
1178 private:
1179
1180 /// Private default constructor
1182
1183
1184 /// ROOT file
1186
1187 /// ROOT files dir
1189 /// Default ROOT files dir
1190 static std::string fDefaultRootFileDir;
1191
1192 /// ROOT file stem
1194 /// Default ROOT file stem
1195 static std::string fDefaultRootFileStem;
1196
1197 /// While the file is open, give it a temporary filename. Perhaps
1198 /// change to a permanent name when closing the file.
1202
1203 /// Search for non-empty trees or histograms in the file
1204 Bool_t HasAnyFilled(void);
1205 Bool_t HasAnyFilled(TDirectory* d);
1206
1207 /// Map file
1208 TMapFile* fMapFile;
1218
1219
1220
1221 private:
1222
1223 /// List of excluded trees
1224 std::vector< TPRegexp > fDisabledTrees;
1225 std::vector< TPRegexp > fDisabledHistos;
1226
1227 /// Add regexp to list of disabled trees names
1228 void DisableTree(const TString& regexp) {
1229 fDisabledTrees.push_back(regexp);
1230 }
1231 /// Does this tree name match a disabled tree name?
1232 bool IsTreeDisabled(const std::string& name) {
1233 for (size_t i = 0; i < fDisabledTrees.size(); i++)
1234 if (fDisabledTrees.at(i).Match(name)) return true;
1235 return false;
1236 }
1237 /// Add regexp to list of disabled histogram directories
1238 void DisableHisto(const TString& regexp) {
1239 fDisabledHistos.push_back(regexp);
1240 }
1241 /// Does this histogram directory match a disabled histogram directory?
1242 bool IsHistoDisabled(const std::string& name) {
1243 for (size_t i = 0; i < fDisabledHistos.size(); i++)
1244 if (fDisabledHistos.at(i).Match(name)) return true;
1245 return false;
1246 }
1247
1248
1249 private:
1250
1251 /// Tree names, addresses, and types
1252 std::map< const std::string, std::vector<QwRootTree*> > fTreeByName;
1253 std::map< const void* , std::vector<QwRootTree*> > fTreeByAddr;
1254 std::map< const std::type_index , std::vector<QwRootTree*> > fTreeByType;
1255 // ... Are type_index objects really unique? Let's hope so.
1256
1257#ifdef HAS_RNTUPLE_SUPPORT
1258 /// RNTuple names, addresses, and types
1259 std::map< const std::string, std::vector<QwRootNTuple*> > fNTupleByName;
1260 std::map< const void* , std::vector<QwRootNTuple*> > fNTupleByAddr;
1261 std::map< const std::type_index , std::vector<QwRootNTuple*> > fNTupleByType;
1262
1263 /// RNTuple support flag
1264 Bool_t fEnableRNTuples;
1265#endif // HAS_RNTUPLE_SUPPORT
1266
1267 /// Is a tree registered for this name
1268 bool HasTreeByName(const std::string& name) {
1269 if (fTreeByName.count(name) == 0) return false;
1270 else return true;
1271 }
1272 /// Is a tree registered for this type
1273 template < class T >
1274 bool HasTreeByType(const T& object) {
1275 const std::type_index type = typeid(object);
1276 if (fTreeByType.count(type) == 0) return false;
1277 else return true;
1278 }
1279 /// Is a tree registered for this object
1280 template < class T >
1281 bool HasTreeByAddr(const T& object) {
1282 const void* addr = static_cast<const void*>(&object);
1283 if (fTreeByAddr.count(addr) == 0) return false;
1284 else return true;
1285 }
1286
1287#ifdef HAS_RNTUPLE_SUPPORT
1288 /// Is an RNTuple registered for this name
1289 bool HasNTupleByName(const std::string& name) {
1290 if (fNTupleByName.count(name) == 0) return false;
1291 else return true;
1292 }
1293 /// Is an RNTuple registered for this type
1294 template < class T >
1295 bool HasNTupleByType(const T& object) {
1296 const std::type_index type = typeid(object);
1297 if (fNTupleByType.count(type) == 0) return false;
1298 else return true;
1299 }
1300 /// Is an RNTuple registered for this object
1301 template < class T >
1302 bool HasNTupleByAddr(const T& object) {
1303 const void* addr = static_cast<const void*>(&object);
1304 if (fNTupleByAddr.count(addr) == 0) return false;
1305 else return true;
1306 }
1307#endif // HAS_RNTUPLE_SUPPORT
1308
1309 /// Directories
1310 std::map< const std::string, TDirectory* > fDirsByName;
1311 std::map< const std::string, std::vector<std::string> > fDirsByType;
1312
1313 /// Is a tree registered for this name
1314 bool HasDirByName(const std::string& name) {
1315 if (fDirsByName.count(name) == 0) return false;
1316 else return true;
1317 }
1318 /// Is a directory registered for this type
1319 template < class T >
1320 bool HasDirByType(const T& object) {
1321 std::string type = typeid(object).name();
1322 if (fDirsByType.count(type) == 0) return false;
1323 else return true;
1324 }
1325
1326
1327 private:
1328
1329 /// Prescaling of events written to tree
1336
1337 /// Maximum tree size
1338 static const Long64_t kMaxTreeSize;
1339 static const Int_t kMaxMapFileSize;
1340};
1341
1342/**
1343 * Construct the indices from one tree to another tree, and optionally in reverse as well.
1344 * @param from Name of tree where index will be created
1345 * @param to Name of tree to which index will point
1346 * @param reverse Flag to create indices in both direction
1347 */
1348inline void QwRootFile::ConstructIndices(const std::string& from, const std::string& to, bool reverse)
1349{
1350 // Return if we do not want this tree information
1351 if (IsTreeDisabled(from)) return;
1352 if (IsTreeDisabled(to)) return;
1353
1354 // If the trees are defined
1355 if (fTreeByName.count(from) > 0 && fTreeByName.count(to) > 0) {
1356
1357 // Construct index from the first tree to the second tree
1358 fTreeByName[from].front()->ConstructIndexTo(fTreeByName[to].front());
1359
1360 // Construct index from the second tree back to the first tree
1361 if (reverse)
1362 fTreeByName[to].front()->ConstructIndexTo(fTreeByName[from].front());
1363 }
1364}
1365
1366/**
1367 * Construct the tree branches of a generic object
1368 * @param name Name for tree
1369 * @param desc Description for tree
1370 * @param object Subsystem array
1371 * @param prefix Prefix for the tree
1372 */
1373template < class T >
1375 const std::string& name,
1376 const std::string& desc,
1377 T& object,
1378 const std::string& prefix)
1379{
1380 // Return if we do not want this tree information
1381 if (IsTreeDisabled(name)) return;
1382
1383 // Pointer to new tree
1384 QwRootTree* tree = 0;
1385
1386 // If the tree does not exist yet, create it
1387 if (fTreeByName.count(name) == 0) {
1388
1389 // Go to top level directory
1390
1391 this->cd();
1392
1393 // New tree with name, description, object, prefix
1394 tree = new QwRootTree(name, desc, object, prefix);
1395
1396 // Settings only relevant for new trees
1397 if (name == "evt")
1399 else if (name == "mul")
1401
1402 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,26,00)
1403 tree->SetAutoFlush(fAutoFlush);
1404 #endif
1405 tree->SetAutoSave(fAutoSave);
1408
1409 if (fCircularBufferSize > 0)
1411
1412 } else {
1413
1414 // New tree based on existing tree
1415 tree = new QwRootTree(fTreeByName[name].front(), object, prefix);
1416 }
1417
1418 // Add the branches to the list of trees by name, object, type
1419 const void* addr = static_cast<const void*>(&object);
1420 const std::type_index type = typeid(object);
1421 fTreeByName[name].push_back(tree);
1422 fTreeByAddr[addr].push_back(tree);
1423 fTreeByType[type].push_back(tree);
1424}
1425
1426
1427/**
1428 * Fill the tree branches of a generic object by name
1429 * @param name Name for tree
1430 * @param object Subsystem array
1431 */
1432template < class T >
1434 const std::string& name,
1435 const T& object)
1436{
1437 // If this name has no registered trees
1438 if (! HasTreeByName(name)) return;
1439 // If this type has no registered trees
1440 if (! HasTreeByType(object)) return;
1441
1442 // Get the address of the object
1443 const void* addr = static_cast<const void*>(&object);
1444
1445 // Fill the trees with the correct address
1446 for (size_t tree = 0; tree < fTreeByAddr[addr].size(); tree++) {
1447 if (fTreeByAddr[addr].at(tree)->GetName() == name) {
1448 fTreeByAddr[addr].at(tree)->FillTreeBranches(object);
1449 }
1450 }
1451}
1452
1453
1454/**
1455 * Fill the tree branches of a generic object by type only
1456 * @param object Subsystem array
1457 */
1458template < class T >
1460 const T& object)
1461{
1462 // If this address has no registered trees
1463 if (! HasTreeByAddr(object)) return;
1464
1465 // Get the address of the object
1466 const void* addr = static_cast<const void*>(&object);
1467
1468 // Fill the trees with the correct address
1469 for (size_t tree = 0; tree < fTreeByAddr[addr].size(); tree++) {
1470 fTreeByAddr[addr].at(tree)->FillTreeBranches(object);
1471 }
1472}
1473
1474
1475#ifdef HAS_RNTUPLE_SUPPORT
1476/**
1477 * Construct the RNTuple fields of a generic object
1478 * @param name Name for RNTuple
1479 * @param desc Description for RNTuple
1480 * @param object Subsystem array
1481 * @param prefix Prefix for the RNTuple
1482 */
1483template < class T >
1484void QwRootFile::ConstructNTupleFields(
1485 const std::string& name,
1486 const std::string& desc,
1487 T& object,
1488 const std::string& prefix)
1489{
1490 // Return if RNTuples are disabled
1491 if (!fEnableRNTuples) return;
1492
1493 // Pointer to new RNTuple
1494 QwRootNTuple* ntuple = 0;
1495
1496 // If the RNTuple does not exist yet, create it
1497 if (fNTupleByName.count(name) == 0) {
1498
1499 // New RNTuple with name, description, object, prefix
1500 ntuple = new QwRootNTuple(name, desc, object, prefix);
1501
1502 // Set compression settings before initializing writer
1503 ntuple->fRNTupleCompressionAlgorithm = fRNTupleCompressionAlgorithm;
1504 ntuple->fRNTupleCompressionLevel = fRNTupleCompressionLevel;
1505
1506 // Initialize the writer with our file
1507 ntuple->InitializeWriter(fRootFile);
1508
1509 // Settings only relevant for new RNTuples
1510 if (name == "evt")
1511 ntuple->SetPrescaling(fNumMpsEventsToSave, fNumMpsEventsToSkip);
1512 else if (name == "mul")
1513 ntuple->SetPrescaling(fNumHelEventsToSave, fNumHelEventsToSkip);
1514
1515 } else {
1516
1517 // For simplicity, don't support multiple RNTuples with same name yet
1518 QwError << "Cannot create duplicate RNTuple: " << name << QwLog::endl;
1519 return;
1520 }
1521
1522 // Add the fields to the list of RNTuples by name, object, type
1523 const void* addr = static_cast<const void*>(&object);
1524 const std::type_index type = typeid(object);
1525 fNTupleByName[name].push_back(ntuple);
1526 fNTupleByAddr[addr].push_back(ntuple);
1527 fNTupleByType[type].push_back(ntuple);
1528}
1529
1530
1531/**
1532 * Fill the RNTuple fields of a generic object by name
1533 * @param name Name for RNTuple
1534 * @param object Subsystem array
1535 */
1536template < class T >
1537void QwRootFile::FillNTupleFields(
1538 const std::string& name,
1539 const T& object)
1540{
1541 // If this name has no registered RNTuples
1542 if (! HasNTupleByName(name)) return;
1543 // If this type has no registered RNTuples
1544 if (! HasNTupleByType(object)) return;
1545
1546 // Get the address of the object
1547 const void* addr = static_cast<const void*>(&object);
1548
1549 // Fill the RNTuples with the correct address
1550 for (size_t ntuple = 0; ntuple < fNTupleByAddr[addr].size(); ntuple++) {
1551 if (fNTupleByAddr[addr].at(ntuple)->GetName() == name) {
1552 fNTupleByAddr[addr].at(ntuple)->FillNTupleFields(object);
1553 }
1554 }
1555}
1556
1557
1558/**
1559 * Fill the RNTuple fields of a generic object by type only
1560 * @param object Subsystem array
1561 */
1562template < class T >
1563void QwRootFile::FillNTupleFields(
1564 const T& object)
1565{
1566 // If this address has no registered RNTuples
1567 if (! HasNTupleByAddr(object)) return;
1568
1569 // Get the address of the object
1570 const void* addr = static_cast<const void*>(&object);
1571
1572 // Fill the RNTuples with the correct address
1573 for (size_t ntuple = 0; ntuple < fNTupleByAddr[addr].size(); ntuple++) {
1574 fNTupleByAddr[addr].at(ntuple)->FillNTupleFields(object);
1575 }
1576}
1577#endif // HAS_RNTUPLE_SUPPORT
1578
1579
1580/**
1581 * Construct the objects directory of a generic object
1582 * @param name Name for objects directory
1583 * @param object Subsystem array
1584 */
1585template < class T >
1586void QwRootFile::ConstructObjects(const std::string& name, T& object)
1587{
1588 // Create the objects in a directory
1589 if (fRootFile) {
1590 std::string type = typeid(object).name();
1591 fDirsByName[name] =
1592 fRootFile->GetDirectory(("/" + name).c_str()) ?
1593 fRootFile->GetDirectory(("/" + name).c_str()) :
1594 fRootFile->GetDirectory("/")->mkdir(name.c_str());
1595 fDirsByType[type].push_back(name);
1596 object.ConstructObjects(fDirsByName[name]);
1597 }
1598
1599 // No support for directories in a map file
1600 if (fMapFile) {
1601 QwMessage << "QwRootFile::ConstructObjects::detectors address "
1602 << &object
1603 << " and its name " << name
1604 << QwLog::endl;
1605
1606 std::string type = typeid(object).name();
1607 fDirsByName[name] = fMapFile->GetDirectory()->mkdir(name.c_str());
1608 fDirsByType[type].push_back(name);
1609 object.ConstructObjects();
1610 }
1611}
1612
1613
1614/**
1615 * Construct the histogram of a generic object
1616 * @param name Name for histogram directory
1617 * @param object Subsystem array
1618 */
1619template < class T >
1620void QwRootFile::ConstructHistograms(const std::string& name, T& object)
1621{
1622 // Return if we do not want this histogram information
1623 if (IsHistoDisabled(name)) return;
1624
1625 // Create the histograms in a directory
1626 if (fRootFile) {
1627 std::string type = typeid(object).name();
1628 fDirsByName[name] =
1629 fRootFile->GetDirectory(("/" + name).c_str()) ?
1630 fRootFile->GetDirectory(("/" + name).c_str()) :
1631 fRootFile->GetDirectory("/")->mkdir(name.c_str());
1632 fDirsByType[type].push_back(name);
1633
1634 object.ConstructHistograms(fDirsByName[name]);
1635 }
1636
1637 // No support for directories in a map file
1638 if (fMapFile) {
1639 QwMessage << "QwRootFile::ConstructHistograms::detectors address "
1640 << &object
1641 << " and its name " << name
1642 << QwLog::endl;
1643
1644 std::string type = typeid(object).name();
1645 fDirsByName[name] = fMapFile->GetDirectory()->mkdir(name.c_str());
1646 fDirsByType[type].push_back(name);
1647 //object.ConstructHistograms(fDirsByName[name]);
1648 object.ConstructHistograms();
1649 }
1650}
1651
1652
1653template < class T >
1654Int_t QwRootFile::WriteParamFileList(const TString &name, T& object)
1655{
1656 Int_t retval = 0;
1657 if (fRootFile) {
1658 TList *param_list = (TList*) fRootFile->FindObjectAny(name);
1659 if (not param_list) {
1660 retval = fRootFile->WriteObject(object.GetParamFileNameList(name), name);
1661 }
1662 }
1663 return retval;
1664}
#define BRANCH_VECTOR_MAX_SIZE
Definition QwRootFile.h:44
#define QwError
Predefined log drain for errors.
Definition QwLog.h:39
#define QwMessage
Predefined log drain for regular messages.
Definition QwLog.h:49
An options class which parses command line, config file and environment.
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:874
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:934
TString fRootFileStem
ROOT file stem.
void Print()
Int_t FillTree(const std::string &name)
Fill the tree with name.
Definition QwRootFile.h:974
TFile * fRootFile
ROOT file.
Bool_t IsMapFile() const
Is the map file active?
Definition QwRootFile.h:876
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.
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:868
static void SetDefaultRootFileDir(const std::string &dir)
Set default ROOT files dir.
Definition QwRootFile.h:864
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:917
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:980
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:968
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
Int_t fRNTupleCompressionLevel
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
Int_t fRNTupleCompressionAlgorithm