CMS 3D CMS Logo

NanoAODOutputModule.cc
Go to the documentation of this file.
1 // -*- C++ -*-
2 //
3 // Package: PhysicsTools/NanoAODOutput
4 // Class : NanoAODOutputModule
5 //
6 // Implementation:
7 // [Notes on implementation]
8 //
9 // Original Author: Christopher Jones
10 // Created: Mon, 07 Aug 2017 14:21:41 GMT
11 //
12 
13 // system include files
14 #include <algorithm>
15 #include <memory>
16 
17 #include "Compression.h"
18 #include "TFile.h"
19 #include "TObjString.h"
20 #include "TROOT.h"
21 #include "TTree.h"
22 #include <string>
23 
24 // user include files
46 
47 #include <iostream>
48 
49 #include "oneapi/tbb/task_arena.h"
50 
52 public:
54  ~NanoAODOutputModule() override;
55 
56  static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
57 
58 private:
59  void write(edm::EventForOutput const& e) override;
61  void writeRun(edm::RunForOutput const&) override;
62  bool isFileOpen() const override;
63  void openFile(edm::FileBlock const&) override;
64  void reallyCloseFile() override;
65 
72  bool m_fakeName; //crab workaround, remove after crab is fixed
76  std::unique_ptr<TFile> m_file;
78 
79  static constexpr int m_firstFlush{1000};
80 
82  public:
83  void branch(TTree& tree) {
84  tree.Branch("run", &m_run, "run/i");
85  tree.Branch("luminosityBlock", &m_luminosityBlock, "luminosityBlock/i");
86  tree.Branch("event", &m_event, "event/l");
87  tree.Branch("bunchCrossing", &m_bunchCrossing, "bunchCrossing/i");
88  }
89  void fill(const edm::EventAuxiliary& aux) {
90  m_run = aux.id().run();
91  m_luminosityBlock = aux.id().luminosityBlock();
92  m_event = aux.id().event();
93  m_bunchCrossing = aux.bunchCrossing();
94  }
95 
96  private:
97  UInt_t m_run;
99  ULong64_t m_event;
102 
104  public:
105  void branch(TTree& tree) {
106  tree.Branch("run", &m_run, "run/i");
107  tree.Branch("luminosityBlock", &m_luminosityBlock, "luminosityBlock/i");
108  }
109  void fill(const edm::LuminosityBlockID& id) {
110  m_run = id.run();
111  m_luminosityBlock = id.value();
112  }
113 
114  private:
115  UInt_t m_run;
118 
120  public:
121  void branch(TTree& tree) { tree.Branch("run", &m_run, "run/i"); }
122  void fill(const edm::RunID& id) { m_run = id.run(); }
123 
124  private:
125  UInt_t m_run;
127 
128  std::vector<TableOutputBranches> m_tables;
129  std::vector<TriggerOutputBranches> m_triggers;
130  bool m_triggers_areSorted = false;
131  std::vector<EventStringOutputBranches> m_evstrings;
132 
133  std::vector<SummaryTableOutputBranches> m_runTables;
134  std::vector<SummaryTableOutputBranches> m_lumiTables;
135  std::vector<LumiOutputBranches> m_lumiTables2;
136  std::vector<TableOutputBranches> m_runFlatTables;
137 
138  std::vector<std::pair<std::string, edm::EDGetToken>> m_nanoMetadata;
139 };
140 
141 //
142 // constants, enums and typedefs
143 //
144 
145 //
146 // static data member definitions
147 //
148 
149 //
150 // constructors and destructor
151 //
154  edm::one::OutputModule<>(pset),
155  m_fileName(pset.getUntrackedParameter<std::string>("fileName")),
156  m_logicalFileName(pset.getUntrackedParameter<std::string>("logicalFileName")),
157  m_compressionLevel(pset.getUntrackedParameter<int>("compressionLevel")),
158  m_compressionAlgorithm(pset.getUntrackedParameter<std::string>("compressionAlgorithm")),
159  m_writeProvenance(pset.getUntrackedParameter<bool>("saveProvenance", true)),
160  m_fakeName(pset.getUntrackedParameter<bool>("fakeNameForCrab", false)),
161  m_autoFlush(pset.getUntrackedParameter<int>("autoFlush", -10000000)),
162  m_processHistoryRegistry() {}
163 
165 
167  //Get data from 'e' and write it to the file
169  jr->eventWrittenToFile(m_jrToken, iEvent.id().run(), iEvent.id().event());
170 
171  if (m_autoFlush) {
172  int64_t events = m_tree->GetEntriesFast();
173  if (events == m_firstFlush) {
174  m_tree->FlushBaskets();
175  float maxMemory;
176  if (m_autoFlush > 0) {
177  // Estimate the memory we'll be using at the first full flush by
178  // linearly scaling the number of events.
179  float percentClusterDone = m_firstFlush / static_cast<float>(m_autoFlush);
180  maxMemory = static_cast<float>(m_tree->GetTotBytes()) / percentClusterDone;
181  } else if (m_tree->GetZipBytes() == 0) {
182  maxMemory = 100 * 1024 * 1024; // Degenerate case of no information in the tree; arbitrary value
183  } else {
184  // Estimate the memory we'll be using by scaling the current compression ratio.
185  float cxnRatio = m_tree->GetTotBytes() / static_cast<float>(m_tree->GetZipBytes());
186  maxMemory = -m_autoFlush * cxnRatio;
187  float percentBytesDone = -m_tree->GetZipBytes() / static_cast<float>(m_autoFlush);
188  m_autoFlush = m_firstFlush / percentBytesDone;
189  }
190  //std::cout << "OptimizeBaskets: total bytes " << m_tree->GetTotBytes() << std::endl;
191  //std::cout << "OptimizeBaskets: zip bytes " << m_tree->GetZipBytes() << std::endl;
192  //std::cout << "OptimizeBaskets: autoFlush " << m_autoFlush << std::endl;
193  //std::cout << "OptimizeBaskets: maxMemory " << static_cast<uint32_t>(maxMemory) << std::endl;
194  //m_tree->OptimizeBaskets(static_cast<uint32_t>(maxMemory), 1, "d");
195  m_tree->OptimizeBaskets(static_cast<uint32_t>(maxMemory), 1, "");
196  }
198  m_tree->FlushBaskets();
199  m_eventsSinceFlush = 0;
200  }
202  }
203 
204  m_commonBranches.fill(iEvent.eventAuxiliary());
205  // fill all tables, starting from main tables and then doing extension tables
206  for (unsigned int extensions = 0; extensions <= 1; ++extensions) {
207  for (auto& t : m_tables)
208  t.fill(iEvent, *m_tree, extensions);
209  }
210  if (!m_triggers_areSorted) { // sort triggers/flags in inverse processHistory order, to save without any special label the most recent ones
211  std::vector<std::string> pnames;
212  for (auto& p : iEvent.processHistory())
213  pnames.push_back(p.processName());
215  return ((std::find(pnames.begin(), pnames.end(), a.processName()) - pnames.begin()) >
216  (std::find(pnames.begin(), pnames.end(), b.processName()) - pnames.begin()));
217  });
218  m_triggers_areSorted = true;
219  }
220  // fill triggers
221  for (auto& t : m_triggers)
222  t.fill(iEvent, *m_tree);
223  // fill event branches
224  for (auto& t : m_evstrings)
225  t.fill(iEvent, *m_tree);
226  tbb::this_task_arena::isolate([&] { m_tree->Fill(); });
227 
229 }
230 
233  jr->reportLumiSection(m_jrToken, iLumi.id().run(), iLumi.id().value());
234 
235  m_commonLumiBranches.fill(iLumi.id());
236 
237  for (auto& t : m_lumiTables)
238  t.fill(iLumi, *m_lumiTree);
239 
240  for (unsigned int extensions = 0; extensions <= 1; ++extensions) {
241  for (auto& t : m_lumiTables2)
242  t.fill(iLumi, *m_lumiTree, extensions);
243  }
244 
245  tbb::this_task_arena::isolate([&] { m_lumiTree->Fill(); });
246 
248 }
249 
252  jr->reportRunNumber(m_jrToken, iRun.id().run());
253 
254  m_commonRunBranches.fill(iRun.id());
255 
256  for (auto& t : m_runTables)
257  t.fill(iRun, *m_runTree);
258 
259  for (unsigned int extensions = 0; extensions <= 1; ++extensions) {
260  for (auto& t : m_runFlatTables)
261  t.fill(iRun, *m_runTree, extensions);
262  }
263 
265  for (const auto& p : m_nanoMetadata) {
266  iRun.getByToken(p.second, hstring);
267  TObjString* tos = dynamic_cast<TObjString*>(m_file->Get(p.first.c_str()));
268  if (tos) {
269  if (hstring->str() != tos->GetString())
270  throw cms::Exception("LogicError", "Inconsistent nanoMetadata " + p.first + " (" + hstring->str() + ")");
271  } else {
272  auto ostr = std::make_unique<TObjString>(hstring->str().c_str());
273  m_file->WriteTObject(ostr.release(), p.first.c_str());
274  }
275  }
276 
277  tbb::this_task_arena::isolate([&] { m_runTree->Fill(); });
278 
280 }
281 
282 bool NanoAODOutputModule::isFileOpen() const { return nullptr != m_file.get(); }
283 
285  m_file = std::make_unique<TFile>(m_fileName.c_str(), "RECREATE", "", m_compressionLevel);
287  cms::Digest branchHash;
288  m_jrToken = jr->outputFileOpened(m_fileName,
290  std::string(),
291  m_fakeName ? "PoolOutputModule" : "NanoAODOutputModule",
294  std::string(),
295  branchHash.digest().toString(),
296  std::vector<std::string>());
297 
298  if (m_compressionAlgorithm == std::string("ZLIB")) {
299  m_file->SetCompressionAlgorithm(ROOT::kZLIB);
300  } else if (m_compressionAlgorithm == std::string("LZMA")) {
301  m_file->SetCompressionAlgorithm(ROOT::kLZMA);
302  } else if (m_compressionAlgorithm == std::string("ZSTD")) {
303  m_file->SetCompressionAlgorithm(ROOT::kZSTD);
304  } else if (m_compressionAlgorithm == std::string("LZ4")) {
305  m_file->SetCompressionAlgorithm(ROOT::kLZ4);
306  } else {
307  throw cms::Exception("Configuration")
308  << "NanoAODOutputModule configured with unknown compression algorithm '" << m_compressionAlgorithm << "'\n"
309  << "Allowed compression algorithms are ZLIB, LZMA, ZSTD, and LZ4\n";
310  }
311  /* Setup file structure here */
312  m_tables.clear();
313  m_triggers.clear();
314  m_triggers_areSorted = false;
315  m_evstrings.clear();
316  m_runTables.clear();
317  m_lumiTables.clear();
318  m_lumiTables2.clear();
319  m_runFlatTables.clear();
320  const auto& keeps = keptProducts();
321  for (const auto& keep : keeps[edm::InEvent]) {
322  if (keep.first->className() == "nanoaod::FlatTable")
323  m_tables.emplace_back(keep.first, keep.second);
324  else if (keep.first->className() == "edm::TriggerResults") {
325  m_triggers.emplace_back(keep.first, keep.second);
326  } else if (keep.first->className() == "std::basic_string<char,std::char_traits<char> >" &&
327  keep.first->productInstanceName() == "genModel") { // friendlyClassName == "String"
328  m_evstrings.emplace_back(keep.first, keep.second, true); // update only at lumiBlock transitions
329  } else
330  throw cms::Exception("Configuration", "NanoAODOutputModule cannot handle class " + keep.first->className());
331  }
332 
333  for (const auto& keep : keeps[edm::InLumi]) {
334  if (keep.first->className() == "nanoaod::MergeableCounterTable")
335  m_lumiTables.push_back(SummaryTableOutputBranches(keep.first, keep.second));
336  else if (keep.first->className() == "nanoaod::UniqueString" && keep.first->moduleLabel() == "nanoMetadata")
337  m_nanoMetadata.emplace_back(keep.first->productInstanceName(), keep.second);
338  else if (keep.first->className() == "nanoaod::FlatTable")
339  m_lumiTables2.push_back(LumiOutputBranches(keep.first, keep.second));
340  else
341  throw cms::Exception(
342  "Configuration",
343  "NanoAODOutputModule cannot handle class " + keep.first->className() + " in LuminosityBlock branch");
344  }
345 
346  for (const auto& keep : keeps[edm::InRun]) {
347  if (keep.first->className() == "nanoaod::MergeableCounterTable")
348  m_runTables.push_back(SummaryTableOutputBranches(keep.first, keep.second));
349  else if (keep.first->className() == "nanoaod::UniqueString" && keep.first->moduleLabel() == "nanoMetadata")
350  m_nanoMetadata.emplace_back(keep.first->productInstanceName(), keep.second);
351  else if (keep.first->className() == "nanoaod::FlatTable")
352  m_runFlatTables.emplace_back(keep.first, keep.second);
353  else
354  throw cms::Exception("Configuration",
355  "NanoAODOutputModule cannot handle class " + keep.first->className() + " in Run branch");
356  }
357 
358  // create the trees
359  m_tree = std::make_unique<TTree>("Events", "Events");
360  m_tree->SetAutoSave(0);
361  m_tree->SetAutoFlush(0);
363 
364  m_lumiTree = std::make_unique<TTree>("LuminosityBlocks", "LuminosityBlocks");
365  m_lumiTree->SetAutoSave(0);
367 
368  m_runTree = std::make_unique<TTree>("Runs", "Runs");
369  m_runTree->SetAutoSave(0);
371 
372  if (m_writeProvenance) {
373  m_metaDataTree = std::make_unique<TTree>(edm::poolNames::metaDataTreeName().c_str(), "Job metadata");
374  m_metaDataTree->SetAutoSave(0);
375  m_parameterSetsTree = std::make_unique<TTree>(edm::poolNames::parameterSetsTreeName().c_str(), "Parameter sets");
376  m_parameterSetsTree->SetAutoSave(0);
377  }
378 }
380  if (m_writeProvenance) {
381  int basketSize = 16384; // fixme configurable?
384  if (m_metaDataTree->GetNbranches() != 0) {
385  m_metaDataTree->SetEntries(-1);
386  }
387  if (m_parameterSetsTree->GetNbranches() != 0) {
388  m_parameterSetsTree->SetEntries(-1);
389  }
390  }
391  m_file->Write();
392  m_file->Close();
393  m_file.reset();
394  m_tree.release(); // apparently root has ownership
395  m_lumiTree.release(); //
396  m_runTree.release(); //
397  m_metaDataTree.release(); //
398  m_parameterSetsTree.release(); //
401 }
402 
405 
406  desc.addUntracked<std::string>("fileName");
407  desc.addUntracked<std::string>("logicalFileName", "");
408 
409  desc.addUntracked<int>("compressionLevel", 9)->setComment("ROOT compression level of output file.");
410  desc.addUntracked<std::string>("compressionAlgorithm", "ZLIB")
411  ->setComment("Algorithm used to compress data in the ROOT output file, allowed values are ZLIB and LZMA");
412  desc.addUntracked<bool>("saveProvenance", true)
413  ->setComment("Save process provenance information, e.g. for edmProvDump");
414  desc.addUntracked<bool>("fakeNameForCrab", false)
415  ->setComment(
416  "Change the OutputModule name in the fwk job report to fake PoolOutputModule. This is needed to run on cran "
417  "(and publish) till crab is fixed");
418  desc.addUntracked<int>("autoFlush", -10000000)->setComment("Autoflush parameter for ROOT file");
419 
420  //replace with whatever you want to get from the EDM by default
421  const std::vector<std::string> keep = {"drop *",
422  "keep nanoaodFlatTable_*Table_*_*",
423  "keep edmTriggerResults_*_*_*",
424  "keep String_*_genModel_*",
425  "keep nanoaodMergeableCounterTable_*Table_*_*",
426  "keep nanoaodUniqueString_nanoMetadata_*_*"};
428 
429  //Used by Workflow management for their own meta data
431  dataSet.setAllowAnything();
432  desc.addUntracked<edm::ParameterSetDescription>("dataset", dataSet)
433  ->setComment("PSet is only used by Data Operations and not by this module.");
434 
436  branchSet.setAllowAnything();
437  desc.add<edm::ParameterSetDescription>("branches", branchSet);
438 
439  descriptions.addDefault(desc);
440 }
441 
std::unique_ptr< TTree > m_runTree
std::vector< TableOutputBranches > m_runFlatTables
std::string const & metaDataTreeName()
Definition: BranchType.cc:159
std::unique_ptr< TTree > m_lumiTree
std::vector< SummaryTableOutputBranches > m_lumiTables
std::unique_ptr< TTree > m_tree
void setAllowAnything()
allow any parameter label/value pairs
void fillParameterSetBranch(TTree *parameterSetsTree, int basketSize)
bool registerProcessHistory(ProcessHistory const &processHistory)
void fillProcessHistoryBranch(TTree *metaDataTree, int basketSize, ProcessHistoryRegistry const &processHistoryRegistry)
NanoAODOutputModule(edm::ParameterSet const &pset)
bool isFileOpen() const override
void fill(const edm::LuminosityBlockID &id)
void reportRunNumber(JobReport::Token token, unsigned int run)
Definition: JobReport.cc:505
void find(edm::Handle< EcalRecHitCollection > &hits, DetId thisDet, std::vector< EcalRecHitCollection::const_iterator > &hit, bool debug=false)
Definition: FindCaloHit.cc:19
BasicHandle getByToken(EDGetToken token, TypeID const &typeID) const
ModuleDescription const & description() const
std::unique_ptr< TTree > m_parameterSetsTree
void writeRun(edm::RunForOutput const &) override
class NanoAODOutputModule::CommonLumiBranches m_commonLumiBranches
std::unique_ptr< TTree > m_metaDataTree
std::unique_ptr< TFile > m_file
int iEvent
Definition: GenABIO.cc:224
void addDefault(ParameterSetDescription const &psetDescription)
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
MD5Result digest()
Definition: Digest.cc:171
std::vector< EventStringOutputBranches > m_evstrings
std::vector< SummaryTableOutputBranches > m_runTables
std::string const & parameterSetsTreeName()
Definition: BranchType.cc:216
std::vector< TableOutputBranches > m_tables
static constexpr int m_firstFlush
std::vector< TriggerOutputBranches > m_triggers
std::vector< std::pair< std::string, edm::EDGetToken > > m_nanoMetadata
#define DEFINE_FWK_MODULE(type)
Definition: MakerMacros.h:16
std::vector< LumiOutputBranches > m_lumiTables2
RunNumber_t run() const
std::size_t Token
Definition: JobReport.h:106
void openFile(edm::FileBlock const &) override
std::string createGlobalIdentifier(bool binary=false)
void reallyCloseFile() override
SelectedProductsForBranchType const & keptProducts() const
void eventWrittenToFile(Token fileToken, RunNumber_t run, EventNumber_t event)
Definition: JobReport.cc:462
double b
Definition: hdecay.h:118
LuminosityBlockID const & id() const
const std::string & str() const
Definition: UniqueString.h:12
virtual ProcessHistory const & processHistory() const
RunID const & id() const
Definition: RunForOutput.h:55
HLT enums.
double a
Definition: hdecay.h:119
void outputFileClosed(Token fileToken)
Definition: JobReport.cc:467
void write(edm::EventForOutput const &e) override
class NanoAODOutputModule::CommonRunBranches m_commonRunBranches
void fill(const edm::EventAuxiliary &aux)
edm::JobReport::Token m_jrToken
class NanoAODOutputModule::CommonEventBranches m_commonBranches
Definition: tree.py:1
edm::ProcessHistoryRegistry m_processHistoryRegistry
static void fillDescription(ParameterSetDescription &desc, std::vector< std::string > const &iDefaultOutputCommands=ProductSelectorRules::defaultSelectionStrings())
std::string toString() const
Definition: Digest.cc:95
int events
std::string m_compressionAlgorithm
RunNumber_t run() const
Definition: RunID.h:26
void reportLumiSection(JobReport::Token token, unsigned int run, unsigned int lumiSectId, unsigned long nEvents=0)
Definition: JobReport.cc:494
void writeLuminosityBlock(edm::LuminosityBlockForOutput const &) override