artdaq  v3_02_00
RootDAQOut_module.cc
1 #include "art/Framework/Core/FileBlock.h"
2 #include "art/Framework/Core/ModuleMacros.h"
3 #include "art/Framework/Core/OutputModule.h"
4 #include "art/Framework/Core/ResultsProducer.h"
5 #include "art/Framework/Core/RPManager.h"
6 #include "art/Framework/IO/FileStatsCollector.h"
7 #include "art/Framework/IO/PostCloseFileRenamer.h"
8 #include "art/Framework/IO/Root/DropMetaData.h"
9 #include "art/Framework/IO/Root/RootOutputClosingCriteria.h"
10 #include "art/Framework/Principal/EventPrincipal.h"
11 #include "art/Framework/Principal/Event.h"
12 #include "art/Framework/Principal/ResultsPrincipal.h"
13 #include "art/Framework/Principal/Results.h"
14 #include "art/Framework/Principal/RunPrincipal.h"
15 #include "art/Framework/Principal/Run.h"
16 #include "art/Framework/Principal/SubRunPrincipal.h"
17 #include "art/Framework/Principal/SubRun.h"
18 #include "art/Persistency/Provenance/ProductMetaData.h"
19 #include "art/Utilities/ConfigurationTable.h"
20 #include "art/Utilities/parent_path.h"
21 #include "art/Utilities/unique_filename.h"
22 
23 #include "artdaq/ArtModules/RootDAQOutput/RootDAQOutFile.h"
24 #include "artdaq/ArtModules/RootDAQOutput/detail/rootOutputConfigurationTools.h"
25 #include "art/Framework/IO/detail/logFileAction.h"
26 
27 #include "canvas/Persistency/Provenance/FileFormatVersion.h"
28 #include "canvas/Utilities/Exception.h"
29 #include "fhiclcpp/ParameterSet.h"
30 #include "fhiclcpp/types/Atom.h"
31 #include "fhiclcpp/types/OptionalAtom.h"
32 #include "fhiclcpp/types/Table.h"
33 #include "messagefacility/MessageLogger/MessageLogger.h"
34 #define TRACE_NAME "RootDAQOut"
35 #include "trace.h" // TRACE
36 
37 # include "Rtypes.h"
38 # include "TBranchElement.h"
39 # include "TClass.h"
40 # include "TFile.h"
41 # include "TTree.h"
42 
43 #include <iomanip>
44 #include <memory>
45 #include <sstream>
46 #include <string>
47 #include <utility>
48 
49 using std::string;
50 
51 namespace art {
52  class RootDAQOut;
53  class RootDAQOutFile;
54 }
55 
56 class art::RootDAQOut final : public OutputModule{
57 public:
58 
59  static constexpr char const* default_tmpDir {"<parent-path-of-filename>"};
60 
61  struct Config
62  {
63 
64 using Name = fhicl::Name;
65 using Comment = fhicl::Comment;
66 template <typename T> using Atom = fhicl::Atom<T>;
67 template <typename T> using OptionalAtom = fhicl::OptionalAtom<T>;
68 
69 fhicl::TableFragment<art::OutputModule::Config> omConfig;
70 Atom<std::string> catalog { Name("catalog"), "" };
71 OptionalAtom<bool> dropAllEvents { Name("dropAllEvents") };
72 Atom<bool> dropAllSubRuns { Name("dropAllSubRuns"), false };
73 OptionalAtom<bool> fastCloning { Name("fastCloning") };
74 Atom<std::string> tmpDir { Name("tmpDir"), default_tmpDir };
75 Atom<int> compressionLevel { Name("compressionLevel"), 7 };
76 Atom<int64_t> saveMemoryObjectThreshold { Name("saveMemoryObjectThreshold"), -1l };
77 Atom<int64_t> treeMaxVirtualSize { Name("treeMaxVirtualSize"), -1 };
78 Atom<int> splitLevel { Name("splitLevel"), 99 };
79 Atom<int> basketSize { Name("basketSize"), 16384 };
80 Atom<bool> dropMetaDataForDroppedData { Name("dropMetaDataForDroppedData"), false };
81 Atom<std::string> dropMetaData { Name("dropMetaData"), "NONE" };
82 Atom<bool> writeParameterSets { Name("writeParameterSets"), true };
83 fhicl::Table<ClosingCriteria::Config> fileProperties { Name("fileProperties") };
84 
85 Config()
86 {
87  // Both RootDAQOut module and OutputModule use the "fileName"
88  // FHiCL parameter. However, whereas in OutputModule the
89  // parameter has a default, for RootDAQOut the parameter should
90  // not. We therefore have to change the default flag setting
91  // for 'OutputModule::Config::fileName'.
92  using namespace fhicl::detail;
93  ParameterBase* adjustFilename {const_cast<fhicl::Atom<std::string>*>(&omConfig().fileName)};
94  adjustFilename->set_value_type(fhicl::value_type::REQUIRED);
95  }
96 
97  struct KeysToIgnore
98 {
99 std::set<std::string> operator()() const
100 {
101  std::set<std::string> keys {art::OutputModule::Config::KeysToIgnore::get()};
102  keys.insert("results");
103  return keys;
104 }
105 };
106 
107 };
108 
109 using Parameters = art::WrappedTable<Config,Config::KeysToIgnore>;
110 
111 explicit RootDAQOut(Parameters const&);
112 
113 void postSelectProducts(FileBlock const&) override;
114 
115 void beginJob() override;
116 void endJob() override;
117 
118 void event(EventPrincipal const&) override;
119 
120 void beginSubRun(SubRunPrincipal const&) override;
121 void endSubRun(SubRunPrincipal const&) override;
122 
123 void beginRun(RunPrincipal const&) override;
124 void endRun(RunPrincipal const&) override;
125 
126 private:
127 
128  std::string const& lastClosedFileName() const override;
129  void openFile(FileBlock const&) override;
130  void respondToOpenInputFile(FileBlock const&) override;
131  void readResults(ResultsPrincipal const& resp) override;
132  void respondToCloseInputFile(FileBlock const&) override;
133  void incrementInputFileNumber() override;
134  void write(EventPrincipal&) override;
135  void writeSubRun(SubRunPrincipal&) override;
136  void writeRun(RunPrincipal&) override;
137  void setSubRunAuxiliaryRangeSetID(RangeSet const&) override;
138  void setRunAuxiliaryRangeSetID(RangeSet const&) override;
139  bool isFileOpen() const override;
140  void setFileStatus(OutputFileStatus) override;
141  bool requestsToCloseFile() const override;
142  void doOpenFile();
143  void startEndFile() override;
144  void writeFileFormatVersion() override;
145  void writeFileIndex() override;
146  void writeEventHistory() override;
147  void writeProcessConfigurationRegistry() override;
148  void writeProcessHistoryRegistry() override;
149  void writeParameterSetRegistry() override;
150  void writeProductDescriptionRegistry() override;
151  void writeParentageRegistry() override;
152  void writeBranchIDListRegistry() override;
153  void
154  doWriteFileCatalogMetadata(FileCatalogMetadata::collection_type const& md,
155  FileCatalogMetadata::collection_type const& ssmd)
156  override;
157  void writeProductDependencies() override;
158  void finishEndFile() override;
159  void doRegisterProducts(MasterProductRegistry& mpr,
160  ModuleDescription const& md) override;
161 
162 private:
163 
164  std::string const catalog_;
165  bool dropAllEvents_ {false};
166  bool dropAllSubRuns_;
167  std::string const moduleLabel_;
168  int inputFileCount_ {0};
169  std::unique_ptr<RootDAQOutFile> rootOutputFile_ {nullptr};
170  FileStatsCollector fstats_;
171  std::string const filePattern_;
172  std::string tmpDir_;
173  std::string lastClosedFileName_ {};
174 
175  // We keep this set of data members for the use of RootDAQOutFile.
176  int const compressionLevel_;
177  int64_t const saveMemoryObjectThreshold_;
178  int64_t const treeMaxVirtualSize_;
179  int const splitLevel_;
180  int const basketSize_;
181  DropMetaData dropMetaData_;
182  bool dropMetaDataForDroppedData_;
183 
184  // We keep this for the use of RootDAQOutFile and we also use it
185  // during file open to make some choices.
186  bool fastCloning_ {true};
187 
188  // Set false only for cases where we are guaranteed never to need
189  // historical ParameterSet information in the downstream file
190  // (e.g. mixing).
191  bool writeParameterSets_;
192  ClosingCriteria fileProperties_;
193 
194  // ResultsProducer management.
195  RPManager rpm_;
196 };
197 
198 art::RootDAQOut::
199 RootDAQOut(Parameters const& config)
200  : OutputModule{ config().omConfig, config.get_PSet() }
201  , catalog_{ config().catalog() }
202  , dropAllSubRuns_{ config().dropAllSubRuns() }
203  , moduleLabel_{ config.get_PSet().get<string>("module_label") }
204  , fstats_{ moduleLabel_, processName() }
205  , filePattern_{ config().omConfig().fileName() }
206  , tmpDir_{ config().tmpDir() == default_tmpDir ? parent_path(filePattern_) : config().tmpDir() }
207  , compressionLevel_{ config().compressionLevel() }
208  , saveMemoryObjectThreshold_{ config().saveMemoryObjectThreshold() }
209  , treeMaxVirtualSize_{ config().treeMaxVirtualSize() }
210  , splitLevel_{ config().splitLevel() }
211  , basketSize_{ config().basketSize() }
212  , dropMetaData_{ config().dropMetaData() }
213  , dropMetaDataForDroppedData_{ config().dropMetaDataForDroppedData() }
214  , writeParameterSets_{ config().writeParameterSets() }
215  , fileProperties_{
216  (detail::validateFileNamePattern(config.get_PSet().has_key(config().fileProperties.name()), filePattern_), // comma operator!
217  config().fileProperties()) }
218  , rpm_{ config.get_PSet() }
219 {
220  bool const dropAllEventsSet{ config().dropAllEvents(dropAllEvents_) };
221  dropAllEvents_ = detail::shouldDropEvents(dropAllEventsSet, dropAllEvents_, dropAllSubRuns_);
222 
223  // N.B. Any time file switching is enabled at a boundary other than
224  // InputFile, fastCloning_ ***MUST*** be deactivated. This is
225  // to ensure that the Event tree from the InputFile is not
226  // accidentally cloned to the output file before the output
227  // module has seen the events that are going to be processed.
228  bool const fastCloningSet{ config().fastCloning(fastCloning_) };
229  fastCloning_ = detail::shouldFastClone(fastCloningSet, fastCloning_, wantAllEvents(), fileProperties_);
230 
231  if (!writeParameterSets_)
232  {
233  mf::LogWarning("PROVENANCE")
234  << "Output module " << moduleLabel_ << " has parameter writeParameterSets set to false.\n"
235  << "Parameter set provenance will not be available in subsequent jobs.\n"
236  << "Check your experiment's policy on this issue to avoid future problems\n"
237  << "with analysis reproducibility.\n";
238  }
239 }
240 
241 void
242 art::RootDAQOut::
243 openFile(FileBlock const& fb)
244 {
245  // Note: The file block here refers to the currently open
246  // input file, so we can find out about the available
247  // products by looping over the branches of the input
248  // file data trees.
249  if (!isFileOpen())
250  {
251  doOpenFile();
252  respondToOpenInputFile(fb);
253  }
254 }
255 
256 void
257 art::RootDAQOut::
258 postSelectProducts(FileBlock const& fb)
259 {
260  if (isFileOpen())
261  {
262  rootOutputFile_->selectProducts(fb);
263  }
264 }
265 
266 void
267 art::RootDAQOut::
268 respondToOpenInputFile(FileBlock const& fb)
269 {
270  ++inputFileCount_;
271  if (!isFileOpen()) return;
272 
273  bool fastCloneThisOne = fastCloning_ && (fb.tree() != nullptr) &&
274  ((remainingEvents() < 0) ||
275  (remainingEvents() >= fb.tree()->GetEntries()));
276  if (fastCloning_ && !fastCloneThisOne)
277  {
278  mf::LogWarning("FastCloning")
279  << "Fast cloning deactivated for this input file due to "
280  << "empty event tree and/or event limits.";
281  }
282  if (fastCloneThisOne && !fb.fastClonable())
283  {
284  mf::LogWarning("FastCloning")
285  << "Fast cloning deactivated for this input file due to "
286  << "information in FileBlock.";
287  fastCloneThisOne = false;
288  }
289  rootOutputFile_->beginInputFile(fb, fastCloneThisOne && fastCloning_);
290  fstats_.recordInputFile(fb.fileName());
291 }
292 
293 void
294 art::RootDAQOut::
295 readResults(ResultsPrincipal const& resp)
296 {
297  rpm_.for_each_RPWorker([&resp](RPWorker& w)
298  {
299  Results const res{ resp, w.moduleDescription() };
300  w.rp().doReadResults(res);
301  });
302 }
303 
304 void
305 art::RootDAQOut::
306 respondToCloseInputFile(FileBlock const& fb)
307 {
308  if (isFileOpen())
309  {
310  rootOutputFile_->respondToCloseInputFile(fb);
311  }
312 }
313 
314 #define STRINGIFY(x) #x
315 #define TOSTRING(x) STRINGIFY(x)
316 
317 void art::RootDAQOut::write(EventPrincipal& ep)
318 {
319  TLOG(10) << "RootDAQOut::write begin - line " << __LINE__;
320  if (dropAllEvents_)
321  {
322  return;
323  }
324  if (hasNewlyDroppedBranch()[InEvent])
325  {
326  ep.addToProcessHistory();
327  }
328  rootOutputFile_->writeOne(ep);
329  fstats_.recordEvent(ep.id());
330  TLOG(10) << "RootDAQOut::write done/return";
331 }
332 
333 void
334 art::RootDAQOut::
335 setSubRunAuxiliaryRangeSetID(RangeSet const& rs)
336 {
337  rootOutputFile_->setSubRunAuxiliaryRangeSetID(rs);
338 }
339 
340 void
341 art::RootDAQOut::
342 writeSubRun(SubRunPrincipal& sr)
343 {
344  if (dropAllSubRuns_)
345  {
346  return;
347  }
348  if (hasNewlyDroppedBranch()[InSubRun])
349  {
350  sr.addToProcessHistory();
351  }
352  rootOutputFile_->writeSubRun(sr);
353  fstats_.recordSubRun(sr.id());
354 }
355 
356 void
357 art::RootDAQOut::
358 setRunAuxiliaryRangeSetID(RangeSet const& rs)
359 {
360  rootOutputFile_->setRunAuxiliaryRangeSetID(rs);
361 }
362 
363 void
364 art::RootDAQOut::
365 writeRun(RunPrincipal& r)
366 {
367  if (hasNewlyDroppedBranch()[InRun])
368  {
369  r.addToProcessHistory();
370  }
371  rootOutputFile_->writeRun(r);
372  fstats_.recordRun(r.id());
373 }
374 
375 void
376 art::RootDAQOut::
377 startEndFile()
378 {
379  auto resp = std::make_unique<ResultsPrincipal>(ResultsAuxiliary{},
380  description().processConfiguration());
381  if (ProductMetaData::instance().productProduced(InResults) ||
382  hasNewlyDroppedBranch()[InResults])
383  {
384  resp->addToProcessHistory();
385  }
386  rpm_.for_each_RPWorker([&resp](RPWorker& w)
387  {
388  Results res{ *resp, w.moduleDescription() };
389  w.rp().doWriteResults(*resp, res);
390  });
391  rootOutputFile_->writeResults(*resp);
392 }
393 
394 void
395 art::RootDAQOut::
396 writeFileFormatVersion()
397 {
398  rootOutputFile_->writeFileFormatVersion();
399 }
400 
401 void
402 art::RootDAQOut::
403 writeFileIndex()
404 {
405  rootOutputFile_->writeFileIndex();
406 }
407 
408 void
409 art::RootDAQOut::
410 writeEventHistory()
411 {
412  rootOutputFile_->writeEventHistory();
413 }
414 
415 void
416 art::RootDAQOut::
417 writeProcessConfigurationRegistry()
418 {
419  rootOutputFile_->writeProcessConfigurationRegistry();
420 }
421 
422 void
423 art::RootDAQOut::
424 writeProcessHistoryRegistry()
425 {
426  rootOutputFile_->writeProcessHistoryRegistry();
427 }
428 
429 void
430 art::RootDAQOut::
431 writeParameterSetRegistry()
432 {
433  if (writeParameterSets_)
434  {
435  rootOutputFile_->writeParameterSetRegistry();
436  }
437 }
438 
439 void
440 art::RootDAQOut::
441 writeProductDescriptionRegistry()
442 {
443  rootOutputFile_->writeProductDescriptionRegistry();
444 }
445 
446 void
447 art::RootDAQOut::
448 writeParentageRegistry()
449 {
450  rootOutputFile_->writeParentageRegistry();
451 }
452 
453 void
454 art::RootDAQOut::
455 writeBranchIDListRegistry()
456 {
457  rootOutputFile_->writeBranchIDListRegistry();
458 }
459 
460 void
461 art::RootDAQOut::
462 doWriteFileCatalogMetadata(FileCatalogMetadata::collection_type const& md,
463  FileCatalogMetadata::collection_type const& ssmd)
464 {
465  rootOutputFile_->writeFileCatalogMetadata(fstats_, md, ssmd);
466 }
467 
468 void
469 art::RootDAQOut::writeProductDependencies()
470 {
471  rootOutputFile_->writeProductDependencies();
472 }
473 
474 void
475 art::RootDAQOut::finishEndFile()
476 {
477  std::string const currentFileName{ rootOutputFile_->currentFileName() };
478  rootOutputFile_->writeTTrees();
479  rootOutputFile_.reset();
480  fstats_.recordFileClose();
481  lastClosedFileName_ = PostCloseFileRenamer{ fstats_ }.maybeRenameFile(rootOutputFile_->currentFileName(), filePattern_);
482  detail::logFileAction("Closed output file ", lastClosedFileName_);
483  rpm_.invoke(&ResultsProducer::doClear);
484 }
485 
486 void
487 art::RootDAQOut::
488 doRegisterProducts(MasterProductRegistry& mpr,
489  ModuleDescription const& md)
490 {
491  // Register Results products from ResultsProducers.
492  rpm_.for_each_RPWorker([&mpr, &md](RPWorker& w)
493  {
494  auto const& params = w.params();
495  w.setModuleDescription(ModuleDescription{ params.rpPSetID,
496  params.rpPluginType,
497  md.moduleLabel() + '#' + params.rpLabel,
498  md.processConfiguration() });
499  w.rp().registerProducts(mpr, w.moduleDescription());
500  });
501 }
502 
503 void
504 art::RootDAQOut::setFileStatus(OutputFileStatus const ofs)
505 {
506  if (isFileOpen())
507  rootOutputFile_->setFileStatus(ofs);
508 }
509 
510 bool
511 art::RootDAQOut::
512 isFileOpen() const
513 {
514  TLOG(10) << "RootDAQOut::isFileOpen start";
515  bool ret = rootOutputFile_.get() != nullptr;
516  TLOG(10) << "RootDAQOut::isFileOpen return " << ret;
517  return ret;
518 }
519 
520 void
521 art::RootDAQOut::incrementInputFileNumber()
522 {
523  if (isFileOpen())
524  rootOutputFile_->incrementInputFileNumber();
525 }
526 
527 bool
528 art::RootDAQOut::
529 requestsToCloseFile() const
530 {
531  bool ret;
532  if (isFileOpen())
533  {
534  ret = rootOutputFile_->requestsToCloseFile();
535  }
536  else
537  {
538  ret = false;
539  }
540  return ret;
541 }
542 
543 void
544 art::RootDAQOut::
545 doOpenFile()
546 {
547  if (inputFileCount_ == 0)
548  {
549  throw art::Exception(art::errors::LogicError)
550  << "Attempt to open output file before input file. "
551  << "Please report this to the core framework developers.\n";
552  }
553  rootOutputFile_ = std::make_unique<RootDAQOutFile>(this,
554  unique_filename(tmpDir_ + "/RootDAQOut"),
555  fileProperties_,
556  compressionLevel_,
557  saveMemoryObjectThreshold_,
558  treeMaxVirtualSize_,
559  splitLevel_,
560  basketSize_,
561  dropMetaData_,
562  dropMetaDataForDroppedData_,
563  fastCloning_);
564  fstats_.recordFileOpen();
565  detail::logFileAction("Opened output file with pattern ", filePattern_);
566  }
567 
568 string const&
569 art::RootDAQOut::
570 lastClosedFileName() const
571 {
572  if (lastClosedFileName_.empty())
573  {
574  throw Exception(errors::LogicError, "RootDAQOut::currentFileName(): ")
575  << "called before meaningful.\n";
576  }
577  return lastClosedFileName_;
578 }
579 
580 void
581 art::RootDAQOut::
582 beginJob()
583 {
584  rpm_.invoke(&ResultsProducer::doBeginJob);
585 }
586 
587 void
588 art::RootDAQOut::
589 endJob()
590 {
591  rpm_.invoke(&ResultsProducer::doEndJob);
592 }
593 
594 void
595 art::RootDAQOut::
596 event(EventPrincipal const& ep)
597 {
598  rpm_.for_each_RPWorker([&ep](RPWorker& w)
599  {
600  Event const e{ ep, w.moduleDescription() };
601  w.rp().doEvent(e);
602  });
603 }
604 
605 void
606 art::RootDAQOut::
607 beginSubRun(art::SubRunPrincipal const& srp)
608 {
609  rpm_.for_each_RPWorker([&srp](RPWorker& w)
610  {
611  SubRun const sr{ srp, w.moduleDescription() };
612  w.rp().doBeginSubRun(sr);
613  });
614 }
615 
616 void
617 art::RootDAQOut::
618 endSubRun(art::SubRunPrincipal const& srp)
619 {
620  rpm_.for_each_RPWorker([&srp](RPWorker& w)
621  {
622  SubRun const sr{ srp, w.moduleDescription() };
623  w.rp().doEndSubRun(sr);
624  });
625 }
626 
627 void
628 art::RootDAQOut::
629 beginRun(art::RunPrincipal const& rp)
630 {
631  rpm_.for_each_RPWorker([&rp](RPWorker& w)
632  {
633  Run const r{ rp, w.moduleDescription() };
634  w.rp().doBeginRun(r);
635  });
636 }
637 
638 void
639 art::RootDAQOut::
640 endRun(art::RunPrincipal const& rp)
641 {
642  rpm_.for_each_RPWorker([&rp](RPWorker& w)
643  {
644  Run const r{ rp, w.moduleDescription() };
645  w.rp().doEndRun(r);
646  });
647 }
648 
649 DEFINE_ART_MODULE(art::RootDAQOut)
650 
651 // vim: set sw=2: