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;
20
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 // Prescale peek. Gathering the branch vector (FillTreeVector) copies
479 // every channel value and is a sizeable fraction of the per-event cost.
480 // When the tree is prescaled, Fill() will discard this event anyway, so
481 // skip the gather as well. This mirrors Fill()'s computation exactly
482 // (Fill() does ++fCurrentEvent then %= fNumEventsCycle), so the gather
483 // and the write always agree on which events are kept.
484 if (fNumEventsCycle > 0) {
485 UInt_t predicted = (fCurrentEvent + 1) % fNumEventsCycle;
486 if (predicted > fNumEventsToSave) return;
487 }
488 if (typeid(object).name() == fType) {
489 // Fill the branch vector
490 object.FillTreeVector(fVector);
491 } else {
492 QwError << "Attempting to fill tree vector for type " << fType << " with "
493 << "object of type " << typeid(object).name() << QwLog::endl;
494 exit(-1);
495 }
496 }
497
498 Long64_t AutoSave(Option_t *option){
499 return fTree->AutoSave(option);
500 }
501
502 /// Fill the tree
503 Int_t Fill() {
505
506 // Tree prescaling
507 if (fNumEventsCycle > 0) {
510 return 0;
511 }
512
513 // Fill the tree
514 Int_t retval = fTree->Fill();
515 // Check for errors
516 if (retval < 0) {
517 QwError << "Writing tree failed! Check disk space or quota." << QwLog::endl;
518 exit(retval);
519 }
520 return retval;
521 }
522
523
524 /// Print the tree name and description
525 void Print() const {
526 QwMessage << GetName() << ", " << GetType();
527 if (fPrefix != "")
528 QwMessage << " (prefix " << GetPrefix() << ")";
530 }
531
532 /// Get the tree pointer for low level operations
533 TTree* GetTree() const { return fTree; };
534
535
536 friend class QwRootFile;
537
538 private:
539
540 /// Tree pointer
541 TTree* fTree;
542 /// Vector of leaves
544
545
546 /// Name, description
547 const std::string fName;
548 const std::string fDesc;
549 const std::string fPrefix;
550
551 /// Get the name of the tree
552 const std::string& GetName() const { return fName; };
553 /// Get the description of the tree
554 const std::string& GetDesc() const { return fDesc; };
555 /// Get the description of the tree
556 const std::string& GetPrefix() const { return fPrefix; };
557
558
559 /// Object type
560 std::string fType;
561
562 /// Get the object type
563 std::string GetType() const { return fType; };
564
565
566 /// Tree prescaling parameters
571
572 /// Set tree prescaling parameters
573 void SetPrescaling(UInt_t num_to_save, UInt_t num_to_skip) {
574 fNumEventsToSave = num_to_save;
575 fNumEventsToSkip = num_to_skip;
577 }
578
579
580 /// Maximum tree size, autoflush and autosave
581 Long64_t fMaxTreeSize;
582 Long64_t fAutoFlush;
583 Long64_t fAutoSave;
585
586 /// Set maximum tree size
587 void SetMaxTreeSize(Long64_t maxsize = 1900000000) {
588 fMaxTreeSize = maxsize;
589 if (fTree) fTree->SetMaxTreeSize(maxsize);
590 }
591
592 /// Set autoflush size
593 void SetAutoFlush(Long64_t autoflush = 30000000) {
594 fAutoFlush = autoflush;
595 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,26,00)
596 if (fTree) fTree->SetAutoFlush(autoflush);
597 #endif
598 }
599
600 /// Set autosave size
601 void SetAutoSave(Long64_t autosave = 300000000) {
602 fAutoSave = autosave;
603 if (fTree) fTree->SetAutoSave(autosave);
604 }
605
606 /// Set basket size
607 void SetBasketSize(Int_t basketsize = 16000) {
608 fBasketSize = basketsize;
609 if (fTree) fTree->SetBasketSize("*",basketsize);
610 }
611
612 //Set circular buffer size for the memory resident tree
613 void SetCircular(Long64_t buff = 100000) {
614 if (fTree) fTree->SetCircular(buff);
615 }
616};
617
618#ifdef HAS_RNTUPLE_SUPPORT
619/**
620 * \class QwRootNTuple
621 * \ingroup QwAnalysis
622 * \brief A wrapper class for a ROOT RNTuple
623 *
624 * This class provides the functionality to write to ROOT RNTuples using a vector
625 * of doubles, similar to QwRootTree but using the newer RNTuple format.
626 */
627class QwRootNTuple {
628
629 public:
630
631 /// Constructor with name and description
632 QwRootNTuple(const std::string& name, const std::string& desc, const std::string& prefix = "")
633 : fName(name), fDesc(desc), fPrefix(prefix), fType("type undefined"),
634 fCurrentEvent(0), fNumEventsCycle(0), fNumEventsToSave(0), fNumEventsToSkip(0) {
635 // Create RNTuple model
636 fModel = ROOT::RNTupleModel::Create();
637 }
638
639 /// Constructor with name, description, and object
640 template < class T >
641 QwRootNTuple(const std::string& name, const std::string& desc, T& object, const std::string& prefix = "")
642 : fName(name), fDesc(desc), fPrefix(prefix), fType("type undefined"),
643 fCurrentEvent(0), fNumEventsCycle(0), fNumEventsToSave(0), fNumEventsToSkip(0) {
644 // Create RNTuple model
645 fModel = ROOT::RNTupleModel::Create();
646
647 // Construct fields and vector
648 ConstructFieldsAndVector(object);
649 }
650
651 /// Destructor
652 virtual ~QwRootNTuple() {
653 Close();
654 }
655
656 /// Close and finalize the RNTuple writer
657 void Close() {
658 if (fWriter) {
659 // Explicitly commit any remaining data and close the writer
660 // This ensures all data is written to the file before destruction
661 fWriter.reset(); // This calls the destructor which should finalize the RNTuple
662 }
663 }
664
665 private:
666
667 /// Construct the fields and vector for generic objects
668 template < class T >
669 void ConstructFieldsAndVector(T& object) {
670 // Reserve space for the field vector
671 fVector.reserve(BRANCH_VECTOR_MAX_SIZE);
672
673 // Associate fields with vector - now using shared field pointers
674 TString prefix = Form("%s", fPrefix.c_str());
675 object.ConstructNTupleAndVector(fModel, prefix, fVector, fFieldPtrs);
676
677 // Store the type of object
678 fType = typeid(object).name();
679
680 // Check memory reservation
681 if (fVector.size() > BRANCH_VECTOR_MAX_SIZE) {
682 QwError << "The field vector is too large: " << fVector.size() << " fields! "
683 << "The maximum size is " << BRANCH_VECTOR_MAX_SIZE << "."
684 << QwLog::endl;
685 exit(-1);
686 }
687
688 // Shrink memory reservation
689 fVector.shrink_to_fit();
690 }
691
692 public:
693
694 /// Initialize the RNTuple writer with a file
695 void InitializeWriter(TFile* file) {
696 if (!fModel) {
697 QwError << "RNTuple model not created for " << fName << QwLog::endl;
698 return;
699 }
700
701 // Before creating the writer, ensure all fields are added to the model
702 if (fVector.empty()) {
703 QwError << "No fields defined in RNTuple model for " << fName << QwLog::endl;
704 return;
705 }
706
707 try {
708 // Create write options with user-specified compression settings
709 ROOT::RNTupleWriteOptions options;
710 options.SetCompression(
711 static_cast<ROOT::RCompressionSetting::EAlgorithm::EValues>(fRNTupleCompressionAlgorithm),
712 fRNTupleCompressionLevel
713 );
714
715 // Create the writer with the model (transfers ownership)
716 // Use Append to add RNTuple to existing TFile
717 fWriter = ROOT::RNTupleWriter::Append(std::move(fModel), fName, *file, options);
718
719 const char* algo_name = "UNKNOWN";
720 switch(fRNTupleCompressionAlgorithm) {
721 case 1: algo_name = "ZLIB"; break;
722 case 2: algo_name = "LZMA"; break;
723 case 4: algo_name = "LZ4"; break;
724 case 5: algo_name = "ZSTD"; break;
725 }
726 QwMessage << "Created RNTuple '" << fName << "' with " << algo_name
727 << " compression (level " << fRNTupleCompressionLevel << ") in file "
728 << file->GetName() << QwLog::endl;
729
730 } catch (const std::exception& e) {
731 QwError << "Failed to create RNTuple writer for '" << fName << "': " << e.what() << QwLog::endl;
732 }
733 }
734
735 /// Fill the fields for generic objects
736 template < class T >
737 void FillNTupleFields(const T& object) {
738 if (typeid(object).name() == fType) {
739 // Fill the field vector
740 object.FillNTupleVector(fVector);
741
742 // Use the shared field pointers which remain valid
743 if (fWriter) {
744 for (size_t i = 0; i < fVector.size() && i < fFieldPtrs.size(); ++i) {
745 if (fFieldPtrs[i]) {
746 *(fFieldPtrs[i]) = fVector[i];
747 }
748 }
749
750 // CRITICAL: Actually commit the data to the RNTuple
751 fWriter->Fill();
752
753 // Update event counter
754 fCurrentEvent++;
755 // RNTuple prescaling
756 if (fNumEventsCycle > 0) {
757 fCurrentEvent %= fNumEventsCycle;
758 }
759 } else {
760 QwError << "RNTuple writer not initialized for " << fName << QwLog::endl;
761 }
762 } else {
763 QwError << "Attempting to fill RNTuple vector for type " << fType << " with "
764 << "object of type " << typeid(object).name() << QwLog::endl;
765 exit(-1);
766 }
767 }
768
769 /// Fill the RNTuple (called by FillTree wrapper methods)
770 void Fill() {
771 // This method is now called indirectly - the actual filling happens in FillNTupleFields
772 // Just here for compatibility with the tree interface
773 }
774
775 /// Get the name of the RNTuple
776 const std::string& GetName() const { return fName; }
777 /// Get the description of the RNTuple
778 const std::string& GetDesc() const { return fDesc; }
779 /// Get the prefix of the RNTuple
780 const std::string& GetPrefix() const { return fPrefix; }
781 /// Get the object type
782 std::string GetType() const { return fType; }
783
784 /// Set prescaling parameters
785 void SetPrescaling(UInt_t num_to_save, UInt_t num_to_skip) {
786 fNumEventsToSave = num_to_save;
787 fNumEventsToSkip = num_to_skip;
788 fNumEventsCycle = fNumEventsToSave + fNumEventsToSkip;
789 }
790
791 /// Print the RNTuple name and description
792 void Print() const {
793 QwMessage << GetName() << ", " << GetType();
794 if (fPrefix != "")
795 QwMessage << " (prefix " << GetPrefix() << ")";
797 }
798
799 private:
800
801 /// RNTuple model and writer
802 std::unique_ptr<ROOT::RNTupleModel> fModel;
803 std::unique_ptr<ROOT::RNTupleWriter> fWriter;
804
805 /// Vector of values and shared field pointers (for RNTuple)
806 std::vector<Double_t> fVector;
807 std::vector<std::shared_ptr<Double_t>> fFieldPtrs;
808
809 /// Name, description, prefix
810 const std::string fName;
811 const std::string fDesc;
812 const std::string fPrefix;
813
814 /// Object type
815 std::string fType;
816
817 /// RNTuple prescaling parameters
818 UInt_t fCurrentEvent;
819 UInt_t fNumEventsCycle;
820 UInt_t fNumEventsToSave;
821 UInt_t fNumEventsToSkip;
822
823 /// RNTuple compression settings
824 Int_t fRNTupleCompressionAlgorithm;
825 Int_t fRNTupleCompressionLevel;
826
827 friend class QwRootFile;
828};
829#endif // HAS_RNTUPLE_SUPPORT
830
831/**
832 * \class QwRootFile
833 * \ingroup QwAnalysis
834 * \brief A wrapper class for a ROOT file or memory mapped file
835 *
836 * This class functions as a wrapper around a ROOT TFile or a TMapFile. The
837 * common inheritance of both is only TObject, so there is a lot that we have
838 * to wrap (rather than inherit). Theoretically you could have both a TFile
839 * and a TMapFile represented by an object of this class at the same time, but
840 * that is untested.
841 *
842 * The functionality of writing to the file is done by templated functions.
843 * The objects that are passed to these functions have to provide the following
844 * functions:
845 * <ul>
846 * <li>ConstructHistograms, FillHistograms
847 * <li>ConstructBranchAndVector, FillTreeVector
848 * </ul>
849 *
850 * The class keeps track of the registered tree names, and the types of objects
851 * that have branches constructed in those trees (via QwRootTree). In most
852 * cases it should be possible to just call FillTreeBranches with only the object,
853 * although in rare cases this could be ambiguous.
854 *
855 * The proper way to register a tree is by either calling ConstructTreeBranches
856 * of NewTree first. Then FillTreeBranches will fill the vector, and FillTree
857 * will actually fill the tree. FillTree should be called only once.
858 */
860
861 public:
862
863 /// \brief Constructor with run label
864 QwRootFile(const TString& run_label);
865 /// \brief Destructor
866 virtual ~QwRootFile();
867
868
869 /// \brief Define the configuration options
870 static void DefineOptions(QwOptions &options);
871 /// \brief Process the configuration options
872 void ProcessOptions(QwOptions &options);
873 /// \brief Set default ROOT files dir
874 static void SetDefaultRootFileDir(const std::string& dir) {
876 }
877 /// \brief Set default ROOT file stem
878 static void SetDefaultRootFileStem(const std::string& stem) {
880 }
881
882
883 /// Is the ROOT file active?
884 Bool_t IsRootFile() const { return (fRootFile); };
885 /// Is the map file active?
886 Bool_t IsMapFile() const { return (fMapFile); };
887
888 /// \brief Construct indices from one tree to another tree
889 void ConstructIndices(const std::string& from, const std::string& to, bool reverse = true);
890
891 /// \brief Construct the tree branches of a generic object
892 template < class T >
893 void ConstructTreeBranches(const std::string& name, const std::string& desc, T& object, const std::string& prefix = "");
894 /// \brief Fill the tree branches of a generic object by tree name
895 template < class T >
896 void FillTreeBranches(const std::string& name, const T& object);
897 /// \brief Fill the tree branches of a generic object by type only
898 template < class T >
899 void FillTreeBranches(const T& object);
900
901#ifdef HAS_RNTUPLE_SUPPORT
902 /// \brief Construct the RNTuple fields of a generic object
903 template < class T >
904 void ConstructNTupleFields(const std::string& name, const std::string& desc, T& object, const std::string& prefix = "");
905 /// \brief Fill the RNTuple fields of a generic object by name
906 template < class T >
907 void FillNTupleFields(const std::string& name, const T& object);
908 /// \brief Fill the RNTuple fields of a generic object by type only
909 template < class T >
910 void FillNTupleFields(const T& object);
911#endif // HAS_RNTUPLE_SUPPORT
912
913
914 template < class T >
915 Int_t WriteParamFileList(const TString& name, T& object);
916
917
918 /// \brief Construct the histograms of a generic object
919 template < class T >
920 void ConstructObjects(const std::string& name, T& object);
921
922 /// \brief Construct the histograms of a generic object
923 template < class T >
924 void ConstructHistograms(const std::string& name, T& object);
925 /// Fill histograms of the subsystem array
926 template < class T >
927 void FillHistograms(T& object) {
928 // Update regularly
929 static Int_t update_count = 0;
930 update_count++;
931 if ((fUpdateInterval > 0) && ( update_count % fUpdateInterval == 0)) Update();
932
933 // Histogram fill prescaling. Physics is processed every event, but the
934 // (relatively expensive) histogram fill is performed only on every Nth
935 // event. This thins the live monitoring output to gain throughput; the
936 // visual refresh rate is unaffected because the displayed histograms
937 // simply accumulate slightly fewer entries.
938 if (fHistoFillPrescale > 1) {
939 if (++fHistoFillCount % fHistoFillPrescale != 0) return;
940 }
941
942 // Debug directory registration
943 std::string type = typeid(object).name();
944 bool hasDir = HasDirByType(object);
945
946 if (! hasDir) return;
947 // Fill histograms
948 object.FillHistograms();
949 }
950
951
952 /// Create a new tree with name and description
953 void NewTree(const std::string& name, const std::string& desc) {
954 if (IsTreeDisabled(name)) return;
955 this->cd();
956 QwRootTree *tree = 0;
957 if (! HasTreeByName(name)) {
958 tree = new QwRootTree(name,desc);
959 } else {
960 tree = new QwRootTree(fTreeByName[name].front());
961 }
962 fTreeByName[name].push_back(tree);
963 }
964
965#ifdef HAS_RNTUPLE_SUPPORT
966 /// Create a new RNTuple with name and description
967 void NewNTuple(const std::string& name, const std::string& desc) {
968 if (IsTreeDisabled(name) || !fEnableRNTuples) return;
969 QwRootNTuple *ntuple = 0;
970 if (! HasNTupleByName(name)) {
971 ntuple = new QwRootNTuple(name, desc);
972 // Set compression settings before initializing writer
973 ntuple->fRNTupleCompressionAlgorithm = fRNTupleCompressionAlgorithm;
974 ntuple->fRNTupleCompressionLevel = fRNTupleCompressionLevel;
975 // Initialize the writer with our file
976 ntuple->InitializeWriter(fRootFile);
977 } else {
978 // For simplicity, don't support copying existing RNTuples yet
979 QwError << "Cannot create duplicate RNTuple: " << name << QwLog::endl;
980 return;
981 }
982 fNTupleByName[name].push_back(ntuple);
983 }
984#endif // HAS_RNTUPLE_SUPPORT
985
986 /// Get the tree with name
987 TTree* GetTree(const std::string& name) {
988 if (! HasTreeByName(name)) return 0;
989 else return fTreeByName[name].front()->GetTree();
990 }
991
992 /// Fill the tree with name
993 Int_t FillTree(const std::string& name) {
994 if (! HasTreeByName(name)) return 0;
995 else return fTreeByName[name].front()->Fill();
996 }
997
998 /// Fill all registered trees
999 Int_t FillTrees() {
1000 // Loop over all registered tree names
1001 Int_t retval = 0;
1002 std::map< const std::string, std::vector<QwRootTree*> >::iterator iter;
1003 for (iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++) {
1004 retval += iter->second.front()->Fill();
1005 }
1006 return retval;
1007 }
1008
1009#ifdef HAS_RNTUPLE_SUPPORT
1010 /// Fill the RNTuple with name
1011 void FillNTuple(const std::string& name) {
1012 if (HasNTupleByName(name)) {
1013 fNTupleByName[name].front()->Fill();
1014 }
1015 }
1016#endif // HAS_RNTUPLE_SUPPORT
1017
1018#ifdef HAS_RNTUPLE_SUPPORT
1019 /// Fill all registered RNTuples
1020 void FillNTuples() {
1021 // Loop over all registered RNTuple names
1022 std::map< const std::string, std::vector<QwRootNTuple*> >::iterator iter;
1023 for (iter = fNTupleByName.begin(); iter != fNTupleByName.end(); iter++) {
1024 iter->second.front()->Fill();
1025 }
1026 }
1027#endif // HAS_RNTUPLE_SUPPORT
1028
1029 /// Print registered trees
1030 void PrintTrees() const {
1031 QwMessage << "Trees: " << QwLog::endl;
1032 // Loop over all registered tree names
1033 std::map< const std::string, std::vector<QwRootTree*> >::const_iterator iter;
1034 for (iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++) {
1035 QwMessage << iter->first << ": " << iter->second.size()
1036 << " objects registered" << QwLog::endl;
1037 // Loop over all registered objects for this tree
1038 std::vector<QwRootTree*>::const_iterator tree;
1039 for (tree = iter->second.begin(); tree != iter->second.end(); tree++) {
1040 (*tree)->Print();
1041 }
1042 }
1043 }
1044 /// Print registered histogram directories
1045 void PrintDirs() const {
1046 QwMessage << "Dirs: " << QwLog::endl;
1047 // Loop ove rall registered directories
1048 std::map< const std::string, TDirectory* >::const_iterator iter;
1049 for (iter = fDirsByName.begin(); iter != fDirsByName.end(); iter++) {
1050 QwMessage << iter->first << QwLog::endl;
1051 }
1052 }
1053
1054
1055 /// Write any object to the ROOT file (only valid for TFile)
1056 template < class T >
1057 Int_t WriteObject(const T* obj, const char* name, Option_t* option = "", Int_t bufsize = 0) {
1058 Int_t retval = 0;
1059 // TMapFile has no support for WriteObject
1060 if (fRootFile) retval = fRootFile->WriteObject(obj,name,option,bufsize);
1061 return retval;
1062 }
1063
1064
1065 // Wrapped functionality
1066 void Update() {
1067 if (fMapFile) {
1068 QwMessage << "TMapFile memory resident size: "
1069 << ((int*)fMapFile->GetBreakval() - (int*)fMapFile->GetBaseAddr()) *
1070 4 / sizeof(int32_t) / 1024 / 1024 << " MiB"
1071 << QwLog::endl;
1072 fMapFile->Update();
1073 }else{
1074 // this option will allow for reading the tree during write
1075 Long64_t nBytes(0);
1076 for (auto iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++)
1077 nBytes += iter->second.front()->AutoSave("SaveSelf");
1078
1079 QwMessage << "TFile saved: "
1080 << nBytes/1000000 << "MB (inaccurate number)" //FIXME this calculation is inaccurate
1081 << QwLog::endl;
1082 }
1083 }
1084 void Print() { if (fMapFile) fMapFile->Print(); if (fRootFile) fRootFile->Print(); }
1085 void ls() { if (fMapFile) fMapFile->ls(); if (fRootFile) fRootFile->ls(); }
1086 void Map() { if (fRootFile) fRootFile->Map(); }
1087
1088 private:
1089 /// Recursively clear in-memory objects from a directory tree.
1090 /// This removes objects from the TDirectory's in-memory list without deleting
1091 /// their on-disk representation (keys). Used to prevent RNTupleWriter::Close()
1092 /// from creating duplicate histogram cycles when it internally calls TFile::Write().
1093 void ClearInMemoryObjects(TDirectory* dir) {
1094 if (!dir) return;
1095
1096 // First, collect subdirectory names
1097 std::vector<TString> subdirs;
1098 TIter next(dir->GetListOfKeys());
1099 TKey* key;
1100 while ((key = (TKey*)next())) {
1101 if (TString(key->GetClassName()) == "TDirectoryFile") {
1102 subdirs.push_back(key->GetName());
1103 }
1104 }
1105
1106 // Recursively clear subdirectories
1107 for (const auto& name : subdirs) {
1108 TDirectory* sub = dynamic_cast<TDirectory*>(dir->Get(name));
1109 if (sub) ClearInMemoryObjects(sub);
1110 }
1111
1112 // Clear this directory's in-memory object list
1113 // "nodelete" option: remove from list but don't delete the TKey entries
1114 TList* list = dir->GetList();
1115 if (list) {
1116 list->Clear("nodelete");
1117 }
1118 }
1119
1120 public:
1121 void Close() {
1122
1123 if (fRootFile) {
1124 // Step 1: Write all trees explicitly
1125 for (auto iter = fTreeByName.begin(); iter != fTreeByName.end(); iter++) {
1126 if (!iter->second.empty() && iter->second.front()) {
1127 TTree* tree = iter->second.front()->GetTree();
1128 if (tree && tree->GetEntries() > 0) {
1129 tree->Write();
1130 }
1131 }
1132 }
1133
1134 // Step 2: Write all in-memory objects (histograms, etc.) to disk
1135 // Use kOverwrite to avoid creating duplicate cycles
1136 fRootFile->Write(0, TObject::kOverwrite);
1137
1138 // Check if we should make the file permanent AFTER writing
1139 // This ensures histograms are on disk and detectable by HasAnyFilled()
1141
1142 // Step 3: CRITICAL FIX for RNTuple histogram duplication
1143 // Clear all in-memory objects from the TFile's directory structure.
1144 // This prevents RNTupleWriter::Close() from re-writing histograms,
1145 // which would create duplicate cycles. The histograms are already
1146 // safely written to disk in Step 2.
1147#ifdef HAS_RNTUPLE_SUPPORT
1148 if (!fNTupleByName.empty()) {
1150 }
1151#endif // HAS_RNTUPLE_SUPPORT
1152 }
1153
1154#ifdef HAS_RNTUPLE_SUPPORT
1155 // Step 4: Close all RNTuples
1156 // Now that in-memory histograms are cleared, the RNTupleWriter destructor
1157 // won't create duplicate histogram cycles when it internally writes to TFile
1158 for (auto& pair : fNTupleByName) {
1159 for (auto& ntuple : pair.second) {
1160 if (ntuple) ntuple->Close();
1161 }
1162 }
1163#endif // HAS_RNTUPLE_SUPPORT
1164
1165 // Step 5: Close the file
1166 if (fRootFile) {
1167 fRootFile->Close();
1168 }
1169
1170 // TMapFile::Close() is NOT idempotent: re-closing in ~QwRootFile()
1171 // crashes on the already-detached mmap descriptor.
1172 if (fMapFile) { fMapFile->Close(); fMapFile = 0; }
1173 }
1174
1175 // Wrapped functionality
1176 Bool_t cd(const char* path = 0) {
1177 Bool_t status = kTRUE;
1178 if (fMapFile) status &= fMapFile->cd(path);
1179 if (fRootFile) status &= fRootFile->cd(path);
1180 return status;
1181 }
1182
1183 // Wrapped functionality
1184 TDirectory* mkdir(const char* name, const char* title = "") {
1185 // TMapFile has no support for mkdir
1186 if (fRootFile) return fRootFile->mkdir(name, title);
1187 else return 0;
1188 }
1189
1190 // Wrapped functionality
1191 Int_t Write(const char* name = 0, Int_t option = 0, Int_t bufsize = 0) {
1192 Int_t retval = 0;
1193 // TMapFile has no support for Write
1194 if (fRootFile) retval = fRootFile->Write(name, option, bufsize);
1195 return retval;
1196 }
1197
1198
1199 private:
1200
1201 /// Private default constructor
1203
1204
1205 /// ROOT file
1207
1208 /// ROOT files dir
1210 /// Default ROOT files dir
1211 static std::string fDefaultRootFileDir;
1212
1213 /// ROOT file stem
1215 /// Default ROOT file stem
1216 static std::string fDefaultRootFileStem;
1217
1218 /// While the file is open, give it a temporary filename. Perhaps
1219 /// change to a permanent name when closing the file.
1223
1224 /// Search for non-empty trees or histograms in the file
1225 Bool_t HasAnyFilled(void);
1226 Bool_t HasAnyFilled(TDirectory* d);
1227
1228 /// Map file
1229 TMapFile* fMapFile;
1231 /// Directory that holds the memory-mapped file (default "/dev/shm" on Linux;
1232 /// override with --mapfile-dir on platforms without /dev/shm, e.g. macOS).
1242
1243
1244
1245 private:
1246
1247 /// List of excluded trees
1248 std::vector< TPRegexp > fDisabledTrees;
1249 std::vector< TPRegexp > fDisabledHistos;
1250
1251 /// Add regexp to list of disabled trees names
1252 void DisableTree(const TString& regexp) {
1253 fDisabledTrees.push_back(regexp);
1254 }
1255 /// Does this tree name match a disabled tree name?
1256 bool IsTreeDisabled(const std::string& name) {
1257 for (size_t i = 0; i < fDisabledTrees.size(); i++)
1258 if (fDisabledTrees.at(i).Match(name)) return true;
1259 return false;
1260 }
1261 /// Add regexp to list of disabled histogram directories
1262 void DisableHisto(const TString& regexp) {
1263 fDisabledHistos.push_back(regexp);
1264 }
1265 /// Does this histogram directory match a disabled histogram directory?
1266 bool IsHistoDisabled(const std::string& name) {
1267 for (size_t i = 0; i < fDisabledHistos.size(); i++)
1268 if (fDisabledHistos.at(i).Match(name)) return true;
1269 return false;
1270 }
1271
1272
1273 private:
1274
1275 /// Tree names, addresses, and types
1276 std::map< const std::string, std::vector<QwRootTree*> > fTreeByName;
1277 std::map< const void* , std::vector<QwRootTree*> > fTreeByAddr;
1278 std::map< const std::type_index , std::vector<QwRootTree*> > fTreeByType;
1279 // ... Are type_index objects really unique? Let's hope so.
1280
1281#ifdef HAS_RNTUPLE_SUPPORT
1282 /// RNTuple names, addresses, and types
1283 std::map< const std::string, std::vector<QwRootNTuple*> > fNTupleByName;
1284 std::map< const void* , std::vector<QwRootNTuple*> > fNTupleByAddr;
1285 std::map< const std::type_index , std::vector<QwRootNTuple*> > fNTupleByType;
1286
1287 /// RNTuple support flag
1288 Bool_t fEnableRNTuples;
1289#endif // HAS_RNTUPLE_SUPPORT
1290
1291 /// Is a tree registered for this name
1292 bool HasTreeByName(const std::string& name) {
1293 if (fTreeByName.count(name) == 0) return false;
1294 else return true;
1295 }
1296 /// Is a tree registered for this type
1297 template < class T >
1298 bool HasTreeByType(const T& object) {
1299 const std::type_index type = typeid(object);
1300 if (fTreeByType.count(type) == 0) return false;
1301 else return true;
1302 }
1303 /// Is a tree registered for this object
1304 template < class T >
1305 bool HasTreeByAddr(const T& object) {
1306 const void* addr = static_cast<const void*>(&object);
1307 if (fTreeByAddr.count(addr) == 0) return false;
1308 else return true;
1309 }
1310
1311#ifdef HAS_RNTUPLE_SUPPORT
1312 /// Is an RNTuple registered for this name
1313 bool HasNTupleByName(const std::string& name) {
1314 if (fNTupleByName.count(name) == 0) return false;
1315 else return true;
1316 }
1317 /// Is an RNTuple registered for this type
1318 template < class T >
1319 bool HasNTupleByType(const T& object) {
1320 const std::type_index type = typeid(object);
1321 if (fNTupleByType.count(type) == 0) return false;
1322 else return true;
1323 }
1324 /// Is an RNTuple registered for this object
1325 template < class T >
1326 bool HasNTupleByAddr(const T& object) {
1327 const void* addr = static_cast<const void*>(&object);
1328 if (fNTupleByAddr.count(addr) == 0) return false;
1329 else return true;
1330 }
1331#endif // HAS_RNTUPLE_SUPPORT
1332
1333 /// Directories
1334 std::map< const std::string, TDirectory* > fDirsByName;
1335 std::map< const std::string, std::vector<std::string> > fDirsByType;
1336
1337 /// Is a tree registered for this name
1338 bool HasDirByName(const std::string& name) {
1339 if (fDirsByName.count(name) == 0) return false;
1340 else return true;
1341 }
1342 /// Is a directory registered for this type
1343 template < class T >
1344 bool HasDirByType(const T& object) {
1345 std::string type = typeid(object).name();
1346 if (fDirsByType.count(type) == 0) return false;
1347 else return true;
1348 }
1349
1350
1351 private:
1352
1353 /// Prescaling of events written to tree
1360
1361 /// Live-output fill prescale: fill histograms (and, via the per-tree
1362 /// prescale, the trees) only on every Nth processed event. Default 1
1363 /// fills on every event. Used to raise throughput in live mapfile mode.
1366
1367 /// Maximum tree size
1368 static const Long64_t kMaxTreeSize;
1369 static const Int_t kMaxMapFileSize;
1370};
1371
1372/**
1373 * Construct the indices from one tree to another tree, and optionally in reverse as well.
1374 * @param from Name of tree where index will be created
1375 * @param to Name of tree to which index will point
1376 * @param reverse Flag to create indices in both direction
1377 */
1378inline void QwRootFile::ConstructIndices(const std::string& from, const std::string& to, bool reverse)
1379{
1380 // Return if we do not want this tree information
1381 if (IsTreeDisabled(from)) return;
1382 if (IsTreeDisabled(to)) return;
1383
1384 // If the trees are defined
1385 if (fTreeByName.count(from) > 0 && fTreeByName.count(to) > 0) {
1386
1387 // Construct index from the first tree to the second tree
1388 fTreeByName[from].front()->ConstructIndexTo(fTreeByName[to].front());
1389
1390 // Construct index from the second tree back to the first tree
1391 if (reverse)
1392 fTreeByName[to].front()->ConstructIndexTo(fTreeByName[from].front());
1393 }
1394}
1395
1396/**
1397 * Construct the tree branches of a generic object
1398 * @param name Name for tree
1399 * @param desc Description for tree
1400 * @param object Subsystem array
1401 * @param prefix Prefix for the tree
1402 */
1403template < class T >
1405 const std::string& name,
1406 const std::string& desc,
1407 T& object,
1408 const std::string& prefix)
1409{
1410 // Return if we do not want this tree information
1411 if (IsTreeDisabled(name)) return;
1412
1413 // Pointer to new tree
1414 QwRootTree* tree = 0;
1415
1416 // If the tree does not exist yet, create it
1417 if (fTreeByName.count(name) == 0) {
1418
1419 // Go to top level directory
1420
1421 this->cd();
1422
1423 // New tree with name, description, object, prefix
1424 tree = new QwRootTree(name, desc, object, prefix);
1425
1426 // Settings only relevant for new trees
1427 if (name == "evt")
1429 else if (name == "mul")
1431
1432 #if ROOT_VERSION_CODE >= ROOT_VERSION(5,26,00)
1433 tree->SetAutoFlush(fAutoFlush);
1434 #endif
1435 tree->SetAutoSave(fAutoSave);
1438
1439 if (fCircularBufferSize > 0)
1441
1442 } else {
1443
1444 // New tree based on existing tree
1445 tree = new QwRootTree(fTreeByName[name].front(), object, prefix);
1446 }
1447
1448 // Add the branches to the list of trees by name, object, type
1449 const void* addr = static_cast<const void*>(&object);
1450 const std::type_index type = typeid(object);
1451 fTreeByName[name].push_back(tree);
1452 fTreeByAddr[addr].push_back(tree);
1453 fTreeByType[type].push_back(tree);
1454}
1455
1456
1457/**
1458 * Fill the tree branches of a generic object by name
1459 * @param name Name for tree
1460 * @param object Subsystem array
1461 */
1462template < class T >
1464 const std::string& name,
1465 const T& object)
1466{
1467 // If this name has no registered trees
1468 if (! HasTreeByName(name)) return;
1469 // If this type has no registered trees
1470 if (! HasTreeByType(object)) return;
1471
1472 // Get the address of the object
1473 const void* addr = static_cast<const void*>(&object);
1474
1475 // Fill the trees with the correct address
1476 for (size_t tree = 0; tree < fTreeByAddr[addr].size(); tree++) {
1477 if (fTreeByAddr[addr].at(tree)->GetName() == name) {
1478 fTreeByAddr[addr].at(tree)->FillTreeBranches(object);
1479 }
1480 }
1481}
1482
1483
1484/**
1485 * Fill the tree branches of a generic object by type only
1486 * @param object Subsystem array
1487 */
1488template < class T >
1490 const T& object)
1491{
1492 // If this address has no registered trees
1493 if (! HasTreeByAddr(object)) return;
1494
1495 // Get the address of the object
1496 const void* addr = static_cast<const void*>(&object);
1497
1498 // Fill the trees with the correct address
1499 for (size_t tree = 0; tree < fTreeByAddr[addr].size(); tree++) {
1500 fTreeByAddr[addr].at(tree)->FillTreeBranches(object);
1501 }
1502}
1503
1504
1505#ifdef HAS_RNTUPLE_SUPPORT
1506/**
1507 * Construct the RNTuple fields of a generic object
1508 * @param name Name for RNTuple
1509 * @param desc Description for RNTuple
1510 * @param object Subsystem array
1511 * @param prefix Prefix for the RNTuple
1512 */
1513template < class T >
1514void QwRootFile::ConstructNTupleFields(
1515 const std::string& name,
1516 const std::string& desc,
1517 T& object,
1518 const std::string& prefix)
1519{
1520 // Return if RNTuples are disabled
1521 if (!fEnableRNTuples) return;
1522
1523 // Pointer to new RNTuple
1524 QwRootNTuple* ntuple = 0;
1525
1526 // If the RNTuple does not exist yet, create it
1527 if (fNTupleByName.count(name) == 0) {
1528
1529 // New RNTuple with name, description, object, prefix
1530 ntuple = new QwRootNTuple(name, desc, object, prefix);
1531
1532 // Set compression settings before initializing writer
1533 ntuple->fRNTupleCompressionAlgorithm = fRNTupleCompressionAlgorithm;
1534 ntuple->fRNTupleCompressionLevel = fRNTupleCompressionLevel;
1535
1536 // Initialize the writer with our file
1537 ntuple->InitializeWriter(fRootFile);
1538
1539 // Settings only relevant for new RNTuples
1540 if (name == "evt")
1541 ntuple->SetPrescaling(fNumMpsEventsToSave, fNumMpsEventsToSkip);
1542 else if (name == "mul")
1543 ntuple->SetPrescaling(fNumHelEventsToSave, fNumHelEventsToSkip);
1544
1545 } else {
1546
1547 // For simplicity, don't support multiple RNTuples with same name yet
1548 QwError << "Cannot create duplicate RNTuple: " << name << QwLog::endl;
1549 return;
1550 }
1551
1552 // Add the fields to the list of RNTuples by name, object, type
1553 const void* addr = static_cast<const void*>(&object);
1554 const std::type_index type = typeid(object);
1555 fNTupleByName[name].push_back(ntuple);
1556 fNTupleByAddr[addr].push_back(ntuple);
1557 fNTupleByType[type].push_back(ntuple);
1558}
1559
1560
1561/**
1562 * Fill the RNTuple fields of a generic object by name
1563 * @param name Name for RNTuple
1564 * @param object Subsystem array
1565 */
1566template < class T >
1567void QwRootFile::FillNTupleFields(
1568 const std::string& name,
1569 const T& object)
1570{
1571 // If this name has no registered RNTuples
1572 if (! HasNTupleByName(name)) return;
1573 // If this type has no registered RNTuples
1574 if (! HasNTupleByType(object)) return;
1575
1576 // Get the address of the object
1577 const void* addr = static_cast<const void*>(&object);
1578
1579 // Fill the RNTuples with the correct address
1580 for (size_t ntuple = 0; ntuple < fNTupleByAddr[addr].size(); ntuple++) {
1581 if (fNTupleByAddr[addr].at(ntuple)->GetName() == name) {
1582 fNTupleByAddr[addr].at(ntuple)->FillNTupleFields(object);
1583 }
1584 }
1585}
1586
1587
1588/**
1589 * Fill the RNTuple fields of a generic object by type only
1590 * @param object Subsystem array
1591 */
1592template < class T >
1593void QwRootFile::FillNTupleFields(
1594 const T& object)
1595{
1596 // If this address has no registered RNTuples
1597 if (! HasNTupleByAddr(object)) return;
1598
1599 // Get the address of the object
1600 const void* addr = static_cast<const void*>(&object);
1601
1602 // Fill the RNTuples with the correct address
1603 for (size_t ntuple = 0; ntuple < fNTupleByAddr[addr].size(); ntuple++) {
1604 fNTupleByAddr[addr].at(ntuple)->FillNTupleFields(object);
1605 }
1606}
1607#endif // HAS_RNTUPLE_SUPPORT
1608
1609
1610/**
1611 * Construct the objects directory of a generic object
1612 * @param name Name for objects directory
1613 * @param object Subsystem array
1614 */
1615template < class T >
1616void QwRootFile::ConstructObjects(const std::string& name, T& object)
1617{
1618 // Create the objects in a directory
1619 if (fRootFile) {
1620 std::string type = typeid(object).name();
1621 fDirsByName[name] =
1622 fRootFile->GetDirectory(("/" + name).c_str()) ?
1623 fRootFile->GetDirectory(("/" + name).c_str()) :
1624 fRootFile->GetDirectory("/")->mkdir(name.c_str());
1625 fDirsByType[type].push_back(name);
1626 object.ConstructObjects(fDirsByName[name]);
1627 }
1628
1629 // No support for directories in a map file
1630 if (fMapFile) {
1631 QwMessage << "QwRootFile::ConstructObjects::detectors address "
1632 << &object
1633 << " and its name " << name
1634 << QwLog::endl;
1635
1636 // TMapFile does not support subdirectories: objects created inside a
1637 // mkdir'd directory are NOT retrievable via TMapFile::Get (readers get
1638 // NULL). We must cd() into the map's top-level directory so that objects
1639 // created by object.ConstructObjects() attach there and are streamed into
1640 // shared memory by TMapFile::Update(); otherwise only empty name stubs
1641 // reach the map.
1642 std::string type = typeid(object).name();
1643 fMapFile->cd();
1644 fDirsByName[name] = fMapFile->GetDirectory();
1645 fDirsByType[type].push_back(name);
1646 object.ConstructObjects();
1647 }
1648}
1649
1650
1651/**
1652 * Construct the histogram of a generic object
1653 * @param name Name for histogram directory
1654 * @param object Subsystem array
1655 */
1656template < class T >
1657void QwRootFile::ConstructHistograms(const std::string& name, T& object)
1658{
1659 // Return if we do not want this histogram information
1660 if (IsHistoDisabled(name)) return;
1661
1662 // Create the histograms in a directory
1663 if (fRootFile) {
1664 std::string type = typeid(object).name();
1665 fDirsByName[name] =
1666 fRootFile->GetDirectory(("/" + name).c_str()) ?
1667 fRootFile->GetDirectory(("/" + name).c_str()) :
1668 fRootFile->GetDirectory("/")->mkdir(name.c_str());
1669 fDirsByType[type].push_back(name);
1670
1671 object.ConstructHistograms(fDirsByName[name]);
1672 }
1673
1674 // No support for directories in a map file
1675 if (fMapFile) {
1676 QwMessage << "QwRootFile::ConstructHistograms::detectors address "
1677 << &object
1678 << " and its name " << name
1679 << QwLog::endl;
1680
1681 // TMapFile does not support subdirectories: histograms created inside a
1682 // mkdir'd directory are NOT retrievable via TMapFile::Get (readers such as
1683 // panguin get NULL). We must cd() into the map's top-level directory so
1684 // that histograms created by object.ConstructHistograms() attach there and
1685 // are streamed into shared memory by TMapFile::Update(); otherwise only
1686 // empty name stubs (class=NULL, size=0) reach the map.
1687 std::string type = typeid(object).name();
1688 fMapFile->cd();
1689 fDirsByName[name] = fMapFile->GetDirectory();
1690 fDirsByType[type].push_back(name);
1691 // TMapFile has no subdirectories, so every group's histograms share the
1692 // map's single top-level directory. Without a namespace, identically
1693 // named histograms from different groups (evt_histo/mul_histo/burst_histo)
1694 // collide: TDirectoryFile::Append replaces (and orphans) the earlier one,
1695 // producing "Replacing existing TH1 (Potential memory leak)" warnings and
1696 // dropping histograms from the live display. Prefix each group's
1697 // histograms with its directory name so they all coexist in the flat map.
1698 TString prefix = TString(name.c_str()) + "_";
1699 object.ConstructHistograms((TDirectory*)NULL, prefix);
1700 }
1701}
1702
1703
1704template < class T >
1705Int_t QwRootFile::WriteParamFileList(const TString &name, T& object)
1706{
1707 Int_t retval = 0;
1708 if (fRootFile) {
1709 TList *param_list = (TList*) fRootFile->FindObjectAny(name);
1710 if (not param_list) {
1711 retval = fRootFile->WriteObject(object.GetParamFileNameList(name), name);
1712 }
1713 }
1714 return retval;
1715}
An options class which parses command line, config file and environment.
#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
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:498
Int_t Fill()
Fill the tree.
Definition QwRootFile.h:503
void ConstructNewTree()
Construct the tree.
Definition QwRootFile.h:426
UInt_t fCurrentEvent
Tree prescaling parameters.
Definition QwRootFile.h:567
virtual ~QwRootTree()
Destructor.
Definition QwRootFile.h:417
const std::string fPrefix
Definition QwRootFile.h:549
UInt_t fNumEventsToSave
Definition QwRootFile.h:569
void SetAutoFlush(Long64_t autoflush=30000000)
Set autoflush size.
Definition QwRootFile.h:593
UInt_t fNumEventsToSkip
Definition QwRootFile.h:570
void Print() const
Print the tree name and description.
Definition QwRootFile.h:525
Long64_t fAutoFlush
Definition QwRootFile.h:582
Long64_t fMaxTreeSize
Maximum tree size, autoflush and autosave.
Definition QwRootFile.h:581
const std::string fName
Name, description.
Definition QwRootFile.h:547
std::string fType
Object type.
Definition QwRootFile.h:560
static const TString kUnitsName
Definition QwRootFile.h:422
void ConstructUnitsBranch()
Definition QwRootFile.h:440
void SetCircular(Long64_t buff=100000)
Definition QwRootFile.h:613
void SetMaxTreeSize(Long64_t maxsize=1900000000)
Set maximum tree size.
Definition QwRootFile.h:587
UInt_t fNumEventsCycle
Definition QwRootFile.h:568
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:601
const std::string & GetPrefix() const
Get the description of the tree.
Definition QwRootFile.h:556
QwRootTreeBranchVector fVector
Vector of leaves.
Definition QwRootFile.h:543
void ConstructBranchAndVector(T &object)
Construct the branches and vector for generic objects.
Definition QwRootFile.h:453
Int_t fBasketSize
Definition QwRootFile.h:584
static Double_t kUnitsValue[]
Definition QwRootFile.h:23
TTree * GetTree() const
Get the tree pointer for low level operations.
Definition QwRootFile.h:533
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:554
TTree * fTree
Tree pointer.
Definition QwRootFile.h:541
friend class QwRootFile
Definition QwRootFile.h:536
void SetBasketSize(Int_t basketsize=16000)
Set basket size.
Definition QwRootFile.h:607
const std::string fDesc
Definition QwRootFile.h:548
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:552
Long64_t fAutoSave
Definition QwRootFile.h:583
void SetPrescaling(UInt_t num_to_save, UInt_t num_to_skip)
Set tree prescaling parameters.
Definition QwRootFile.h:573
std::string GetType() const
Get the object type.
Definition QwRootFile.h:563
Bool_t IsRootFile() const
Is the ROOT file active?
Definition QwRootFile.h:884
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:953
TString fRootFileStem
ROOT file stem.
void Print()
Int_t FillTree(const std::string &name)
Fill the tree with name.
Definition QwRootFile.h:993
TFile * fRootFile
ROOT file.
Bool_t IsMapFile() const
Is the map file active?
Definition QwRootFile.h:886
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.
UInt_t fHistoFillCount
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:28
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:878
static void SetDefaultRootFileDir(const std::string &dir)
Set default ROOT files dir.
Definition QwRootFile.h:874
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:927
TString fMapFileDir
TString fPermanentName
UInt_t fHistoFillPrescale
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:999
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:987
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