JAPAn
Just Another Parity Analyzer
Loading...
Searching...
No Matches
QwRootFile.cc
Go to the documentation of this file.
1/*!
2 * \file QwRootFile.cc
3 * \brief Implementation for ROOT file and tree management wrapper classes
4 */
5
6#include "QwRootFile.h"
7#include "QwRunCondition.h"
8#include "TH1.h"
9
10#include <unistd.h>
11#include <cstdio>
12
13std::string QwRootFile::fDefaultRootFileDir = ".";
14std::string QwRootFile::fDefaultRootFileStem = "Qweak_";
15
16const Long64_t QwRootFile::kMaxTreeSize = 100000000000LL;
17const Int_t QwRootFile::kMaxMapFileSize = 0x3fffffff; // 1 GiB
18
19const TString QwRootTree::kUnitsName = "ppm/D:ppb/D:um/D:mm/D:mV_uA/D:V_uA/D";
20Double_t QwRootTree::kUnitsValue[] = { 1e-6, 1e-9, 1e-3, 1 , 1e-3, 1};
21
22/**
23 * Constructor with relative filename
24 */
25QwRootFile::QwRootFile(const TString& run_label)
27 fMapFile(0), fEnableMapFile(kFALSE),
29#ifdef HAS_RNTUPLE_SUPPORT
30 , fEnableRNTuples(kFALSE)
31#endif // HAS_RNTUPLE_SUPPORT
32{
33 // Process the configuration options
35
36#ifdef QW_ENABLE_MAPFILE
37 // Check for the memory-mapped file flag
38 if (fEnableMapFile) {
39
40 TString mapfilename = "/dev/shm/";
41
42 mapfilename += "/QwMemMapFile.map";
43
44 fMapFile = TMapFile::Create(mapfilename,"UPDATE", kMaxMapFileSize, "RealTime Producer File");
45
46 if (not fMapFile) {
47 QwError << "Memory-mapped file " << mapfilename
48 << " could not be opened!" << QwLog::endl;
49 return;
50 }
51
52 QwMessage << "================== RealTime Producer Memory Map File =================" << QwLog::endl;
53 fMapFile->Print();
54 QwMessage << "======================================================================" << QwLog::endl;
55 } else
56#endif
57 {
58
59 TString rootfilename = fRootFileDir;
60 TString hostname = gSystem -> HostName();
61
62 // Use a probably-unique temporary file name.
63 pid_t pid = getpid();
64
65 fPermanentName = rootfilename
66 + Form("/%s%s.root", fRootFileStem.Data(), run_label.Data());
68 rootfilename += Form("/%s%s.%s.%d.root",
69 fRootFileStem.Data(), run_label.Data(),
70 hostname.Data(), pid);
71 // Delete permanent file if it exists to prevent accumulation across segments
72 if (gSystem->AccessPathName(fPermanentName.Data()) == 0) {
73 QwVerbose << "Removing existing permanent file: " << fPermanentName << QwLog::endl;
74 gSystem->Unlink(fPermanentName.Data());
75 }
76 // CRITICAL: Also delete the temporary file if it exists!
77 // RECREATE mode doesn't properly clear files that contain RNTuples,
78 // so we must manually delete before opening
79 if (gSystem->AccessPathName(rootfilename.Data()) == 0) {
80 QwVerbose << "Removing existing temporary file before RECREATE: " << rootfilename << QwLog::endl;
81 gSystem->Unlink(rootfilename.Data());
82 }
83 } else {
84 rootfilename = fPermanentName;
85 // Delete permanent file if it exists to ensure RECREATE truly starts fresh
86 // This is especially important for RNTuple files where RECREATE doesn't properly clear
87 if (gSystem->AccessPathName(rootfilename.Data()) == 0) {
88 QwMessage << "File exists before RECREATE, deleting: " << rootfilename << QwLog::endl;
89 int unlink_result = gSystem->Unlink(rootfilename.Data());
90 if (unlink_result == 0) {
91 QwMessage << "Successfully deleted file" << QwLog::endl;
92 } else {
93 QwError << "Failed to delete file! Error code: " << unlink_result << QwLog::endl;
94 }
95 } else {
96 QwMessage << "File does not exist before RECREATE: " << rootfilename << QwLog::endl;
97 }
98 }
99 QwMessage << "Opening file with RECREATE mode: " << rootfilename << QwLog::endl;
100 QwMessage << "QwRootFile constructor called for: " << rootfilename << QwLog::endl;
101 fRootFile = new TFile(rootfilename.Data(), "RECREATE", "myfile1");
102 if (! fRootFile) {
103 QwError << "ROOT file " << rootfilename
104 << " could not be opened!" << QwLog::endl;
105 return;
106 } else {
107 QwMessage << "Opened "<< (fUseTemporaryFile?"temporary ":"")
108 <<"rootfile " << rootfilename << QwLog::endl;
109 }
110
111 TString run_condition_name = Form("condition_%s", run_label.Data());
112 TList *run_cond_list = (TList*) fRootFile -> FindObjectAny(run_condition_name);
113 if (not run_cond_list) {
114 QwRunCondition run_condition(
115 gQwOptions.GetArgc(),
116 gQwOptions.GetArgv(),
117 run_condition_name
118 );
119
121 run_condition.Get(),
122 run_condition.GetName()
123 );
124 }
125
126 fRootFile->SetCompressionAlgorithm(fCompressionAlgorithm);
127 fRootFile->SetCompressionLevel(fCompressionLevel);
128 }
129}
130
131
132/**
133 * Destructor
134 */
136{
137 // Keep the file on disk if any trees or histograms have been filled.
138 // Also respect any other requests to keep the file around.
140
141 // Close the map file
142 if (fMapFile) {
143 fMapFile->Close();
144 // TMapFiles may not be deleted
145 fMapFile = 0;
146 }
147
148 // Close the ROOT file.
149 // Rename if permanence is requested, remove otherwise
150 if (fRootFile) {
151 TString rootfilename = fRootFile->GetName();
152
153 fRootFile->Close();
154 delete fRootFile;
155 fRootFile = 0;
156
157 int err;
158 const char* action;
160 if (fMakePermanent) {
161 // Delete existing permanent file first to avoid accumulation
162 if (gSystem->AccessPathName(fPermanentName.Data()) == 0) {
163 remove(fPermanentName.Data());
164 }
165 action = " rename ";
166 err = rename( rootfilename.Data(), fPermanentName.Data() );
167 } else {
168 action = " remove ";
169 err = remove( rootfilename.Data() );
170 }
171 // It'd be proper to "extern int errno" and strerror() here,
172 // but that doesn't seem very C++-ish.
173 if (err) {
174 QwWarning << "Couldn't" << action << rootfilename << QwLog::endl;
175 } else {
176 QwMessage << "Was able to" << action << rootfilename << QwLog::endl;
177 QwMessage << "Root file is " << fPermanentName << QwLog::endl;
178 }
179 }
180 }
181
182 // Delete Qweak ROOT trees
183 std::map< const std::string, std::vector<QwRootTree*> >::iterator map_iter;
184 std::vector<QwRootTree*>::iterator vec_iter;
185 for (map_iter = fTreeByName.begin(); map_iter != fTreeByName.end(); map_iter++) {
186 for (vec_iter = map_iter->second.begin(); vec_iter != map_iter->second.end(); vec_iter++) {
187 delete *vec_iter;
188 }
189 }
190}
191
192/**
193 * Defines configuration options using QwOptions functionality.
194 * @param options Options object
195 */
197{
198 // Define the ROOT files directory
199 options.AddOptions("Default options")
200 ("rootfiles", po::value<std::string>()->default_value(fDefaultRootFileDir),
201 "directory of the output ROOT files");
202
203 // Define the ROOT filename stem
204 options.AddOptions("Default options")
205 ("rootfile-stem", po::value<std::string>()->default_value(fDefaultRootFileStem),
206 "stem of the output ROOT filename");
207
208 // Define the memory map option
209 options.AddOptions()
210 ("enable-mapfile", po::value<bool>()->default_bool_value(false),
211 "enable output to memory-mapped file\n(likely requires circular-buffer too)");
212 options.AddOptions()
213 ("write-temporary-rootfiles", po::value<bool>()->default_bool_value(true),
214 "When writing ROOT files, use the PID to create a temporary filename");
215
216 // Define the histogram and tree options
217 options.AddOptions("ROOT output options")
218 ("disable-tree", po::value<std::vector<std::string>>()->composing(),
219 "disable output to tree regex");
220 options.AddOptions("ROOT output options")
221 ("disable-trees", po::value<bool>()->default_bool_value(false),
222 "disable output to all trees");
223 options.AddOptions("ROOT output options")
224 ("disable-histos", po::value<bool>()->default_bool_value(false),
225 "disable output to all histograms");
226
227 // Define the helicity window versus helicity pattern options
228 options.AddOptions("ROOT output options")
229 ("disable-mps-tree", po::value<bool>()->default_bool_value(false),
230 "disable helicity window output");
231 options.AddOptions("ROOT output options")
232 ("disable-pair-tree", po::value<bool>()->default_bool_value(false),
233 "disable helicity pairs output");
234 options.AddOptions("ROOT output options")
235 ("disable-hel-tree", po::value<bool>()->default_bool_value(false),
236 "disable helicity pattern output");
237 options.AddOptions("ROOT output options")
238 ("disable-burst-tree", po::value<bool>()->default_bool_value(false),
239 "disable burst tree");
240 options.AddOptions("ROOT output options")
241 ("disable-slow-tree", po::value<bool>()->default_bool_value(false),
242 "disable slow control tree");
243
244#ifdef HAS_RNTUPLE_SUPPORT
245 // Define the RNTuple options
246 options.AddOptions("ROOT output options")
247 ("enable-rntuples", po::value<bool>()->default_bool_value(false),
248 "enable RNTuple output");
249#endif // HAS_RNTUPLE_SUPPORT
250
251 // Define the tree output prescaling options
252 options.AddOptions("ROOT output options")
253 ("num-mps-accepted-events", po::value<int>()->default_value(0),
254 "number of accepted consecutive MPS events");
255 options.AddOptions("ROOT output options")
256 ("num-mps-discarded-events", po::value<int>()->default_value(0),
257 "number of discarded consecutive MPS events");
258 options.AddOptions("ROOT output options")
259 ("num-hel-accepted-events", po::value<int>()->default_value(0),
260 "number of accepted consecutive pattern events");
261 options.AddOptions("ROOT output options")
262 ("num-hel-discarded-events", po::value<int>()->default_value(0),
263 "number of discarded consecutive pattern events");
264 options.AddOptions("ROOT output options")
265 ("mapfile-update-interval", po::value<int>()->default_value(-1),
266 "Events between a map file update");
267
268 // Define the autoflush and autosave option (default values by ROOT)
269 options.AddOptions("ROOT performance options")
270 ("autoflush", po::value<int>()->default_value(0),
271 "TTree autoflush");
272 options.AddOptions("ROOT performance options")
273 ("autosave", po::value<int>()->default_value(300000000),
274 "TTree autosave");
275 options.AddOptions("ROOT performance options")
276 ("basket-size", po::value<int>()->default_value(16000),
277 "TTree basket size");
278 options.AddOptions("ROOT performance options")
279 ("circular-buffer", po::value<int>()->default_value(0),
280 "TTree circular buffer");
281 options.AddOptions("ROOT performance options")
282 ("compression-algorithm", po::value<int>()->default_value(1),
283 "TFile compression algorithm (1=ZLIB, 2=LZMA, 4=LZ4, 5=ZSTD, default=1 ZLIB)");
284 options.AddOptions("ROOT performance options")
285 ("compression-level", po::value<int>()->default_value(1),
286 "TFile compression level (default = 1, no compression = 0)");
287}
288
289
290/**
291 * Parse the configuration options and store in class fields
292 * @param options Options object
293 */
295{
296 // Option 'rootfiles' to specify ROOT files dir
297 fRootFileDir = TString(options.GetValue<std::string>("rootfiles"));
298
299 // Option 'root-stem' to specify ROOT file stem
300 fRootFileStem = TString(options.GetValue<std::string>("rootfile-stem"));
301
302 // Option 'mapfile' to enable memory-mapped ROOT file
303 fEnableMapFile = options.GetValue<bool>("enable-mapfile");
304#ifndef QW_ENABLE_MAPFILE
305 if( fEnableMapFile ) {
307 QwWarning << "QwRootFile::ProcessOptions: "
308 << "The 'enable-mapfile' flag is not supported by the ROOT "
309 "version with which this app is built. Disabling it."
310 << QwLog::endl;
311 fEnableMapFile = false;
312 }
313#endif
314 fUseTemporaryFile = options.GetValue<bool>("write-temporary-rootfiles");
315
316#ifdef HAS_RNTUPLE_SUPPORT
317 // Option 'enable-rntuples' to enable RNTuple output
318 fEnableRNTuples = options.GetValue<bool>("enable-rntuples");
319#endif // HAS_RNTUPLE_SUPPORT
320
321 // Options 'disable-trees' and 'disable-histos' for disabling
322 // tree and histogram output
323 auto v = options.GetValueVector<std::string>("disable-tree");
324 std::for_each(v.begin(), v.end(), [&](const std::string& s){ this->DisableTree(s); });
325 if (options.GetValue<bool>("disable-trees")) DisableTree(".*");
326 if (options.GetValue<bool>("disable-histos")) DisableHisto(".*");
327
328 // Options 'disable-mps' and 'disable-hel' for disabling
329 // helicity window and helicity pattern output
330 if (options.GetValue<bool>("disable-mps-tree")) DisableTree("^evt$");
331 if (options.GetValue<bool>("disable-pair-tree")) DisableTree("^pr$");
332 if (options.GetValue<bool>("disable-hel-tree")) DisableTree("^mul$");
333 if (options.GetValue<bool>("disable-burst-tree")) DisableTree("^burst$");
334 if (options.GetValue<bool>("disable-slow-tree")) DisableTree("^slow$");
335
336 // Options 'num-accepted-events' and 'num-discarded-events' for
337 // prescaling of the tree output
338 fNumMpsEventsToSave = options.GetValue<int>("num-mps-accepted-events");
339 fNumMpsEventsToSkip = options.GetValue<int>("num-mps-discarded-events");
340 fNumHelEventsToSave = options.GetValue<int>("num-mps-accepted-events");
341 fNumHelEventsToSkip = options.GetValue<int>("num-mps-discarded-events");
342
343 // Update interval for the map file
344 fCircularBufferSize = options.GetValue<int>("circular-buffer");
345 fUpdateInterval = options.GetValue<int>("mapfile-update-interval");
346 fCompressionAlgorithm = options.GetValue<int>("compression-algorithm");
347 fCompressionLevel = options.GetValue<int>("compression-level");
348 fBasketSize = options.GetValue<int>("basket-size");
349
350 // Autoflush and autosave
351 fAutoFlush = options.GetValue<int>("autoflush");
352 if ((ROOT_VERSION_CODE < ROOT_VERSION(5,26,00)) && fAutoFlush != -30000000){
354 QwWarning << "QwRootFile::ProcessOptions: "
355 << "The 'autoflush' flag is not supported by ROOT version "
356 << ROOT_RELEASE
357 << QwLog::endl;
358 }
359 fAutoSave = options.GetValue<int>("autosave");
360 return;
361}
362
363/**
364 * Determine whether the rootfile object has any non-empty trees or
365 * histograms.
366 */
368 return this->HasAnyFilled(fRootFile);
369}
370Bool_t QwRootFile::HasAnyFilled(TDirectory* d) {
371 if (!d) {
372
373 return false;
374 }
375
376 // First check if any in-memory trees have been filled
377 for (auto& pair : fTreeByName) {
378 for (auto& tree : pair.second) {
379 if (tree && tree->GetTree()) {
380 Long64_t entries = tree->GetTree()->GetEntries();
381 if (entries > 0) {
382
383 return true;
384 }
385 }
386 }
387 }
388
389#ifdef HAS_RNTUPLE_SUPPORT
390 // Then check if any RNTuples have been filled
391 for (auto& pair : fNTupleByName) {
392 for (auto& ntuple : pair.second) {
393 if (ntuple && ntuple->fCurrentEvent > 0) {
394
395 return true;
396 }
397 }
398 }
399#endif // HAS_RNTUPLE_SUPPORT
400
401 TList* l = d->GetListOfKeys();
402
403
404 for( int i=0; i < l->GetEntries(); ++i) {
405 const char* name = l->At(i)->GetName();
406 TObject* obj = d->FindObjectAny(name);
407
408
409
410 // Objects which can't be found don't count.
411 if (!obj) {
412
413 continue;
414 }
415
416 // Lists of parameter files, map files, and job conditions don't count.
417 if ( TString(name).Contains("parameter_file") ) {
418
419 continue;
420 }
421 if ( TString(name).Contains("mapfile") ) {
422 continue;
423 }
424 if ( TString(name).Contains("_condition") ) {
425 continue;
426 }
427 // The EPICS tree doesn't count
428 if ( TString(name).Contains("slow") ) {
429 continue;
430 }
431
432 // Recursively check subdirectories.
433 if (obj->IsA()->InheritsFrom( "TDirectory" )) {
434 if (this->HasAnyFilled( (TDirectory*)obj )) return true;
435 }
436
437 if (obj->IsA()->InheritsFrom( "TTree" )) {
438 Long64_t entries = ((TTree*) obj)->GetEntries();
439 if ( entries ) return true;
440 }
441
442 if (obj->IsA()->InheritsFrom( "TH1" )) {
443 Double_t entries = ((TH1*) obj)->GetEntries();
444 if ( entries ) return true;
445 }
446 }
447 return false;
448}
#define gQwOptions
Definition QwOptions.h:31
#define QwVerbose
Predefined log drain for verbose messages.
Definition QwLog.h:54
#define QwError
Predefined log drain for errors.
Definition QwLog.h:39
#define QwWarning
Predefined log drain for warnings.
Definition QwLog.h:44
#define QwMessage
Predefined log drain for regular messages.
Definition QwLog.h:49
ROOT file and tree management wrapper classes.
Run condition management and metadata.
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
std::vector< T > GetValueVector(const std::string &key)
Get a list of templated values.
Definition QwOptions.h:249
T GetValue(const std::string &key)
Get a templated value.
Definition QwOptions.h:236
po::options_description_easy_init AddOptions(const std::string &blockname="Specialized options")
Add an option to a named block or create new block.
Definition QwOptions.h:170
static const TString kUnitsName
Definition QwRootFile.h:422
static Double_t kUnitsValue[]
Definition QwRootFile.h:20
Int_t fBasketSize
TString fRootFileStem
ROOT file stem.
TFile * fRootFile
ROOT file.
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 HasAnyFilled(void)
Search for non-empty trees or histograms in the file.
static std::string fDefaultRootFileDir
Default ROOT files dir.
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)
void ProcessOptions(QwOptions &options)
Process the configuration options.
UInt_t fNumHelEventsToSkip
Int_t fAutoSave
std::map< const std::string, std::vector< QwRootTree * > > fTreeByName
Tree names, addresses, and types.
Int_t fCompressionLevel
void DisableHisto(const TString &regexp)
Add regexp to list of disabled histogram directories.
Bool_t fUseTemporaryFile
UInt_t fNumMpsEventsToSave
static std::string fDefaultRootFileStem
Default ROOT file stem.
TString fPermanentName
static void DefineOptions(QwOptions &options)
Define the configuration options.
UInt_t fNumHelEventsToSave
Int_t fAutoFlush
TMapFile * fMapFile
Map file.
Int_t fUpdateInterval
QwRootFile()
Private default constructor.
UInt_t fCircularBufferSize
static const Long64_t kMaxTreeSize
Maximum tree size.
Bool_t fMakePermanent
UInt_t fNumMpsEventsToSkip
Prescaling of events written to tree.
Bool_t fEnableMapFile
Run condition and quality management.