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