32#ifdef HAS_RNTUPLE_SUPPORT
33 , fEnableRNTuples(kFALSE)
39#ifdef QW_ENABLE_MAPFILE
47 if (gSystem->AccessPathName(
fMapFileDir, kWritePermission)) {
49 <<
"' does not exist or is not writable." <<
QwLog::endl;
50 QwError <<
"Set --mapfile-dir to a writable directory (ideally a RAM "
51 "disk). /dev/shm does not exist on macOS; a normal directory "
52 "or a hdiutil-mounted RAM disk works there." <<
QwLog::endl;
56 TString mapfilename =
fMapFileDir +
"/QwMemMapFile.map";
61 QwError <<
"Memory-mapped file " << mapfilename
66 QwMessage <<
"================== RealTime Producer Memory Map File =================" <<
QwLog::endl;
69 QwMessage <<
"======================================================================" <<
QwLog::endl;
75 TString hostname = gSystem -> HostName();
83 rootfilename += Form(
"/%s%s.%s.%d.root",
85 hostname.Data(), pid);
94 if (gSystem->AccessPathName(rootfilename.Data()) == 0) {
95 QwVerbose <<
"Removing existing temporary file before RECREATE: " << rootfilename <<
QwLog::endl;
96 gSystem->Unlink(rootfilename.Data());
102 if (gSystem->AccessPathName(rootfilename.Data()) == 0) {
104 int unlink_result = gSystem->Unlink(rootfilename.Data());
105 if (unlink_result == 0) {
120 fRootFile = TFile::Open(rootfilename.Data(),
"RECREATE",
"myfile1");
122 QwError <<
"ROOT file " << rootfilename
132 TString run_condition_name = Form(
"condition_%s", run_label.Data());
133 TList *run_cond_list = (TList*)
fRootFile -> FindObjectAny(run_condition_name);
134 if (not run_cond_list) {
172 TString rootfilename =
fRootFile->GetName();
190 err = remove( rootfilename.Data() );
204 std::map< const std::string, std::vector<QwRootTree*> >::iterator map_iter;
205 std::vector<QwRootTree*>::iterator vec_iter;
207 for (vec_iter = map_iter->second.begin(); vec_iter != map_iter->second.end(); vec_iter++) {
222 "directory of the output ROOT files");
227 "stem of the output ROOT filename");
231 (
"enable-mapfile", po::value<bool>()->default_bool_value(
false),
232 "enable output to memory-mapped file\n(likely requires circular-buffer too)");
234 (
"mapfile-dir", po::value<std::string>()->default_value(
"/dev/shm"),
235 "directory that holds the memory-mapped file (QwMemMapFile.map). Defaults "
236 "to /dev/shm (Linux tmpfs). Set this on platforms without /dev/shm "
237 "(e.g. macOS) to a writable directory, ideally a RAM disk.");
239 (
"write-temporary-rootfiles", po::value<bool>()->default_bool_value(
true),
240 "When writing ROOT files, use the PID to create a temporary filename");
244 (
"disable-tree", po::value<std::vector<std::string>>()->composing(),
245 "disable output to tree regex");
247 (
"disable-trees", po::value<bool>()->default_bool_value(
false),
248 "disable output to all trees");
250 (
"disable-histos", po::value<bool>()->default_bool_value(
false),
251 "disable output to all histograms");
255 (
"disable-mps-tree", po::value<bool>()->default_bool_value(
false),
256 "disable helicity window output");
258 (
"disable-pair-tree", po::value<bool>()->default_bool_value(
false),
259 "disable helicity pairs output");
261 (
"disable-hel-tree", po::value<bool>()->default_bool_value(
false),
262 "disable helicity pattern output");
264 (
"disable-burst-tree", po::value<bool>()->default_bool_value(
false),
265 "disable burst tree");
267 (
"disable-slow-tree", po::value<bool>()->default_bool_value(
false),
268 "disable slow control tree");
270 (
"disable-redundant-trees", po::value<bool>()->default_bool_value(
false),
271 "disable the per-entry trees whose content is already captured by a "
272 "matching histogram directory (evt/mul/burst). Histograms, the "
273 "running-sum trees (evts/muls/bursts), the pair tree (pr) and the slow "
274 "control tree (slow) are kept.");
276#ifdef HAS_RNTUPLE_SUPPORT
279 (
"enable-rntuples", po::value<bool>()->default_bool_value(
false),
280 "enable RNTuple output");
285 (
"num-mps-accepted-events", po::value<int>()->default_value(0),
286 "number of accepted consecutive MPS events");
288 (
"num-mps-discarded-events", po::value<int>()->default_value(0),
289 "number of discarded consecutive MPS events");
291 (
"num-hel-accepted-events", po::value<int>()->default_value(0),
292 "number of accepted consecutive pattern events");
294 (
"num-hel-discarded-events", po::value<int>()->default_value(0),
295 "number of discarded consecutive pattern events");
297 (
"mapfile-update-interval", po::value<int>()->default_value(-1),
298 "Events between a map file update");
300 (
"fill-prescale", po::value<int>()->default_value(1),
301 "Fill trees and histograms only on every Nth processed event (physics is "
302 "still computed every event). Roughly multiplies live (mapfile) throughput "
303 "by N at the cost of fewer accumulated monitoring entries. Default 1 = "
304 "fill every event.");
307 options.
AddOptions(
"ROOT performance options")
308 (
"autoflush", po::value<int>()->default_value(0),
310 options.
AddOptions(
"ROOT performance options")
311 (
"autosave", po::value<int>()->default_value(300000000),
313 options.
AddOptions(
"ROOT performance options")
314 (
"basket-size", po::value<int>()->default_value(16000),
315 "TTree basket size");
316 options.
AddOptions(
"ROOT performance options")
317 (
"circular-buffer", po::value<int>()->default_value(0),
318 "TTree circular buffer");
319 options.
AddOptions(
"ROOT performance options")
320 (
"compression-algorithm", po::value<int>()->default_value(1),
321 "TFile compression algorithm (1=ZLIB, 2=LZMA, 4=LZ4, 5=ZSTD, default=1 ZLIB)");
322 options.
AddOptions(
"ROOT performance options")
323 (
"compression-level", po::value<int>()->default_value(1),
324 "TFile compression level (default = 1, no compression = 0)");
325 options.
AddOptions(
"ROOT performance options")
326 (
"rntuple-compression-algorithm", po::value<int>()->default_value(4),
327 "RNTuple compression algorithm (1=ZLIB, 2=LZMA, 4=LZ4, 5=ZSTD, default=4 LZ4)");
328 options.
AddOptions(
"ROOT performance options")
329 (
"rntuple-compression-level", po::value<int>()->default_value(0),
330 "RNTuple compression level (0-12, default=0 for maximum performance)");
343 if( ! fs::exists(tmppath) || ! fs::is_directory(tmppath)) {
345 <<
", does not exist. Exiting."
355#ifndef QW_ENABLE_MAPFILE
358 QwWarning <<
"QwRootFile::ProcessOptions: "
359 <<
"The 'enable-mapfile' flag is not supported by the ROOT "
360 "version with which this app is built. Disabling it."
370#ifdef HAS_RNTUPLE_SUPPORT
372 fEnableRNTuples = options.
GetValue<
bool>(
"enable-rntuples");
379 QwWarning <<
"QwRootFile::ProcessOptions: "
380 <<
"RNTuple output is not supported alongside --enable-mapfile "
381 "(TMapFile is not a TDirectory). Disabling RNTuples."
383 fEnableRNTuples =
false;
390 std::for_each(v.begin(), v.end(), [&](
const std::string& s){ this->DisableTree(s); });
414 const UInt_t kMapFileCircularDefault = 100;
416 QwWarning <<
"QwRootFile::ProcessOptions: "
417 <<
"--enable-mapfile requires a bounded TTree to avoid "
419 <<
" MiB mmap region. "
420 "Forcing --circular-buffer=" << kMapFileCircularDefault
421 <<
" (pass --circular-buffer=N to override; pass "
422 "--disable-trees to suppress tree output entirely)."
427#ifdef HAS_RNTUPLE_SUPPORT
436 if (fEnableRNTuples) {
438 QwMessage <<
"QwRootFile::ProcessOptions: "
439 <<
"--enable-rntuples is set; disabling tree output "
440 "(channels share layout state between TTree and RNTuple "
441 "writers, so the two cannot be produced in the same run)."
461 if (options.
GetValue<
bool>(
"disable-redundant-trees")) {
480 int fillPrescale = options.
GetValue<
int>(
"fill-prescale");
481 if (fillPrescale < 1) fillPrescale = 1;
483 if (fillPrescale > 1) {
504 if ((ROOT_VERSION_CODE < ROOT_VERSION(5,26,00)) &&
fAutoFlush != -30000000){
506 QwWarning <<
"QwRootFile::ProcessOptions: "
507 <<
"The 'autoflush' flag is not supported by ROOT version "
530 for (
auto& tree : pair.second) {
531 if (tree && tree->GetTree()) {
532 Long64_t entries = tree->GetTree()->GetEntries();
541#ifdef HAS_RNTUPLE_SUPPORT
543 for (
auto& pair : fNTupleByName) {
544 for (
auto& ntuple : pair.second) {
545 if (ntuple && ntuple->fCurrentEvent > 0) {
553 TList* l = d->GetListOfKeys();
556 for(
int i=0; i < l->GetEntries(); ++i) {
557 const char* name = l->At(i)->GetName();
558 TObject* obj = d->FindObjectAny(name);
569 if ( TString(name).Contains(
"parameter_file") ) {
573 if ( TString(name).Contains(
"mapfile") ) {
576 if ( TString(name).Contains(
"_condition") ) {
580 if ( TString(name).Contains(
"slow") ) {
585 if (obj->IsA()->InheritsFrom(
"TDirectory" )) {
586 if (this->
HasAnyFilled( (TDirectory*)obj ))
return true;
589 if (obj->IsA()->InheritsFrom(
"TTree" )) {
590 Long64_t entries = ((TTree*) obj)->GetEntries();
591 if ( entries )
return true;
594 if (obj->IsA()->InheritsFrom(
"TH1" )) {
595 Double_t entries = ((TH1*) obj)->GetEntries();
596 if ( entries )
return true;