File indexing completed on 2025-08-12 22:11:54
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <algorithm>
0016 #include <memory>
0017
0018 #include "Compression.h"
0019 #include "TFile.h"
0020 #include "TObjString.h"
0021 #include "TROOT.h"
0022 #include "TTree.h"
0023 #include <string>
0024
0025
0026 #include "FWCore/Framework/interface/one/OutputModule.h"
0027 #include "FWCore/Framework/interface/RunForOutput.h"
0028 #include "FWCore/Framework/interface/LuminosityBlockForOutput.h"
0029 #include "FWCore/Framework/interface/EventForOutput.h"
0030 #include "FWCore/ServiceRegistry/interface/Service.h"
0031 #include "FWCore/Framework/interface/MakerMacros.h"
0032 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0033 #include "FWCore/MessageLogger/interface/JobReport.h"
0034 #include "FWCore/Utilities/interface/GlobalIdentifier.h"
0035 #include "FWCore/Utilities/interface/Digest.h"
0036 #include "IOPool/Provenance/interface/CommonProvenanceFiller.h"
0037 #include "DataFormats/Provenance/interface/BranchType.h"
0038 #include "DataFormats/Provenance/interface/BranchDescription.h"
0039 #include "DataFormats/Provenance/interface/ProcessHistoryRegistry.h"
0040 #include "DataFormats/NanoAOD/interface/FlatTable.h"
0041 #include "L1TriggerScouting/Utilities/plugins/OrbitTableOutputBranches.h"
0042 #include "L1TriggerScouting/Utilities/plugins/SelectedBxTableOutputBranches.h"
0043
0044 #include "oneapi/tbb/task_arena.h"
0045
0046 class OrbitNanoAODOutputModule : public edm::one::OutputModule<> {
0047 public:
0048 OrbitNanoAODOutputModule(edm::ParameterSet const& pset);
0049 ~OrbitNanoAODOutputModule() override;
0050
0051 static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
0052
0053 private:
0054 void write(edm::EventForOutput const& e) override;
0055 void writeLuminosityBlock(edm::LuminosityBlockForOutput const&) override;
0056 void writeRun(edm::RunForOutput const&) override;
0057 bool isFileOpen() const override;
0058 void openFile(edm::FileBlock const&) override;
0059 void reallyCloseFile() override;
0060
0061 std::string m_fileName;
0062 std::string m_logicalFileName;
0063 int m_compressionLevel;
0064 int m_eventsSinceFlush{0};
0065 std::string m_compressionAlgorithm;
0066 bool m_skipEmptyBXs;
0067 bool m_writeProvenance;
0068 bool m_fakeName;
0069 int m_autoFlush;
0070 edm::ProcessHistoryRegistry m_processHistoryRegistry;
0071 edm::JobReport::Token m_jrToken;
0072 std::unique_ptr<TFile> m_file;
0073 std::unique_ptr<TTree> m_tree, m_lumiTree, m_runTree, m_metaDataTree, m_parameterSetsTree;
0074
0075 static constexpr int m_firstFlush{1000};
0076
0077 class CommonEventBranches {
0078 public:
0079 void branch(TTree& tree) {
0080 tree.Branch("run", &m_run, "run/i");
0081 tree.Branch("luminosityBlock", &m_luminosityBlock, "luminosityBlock/i");
0082 tree.Branch("bunchCrossing", &m_bunchCrossing, "bunchCrossing/i");
0083 tree.Branch("orbitNumber", &m_orbitNumber, "orbitNumber/i");
0084 }
0085 void fill(const edm::EventAuxiliary& aux) {
0086 m_run = aux.id().run();
0087 m_luminosityBlock = aux.id().luminosityBlock();
0088 m_orbitNumber = aux.id().event();
0089 }
0090 void setBx(unsigned bx) { m_bunchCrossing = bx; }
0091
0092 private:
0093 UInt_t m_run;
0094 UInt_t m_luminosityBlock;
0095 UInt_t m_bunchCrossing;
0096 UInt_t m_orbitNumber;
0097 } m_commonBranches;
0098
0099 class CommonLumiBranches {
0100 public:
0101 void branch(TTree& tree) {
0102 tree.Branch("run", &m_run, "run/i");
0103 tree.Branch("luminosityBlock", &m_luminosityBlock, "luminosityBlock/i");
0104 tree.Branch("nOrbits", &m_orbits, "nOrbits/i");
0105 }
0106 void fill(const edm::LuminosityBlockID& id, unsigned int nOrbits) {
0107 m_run = id.run();
0108 m_luminosityBlock = id.value();
0109 m_orbits = nOrbits;
0110 }
0111
0112 private:
0113 UInt_t m_run;
0114 UInt_t m_luminosityBlock;
0115 UInt_t m_orbits;
0116 } m_commonLumiBranches;
0117
0118 class CommonRunBranches {
0119 public:
0120 void branch(TTree& tree) { tree.Branch("run", &m_run, "run/i"); }
0121 void fill(const edm::RunID& id) { m_run = id.run(); }
0122
0123 private:
0124 UInt_t m_run;
0125 } m_commonRunBranches;
0126
0127 std::vector<OrbitTableOutputBranches> m_tables;
0128 std::vector<SelectedBxTableOutputBranches> m_selbxs;
0129 unsigned int m_nOrbits;
0130
0131 std::vector<std::pair<std::string, edm::EDGetToken>> m_nanoMetadata;
0132
0133 edm::EDGetTokenT<std::vector<unsigned>> m_bxMaskToken;
0134 std::vector<unsigned> allBXs_;
0135 };
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148 OrbitNanoAODOutputModule::OrbitNanoAODOutputModule(edm::ParameterSet const& pset)
0149 : edm::one::OutputModuleBase::OutputModuleBase(pset),
0150 edm::one::OutputModule<>(pset),
0151 m_fileName(pset.getUntrackedParameter<std::string>("fileName")),
0152 m_logicalFileName(pset.getUntrackedParameter<std::string>("logicalFileName")),
0153 m_compressionLevel(pset.getUntrackedParameter<int>("compressionLevel")),
0154 m_compressionAlgorithm(pset.getUntrackedParameter<std::string>("compressionAlgorithm")),
0155 m_skipEmptyBXs(pset.getParameter<bool>("skipEmptyBXs")),
0156 m_writeProvenance(pset.getUntrackedParameter<bool>("saveProvenance", true)),
0157 m_fakeName(pset.getUntrackedParameter<bool>("fakeNameForCrab", false)),
0158 m_autoFlush(pset.getUntrackedParameter<int>("autoFlush", -10000000)),
0159 m_processHistoryRegistry(),
0160 m_nOrbits(0) {
0161 edm::InputTag bxMask = pset.getParameter<edm::InputTag>("selectedBx");
0162 if (!bxMask.label().empty()) {
0163 m_bxMaskToken = consumes<std::vector<unsigned>>(bxMask);
0164 } else {
0165 allBXs_.resize(l1ScoutingRun3::OrbitFlatTable::NBX);
0166 for (unsigned int i = 0; i < l1ScoutingRun3::OrbitFlatTable::NBX; ++i) {
0167 allBXs_[i] = i + 1;
0168 }
0169 }
0170 }
0171
0172 OrbitNanoAODOutputModule::~OrbitNanoAODOutputModule() {}
0173
0174 void OrbitNanoAODOutputModule::write(edm::EventForOutput const& iEvent) {
0175
0176 edm::Service<edm::JobReport> jr;
0177 jr->eventWrittenToFile(m_jrToken, iEvent.id().run(), iEvent.id().event());
0178 m_nOrbits++;
0179
0180 if (m_autoFlush) {
0181 int64_t events = m_tree->GetEntriesFast();
0182 if (events == m_firstFlush) {
0183 m_tree->FlushBaskets();
0184 float maxMemory;
0185 if (m_autoFlush > 0) {
0186
0187
0188 float percentClusterDone = m_firstFlush / static_cast<float>(m_autoFlush);
0189 maxMemory = static_cast<float>(m_tree->GetTotBytes()) / percentClusterDone;
0190 } else if (m_tree->GetZipBytes() == 0) {
0191 maxMemory = 100 * 1024 * 1024;
0192 } else {
0193
0194 float cxnRatio = m_tree->GetTotBytes() / static_cast<float>(m_tree->GetZipBytes());
0195 maxMemory = -m_autoFlush * cxnRatio;
0196 float percentBytesDone = -m_tree->GetZipBytes() / static_cast<float>(m_autoFlush);
0197 m_autoFlush = m_firstFlush / percentBytesDone;
0198 }
0199
0200
0201
0202
0203
0204 m_tree->OptimizeBaskets(static_cast<uint32_t>(maxMemory), 1, "");
0205 }
0206 if (m_eventsSinceFlush == m_autoFlush) {
0207 m_tree->FlushBaskets();
0208 m_eventsSinceFlush = 0;
0209 }
0210 m_eventsSinceFlush++;
0211 }
0212
0213 m_commonBranches.fill(iEvent.eventAuxiliary());
0214
0215 for (unsigned int extensions = 0; extensions <= 1; ++extensions) {
0216 for (auto& t : m_tables) {
0217 t.beginFill(iEvent, *m_tree, extensions);
0218 }
0219 }
0220
0221 for (auto& t : m_selbxs) {
0222 t.beginFill(iEvent, *m_tree);
0223 }
0224
0225 const std::vector<unsigned>* selbx = &allBXs_;
0226 if (!m_bxMaskToken.isUninitialized()) {
0227 edm::Handle<std::vector<unsigned>> handle;
0228 iEvent.getByToken(m_bxMaskToken, handle);
0229 selbx = &*handle;
0230 }
0231
0232 tbb::this_task_arena::isolate([&] {
0233 for (unsigned bx : *selbx) {
0234 if (m_skipEmptyBXs) {
0235 bool empty = true;
0236 for (auto& t : m_tables) {
0237 if (t.hasBx(bx)) {
0238 empty = false;
0239 break;
0240 }
0241 }
0242 if (empty) {
0243 continue;
0244 }
0245 }
0246
0247 m_commonBranches.setBx(bx);
0248 for (auto& t : m_tables) {
0249 t.fillBx(bx);
0250 }
0251 for (auto& t : m_selbxs) {
0252 t.fillBx(bx);
0253 }
0254 m_tree->Fill();
0255 }
0256 });
0257
0258
0259 for (auto& t : m_tables) {
0260 t.endFill();
0261 }
0262 for (auto& t : m_selbxs) {
0263 t.endFill();
0264 }
0265 m_processHistoryRegistry.registerProcessHistory(iEvent.processHistory());
0266 }
0267
0268 void OrbitNanoAODOutputModule::writeLuminosityBlock(edm::LuminosityBlockForOutput const& iLumi) {
0269 edm::Service<edm::JobReport> jr;
0270 jr->reportLumiSection(m_jrToken, iLumi.id().run(), iLumi.id().value());
0271
0272 m_commonLumiBranches.fill(iLumi.id(), m_nOrbits);
0273
0274 tbb::this_task_arena::isolate([&] { m_lumiTree->Fill(); });
0275
0276 m_processHistoryRegistry.registerProcessHistory(iLumi.processHistory());
0277
0278 m_nOrbits = 0;
0279 }
0280
0281 void OrbitNanoAODOutputModule::writeRun(edm::RunForOutput const& iRun) {
0282 edm::Service<edm::JobReport> jr;
0283 jr->reportRunNumber(m_jrToken, iRun.id().run());
0284
0285 m_commonRunBranches.fill(iRun.id());
0286
0287 tbb::this_task_arena::isolate([&] { m_runTree->Fill(); });
0288
0289 m_processHistoryRegistry.registerProcessHistory(iRun.processHistory());
0290 }
0291
0292 bool OrbitNanoAODOutputModule::isFileOpen() const { return nullptr != m_file.get(); }
0293
0294 void OrbitNanoAODOutputModule::openFile(edm::FileBlock const&) {
0295 m_file = std::make_unique<TFile>(m_fileName.c_str(), "RECREATE", "", m_compressionLevel);
0296 edm::Service<edm::JobReport> jr;
0297 cms::Digest branchHash;
0298 m_jrToken = jr->outputFileOpened(m_fileName,
0299 m_logicalFileName,
0300 std::string(),
0301 m_fakeName ? "PoolOutputModule" : "OrbitNanoAODOutputModule",
0302 description().moduleLabel(),
0303 edm::createGlobalIdentifier(),
0304 std::string(),
0305 branchHash.digest().toString(),
0306 std::vector<std::string>());
0307
0308 if (m_compressionAlgorithm == std::string("ZLIB")) {
0309 m_file->SetCompressionAlgorithm(ROOT::RCompressionSetting::EAlgorithm::kZLIB);
0310 } else if (m_compressionAlgorithm == std::string("LZMA")) {
0311 m_file->SetCompressionAlgorithm(ROOT::RCompressionSetting::EAlgorithm::kLZMA);
0312 } else if (m_compressionAlgorithm == std::string("ZSTD")) {
0313 m_file->SetCompressionAlgorithm(ROOT::RCompressionSetting::EAlgorithm::kZSTD);
0314 } else if (m_compressionAlgorithm == std::string("LZ4")) {
0315 m_file->SetCompressionAlgorithm(ROOT::RCompressionSetting::EAlgorithm::kLZ4);
0316 } else {
0317 throw cms::Exception("Configuration")
0318 << "OrbitNanoAODOutputModule configured with unknown compression algorithm '" << m_compressionAlgorithm << "'\n"
0319 << "Allowed compression algorithms are ZLIB, LZMA, ZSTD, and LZ4\n";
0320 }
0321
0322 m_tables.clear();
0323 const auto& keeps = keptProducts();
0324 for (const auto& keep : keeps[edm::InEvent]) {
0325 if (keep.first->className() == "l1ScoutingRun3::OrbitFlatTable")
0326 m_tables.emplace_back(keep.first, keep.second);
0327 else if (keep.first->className() == "std::vector<unsigned int>")
0328 m_selbxs.emplace_back(keep.first, keep.second);
0329 else
0330 throw cms::Exception("Configuration", "OrbitNanoAODOutputModule cannot handle class " + keep.first->className());
0331 }
0332
0333
0334 m_tree = std::make_unique<TTree>("Events", "Events");
0335 m_tree->SetAutoSave(0);
0336 m_tree->SetAutoFlush(0);
0337 m_commonBranches.branch(*m_tree);
0338
0339 m_lumiTree = std::make_unique<TTree>("LuminosityBlocks", "LuminosityBlocks");
0340 m_lumiTree->SetAutoSave(0);
0341 m_commonLumiBranches.branch(*m_lumiTree);
0342
0343 m_runTree = std::make_unique<TTree>("Runs", "Runs");
0344 m_runTree->SetAutoSave(0);
0345 m_commonRunBranches.branch(*m_runTree);
0346
0347 if (m_writeProvenance) {
0348 m_metaDataTree = std::make_unique<TTree>(edm::poolNames::metaDataTreeName().c_str(), "Job metadata");
0349 m_metaDataTree->SetAutoSave(0);
0350 m_parameterSetsTree = std::make_unique<TTree>(edm::poolNames::parameterSetsTreeName().c_str(), "Parameter sets");
0351 m_parameterSetsTree->SetAutoSave(0);
0352 }
0353 }
0354 void OrbitNanoAODOutputModule::reallyCloseFile() {
0355 if (m_writeProvenance) {
0356 int basketSize = 16384;
0357 edm::fillParameterSetBranch(m_parameterSetsTree.get(), basketSize);
0358 edm::fillProcessHistoryBranch(m_metaDataTree.get(), basketSize, m_processHistoryRegistry);
0359 if (m_metaDataTree->GetNbranches() != 0) {
0360 m_metaDataTree->SetEntries(-1);
0361 }
0362 if (m_parameterSetsTree->GetNbranches() != 0) {
0363 m_parameterSetsTree->SetEntries(-1);
0364 }
0365 }
0366 m_file->Write();
0367 m_file->Close();
0368 m_file.reset();
0369 m_tree.release();
0370 m_lumiTree.release();
0371 m_runTree.release();
0372 m_metaDataTree.release();
0373 m_parameterSetsTree.release();
0374 edm::Service<edm::JobReport> jr;
0375 jr->outputFileClosed(m_jrToken);
0376 }
0377
0378 void OrbitNanoAODOutputModule::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
0379 edm::ParameterSetDescription desc;
0380
0381 desc.addUntracked<std::string>("fileName");
0382 desc.addUntracked<std::string>("logicalFileName", "");
0383
0384 desc.addUntracked<int>("compressionLevel", 9)->setComment("ROOT compression level of output file.");
0385 desc.addUntracked<std::string>("compressionAlgorithm", "ZLIB")
0386 ->setComment("Algorithm used to compress data in the ROOT output file, allowed values are ZLIB and LZMA");
0387 desc.add<bool>("skipEmptyBXs", false)->setComment("Skip BXs where all input collections are empty");
0388 desc.addUntracked<bool>("saveProvenance", true)
0389 ->setComment("Save process provenance information, e.g. for edmProvDump");
0390 desc.addUntracked<bool>("fakeNameForCrab", false)
0391 ->setComment(
0392 "Change the OutputModule name in the fwk job report to fake PoolOutputModule. This is needed to run on "
0393 "crab "
0394 "(and publish) till crab is fixed");
0395 desc.addUntracked<int>("autoFlush", -10000000)->setComment("Autoflush parameter for ROOT file");
0396
0397
0398 const std::vector<std::string> keep = {"drop *", "keep l1ScoutingRun3OrbitFlatTable_*Table_*_*"};
0399 edm::one::OutputModule<>::fillDescription(desc, keep);
0400
0401
0402 edm::ParameterSetDescription dataSet;
0403 dataSet.setAllowAnything();
0404 desc.addUntracked<edm::ParameterSetDescription>("dataset", dataSet)
0405 ->setComment("PSet is only used by Data Operations and not by this module.");
0406
0407 edm::ParameterSetDescription branchSet;
0408 branchSet.setAllowAnything();
0409 desc.add<edm::ParameterSetDescription>("branches", branchSet);
0410
0411 desc.add<edm::InputTag>("selectedBx", edm::InputTag())->setComment("selected Bx (1-3564)");
0412 descriptions.addDefault(desc);
0413 }
0414
0415 DEFINE_FWK_MODULE(OrbitNanoAODOutputModule);