artdaq  v3_01_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 #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  TLOG(10) << "RootDAQOut::write begin - line " << __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  TLOG(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  TLOG(10) << "RootDAQOut::isFileOpen start";
509  bool ret = rootOutputFile_.get() != nullptr;
510  TLOG(10) << "RootDAQOut::isFileOpen return " << 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  }
529  else {
530  ret = false;
531  }
532  return ret;
533 }
534 
535 #if ART_HEX_VERSION < 0x20703
536 art::Boundary
537 art::RootDAQOut::
538 fileSwitchBoundary() const
539 {
540  TLOG(10) << "RootDAQOut::fileSwitchBoundary start";
541  auto bb = fileProperties_.granularity();
542  TLOG(10) << "RootDAQOut::fileSwitchBoundary done/return";
543  return bb;
544 }
545 #endif
546 
547 void
548 art::RootDAQOut::
549 doOpenFile()
550 {
551  if (inputFileCount_ == 0) {
552  throw art::Exception(art::errors::LogicError)
553  << "Attempt to open output file before input file. "
554  << "Please report this to the core framework developers.\n";
555  }
556  rootOutputFile_ = std::make_unique<RootDAQOutFile>(this,
557  unique_filename(tmpDir_ + "/RootDAQOut"),
558  fileProperties_,
559  compressionLevel_,
560  saveMemoryObjectThreshold_,
561  treeMaxVirtualSize_,
562  splitLevel_,
563  basketSize_,
564  dropMetaData_,
565  dropMetaDataForDroppedData_,
566  fastCloning_);
567  fstats_.recordFileOpen();
568  detail::logFileAction("Opened output file with pattern ", filePattern_);
569 }
570 
571 string const&
572 art::RootDAQOut::
573 lastClosedFileName() const
574 {
575  if (lastClosedFileName_.empty()) {
576  throw Exception(errors::LogicError, "RootDAQOut::currentFileName(): ")
577  << "called before meaningful.\n";
578  }
579  return lastClosedFileName_;
580 }
581 
582 void
583 art::RootDAQOut::
584 beginJob()
585 {
586  rpm_.invoke(&ResultsProducer::doBeginJob);
587 }
588 
589 void
590 art::RootDAQOut::
591 endJob()
592 {
593  rpm_.invoke(&ResultsProducer::doEndJob);
594 }
595 
596 void
597 art::RootDAQOut::
598 event(EventPrincipal const& ep)
599 {
600  rpm_.for_each_RPWorker([&ep](RPWorker& w) {
601  Event const e{ ep, w.moduleDescription() };
602  w.rp().doEvent(e);
603  });
604 }
605 
606 void
607 art::RootDAQOut::
608 beginSubRun(art::SubRunPrincipal const& srp)
609 {
610  rpm_.for_each_RPWorker([&srp](RPWorker& w) {
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  SubRun const sr{ srp, w.moduleDescription() };
622  w.rp().doEndSubRun(sr);
623  });
624 }
625 
626 void
627 art::RootDAQOut::
628 beginRun(art::RunPrincipal const& rp)
629 {
630  rpm_.for_each_RPWorker([&rp](RPWorker& w) {
631  Run const r{ rp, w.moduleDescription() };
632  w.rp().doBeginRun(r);
633  });
634 }
635 
636 void
637 art::RootDAQOut::
638 endRun(art::RunPrincipal const& rp)
639 {
640  rpm_.for_each_RPWorker([&rp](RPWorker& w) {
641  Run const r{ rp, w.moduleDescription() };
642  w.rp().doEndRun(r);
643  });
644 }
645 
646 DEFINE_ART_MODULE(art::RootDAQOut)
647 
648 // vim: set sw=2: