Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-02-14 03:16:47

0001 
0002 #include "IOPool/Output/src/RootOutputFile.h"
0003 
0004 #include "FWCore/Utilities/interface/GlobalIdentifier.h"
0005 
0006 #include "DataFormats/Provenance/interface/EventAuxiliary.h"
0007 #include "DataFormats/Provenance/interface/ProductDescription.h"
0008 #include "FWCore/Version/interface/GetFileFormatVersion.h"
0009 #include "DataFormats/Provenance/interface/FileFormatVersion.h"
0010 #include "FWCore/Utilities/interface/EDMException.h"
0011 #include "FWCore/Utilities/interface/Algorithms.h"
0012 #include "FWCore/Utilities/interface/Digest.h"
0013 #include "FWCore/Common/interface/OutputProcessBlockHelper.h"
0014 #include "FWCore/Framework/interface/FileBlock.h"
0015 #include "FWCore/Framework/interface/EventForOutput.h"
0016 #include "FWCore/Framework/interface/LuminosityBlockForOutput.h"
0017 #include "FWCore/Framework/interface/MergeableRunProductMetadata.h"
0018 #include "FWCore/Framework/interface/OccurrenceForOutput.h"
0019 #include "FWCore/Framework/interface/ProcessBlockForOutput.h"
0020 #include "FWCore/Framework/interface/RunForOutput.h"
0021 #include "FWCore/MessageLogger/interface/JobReport.h"
0022 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0023 #include "DataFormats/Common/interface/BasicHandle.h"
0024 #include "DataFormats/Provenance/interface/ProductDependencies.h"
0025 #include "DataFormats/Provenance/interface/BranchIDList.h"
0026 #include "DataFormats/Provenance/interface/Parentage.h"
0027 #include "DataFormats/Provenance/interface/ParentageRegistry.h"
0028 #include "DataFormats/Provenance/interface/EventID.h"
0029 #include "DataFormats/Provenance/interface/EventToProcessBlockIndexes.h"
0030 #include "DataFormats/Provenance/interface/ParameterSetBlob.h"
0031 #include "DataFormats/Provenance/interface/ParameterSetID.h"
0032 #include "DataFormats/Provenance/interface/ProcessHistoryID.h"
0033 #include "DataFormats/Provenance/interface/ProductRegistry.h"
0034 #include "DataFormats/Provenance/interface/StoredProcessBlockHelper.h"
0035 #include "DataFormats/Provenance/interface/ThinnedAssociationsHelper.h"
0036 #include "DataFormats/Provenance/interface/ProductRegistry.h"
0037 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0038 #include "FWCore/ParameterSet/interface/Registry.h"
0039 #include "FWCore/ServiceRegistry/interface/Service.h"
0040 #include "FWCore/Utilities/interface/ExceptionPropagate.h"
0041 #include "IOPool/Common/interface/getWrapperBasePtr.h"
0042 #include "IOPool/Provenance/interface/CommonProvenanceFiller.h"
0043 
0044 #include "TTree.h"
0045 #include "TFile.h"
0046 #include "TClass.h"
0047 #include "Rtypes.h"
0048 #include "RVersion.h"
0049 
0050 #include "Compression.h"
0051 
0052 #include <algorithm>
0053 #include <iomanip>
0054 #include <sstream>
0055 
0056 namespace edm {
0057 
0058   namespace {
0059     bool sorterForJobReportHash(ProductDescription const* lh, ProductDescription const* rh) {
0060       return lh->fullClassName() < rh->fullClassName()               ? true
0061              : lh->fullClassName() > rh->fullClassName()             ? false
0062              : lh->moduleLabel() < rh->moduleLabel()                 ? true
0063              : lh->moduleLabel() > rh->moduleLabel()                 ? false
0064              : lh->productInstanceName() < rh->productInstanceName() ? true
0065              : lh->productInstanceName() > rh->productInstanceName() ? false
0066              : lh->processName() < rh->processName()                 ? true
0067                                                                      : false;
0068     }
0069 
0070     TFile* openTFile(char const* name, int compressionLevel) {
0071       TFile* file = TFile::Open(name, "recreate", "", compressionLevel);
0072       std::exception_ptr e = edm::threadLocalException::getException();
0073       if (e != std::exception_ptr()) {
0074         edm::threadLocalException::setException(std::exception_ptr());
0075         std::rethrow_exception(e);
0076       }
0077       return file;
0078     }
0079   }  // namespace
0080 
0081   RootOutputFile::RootOutputFile(PoolOutputModule* om,
0082                                  std::string const& fileName,
0083                                  std::string const& logicalFileName,
0084                                  std::vector<std::string> const& processesWithSelectedMergeableRunProducts,
0085                                  std::string const& overrideGUID)
0086       : file_(fileName),
0087         logicalFile_(logicalFileName),
0088         reportToken_(0),
0089         om_(om),
0090         whyNotFastClonable_(om_->whyNotFastClonable()),
0091         canFastCloneAux_(false),
0092         filePtr_(openTFile(file_.c_str(), om_->compressionLevel())),
0093         fid_(),
0094         eventEntryNumber_(0LL),
0095         lumiEntryNumber_(0LL),
0096         runEntryNumber_(0LL),
0097         indexIntoFile_(),
0098         storedMergeableRunProductMetadata_(processesWithSelectedMergeableRunProducts),
0099         nEventsInLumi_(0),
0100         metaDataTree_(nullptr),
0101         parameterSetsTree_(nullptr),
0102         parentageTree_(nullptr),
0103         lumiAux_(),
0104         runAux_(),
0105         pEventAux_(nullptr),
0106         pLumiAux_(&lumiAux_),
0107         pRunAux_(&runAux_),
0108         eventEntryInfoVector_(),
0109         pEventEntryInfoVector_(&eventEntryInfoVector_),
0110         pBranchListIndexes_(nullptr),
0111         pEventSelectionIDs_(nullptr),
0112         eventTree_(filePtr(), InEvent, om_->splitLevel(), om_->treeMaxVirtualSize()),
0113         lumiTree_(filePtr(), InLumi, om_->splitLevel(), om_->treeMaxVirtualSize()),
0114         runTree_(filePtr(), InRun, om_->splitLevel(), om_->treeMaxVirtualSize()),
0115         dataTypeReported_(false),
0116         processHistoryRegistry_(),
0117         parentageIDs_(),
0118         branchesWithStoredHistory_(),
0119         wrapperBaseTClass_(TClass::GetClass("edm::WrapperBase")) {
0120     std::vector<std::string> const& processesWithProcessBlockProducts =
0121         om_->outputProcessBlockHelper().processesWithProcessBlockProducts();
0122     for (auto const& processName : processesWithProcessBlockProducts) {
0123       processBlockTrees_.emplace_back(std::make_unique<RootOutputTree>(
0124           filePtr(), InProcess, om_->splitLevel(), om_->treeMaxVirtualSize(), processName));
0125     }
0126 
0127     if (om_->compressionAlgorithm() == std::string("ZLIB")) {
0128       filePtr_->SetCompressionAlgorithm(ROOT::RCompressionSetting::EAlgorithm::kZLIB);
0129     } else if (om_->compressionAlgorithm() == std::string("LZMA")) {
0130       filePtr_->SetCompressionAlgorithm(ROOT::RCompressionSetting::EAlgorithm::kLZMA);
0131     } else if (om_->compressionAlgorithm() == std::string("ZSTD")) {
0132       filePtr_->SetCompressionAlgorithm(ROOT::RCompressionSetting::EAlgorithm::kZSTD);
0133     } else if (om_->compressionAlgorithm() == std::string("LZ4")) {
0134       filePtr_->SetCompressionAlgorithm(ROOT::RCompressionSetting::EAlgorithm::kLZ4);
0135     } else {
0136       throw Exception(errors::Configuration)
0137           << "PoolOutputModule configured with unknown compression algorithm '" << om_->compressionAlgorithm() << "'\n"
0138           << "Allowed compression algorithms are ZLIB, LZMA, LZ4, and ZSTD\n";
0139     }
0140     if (-1 != om->eventAutoFlushSize()) {
0141       eventTree_.setAutoFlush(-1 * om->eventAutoFlushSize());
0142     }
0143     if (om_->compactEventAuxiliary()) {
0144       eventTree_.addAuxiliary<EventAuxiliary>(
0145           BranchTypeToAuxiliaryBranchName(InEvent), pEventAux_, om_->auxItems()[InEvent].basketSize_, false);
0146       eventTree_.tree()->SetBranchStatus(BranchTypeToAuxiliaryBranchName(InEvent).c_str(),
0147                                          false);  // see writeEventAuxiliary
0148     } else {
0149       eventTree_.addAuxiliary<EventAuxiliary>(
0150           BranchTypeToAuxiliaryBranchName(InEvent), pEventAux_, om_->auxItems()[InEvent].basketSize_);
0151     }
0152 
0153     eventTree_.addAuxiliary<StoredProductProvenanceVector>(BranchTypeToProductProvenanceBranchName(InEvent),
0154                                                            pEventEntryInfoVector(),
0155                                                            om_->auxItems()[InEvent].basketSize_);
0156     eventTree_.addAuxiliary<EventSelectionIDVector>(
0157         poolNames::eventSelectionsBranchName(), pEventSelectionIDs_, om_->auxItems()[InEvent].basketSize_, false);
0158     eventTree_.addAuxiliary<BranchListIndexes>(
0159         poolNames::branchListIndexesBranchName(), pBranchListIndexes_, om_->auxItems()[InEvent].basketSize_);
0160 
0161     if (om_->outputProcessBlockHelper().productsFromInputKept()) {
0162       eventTree_.addAuxiliary<EventToProcessBlockIndexes>(poolNames::eventToProcessBlockIndexesBranchName(),
0163                                                           pEventToProcessBlockIndexes_,
0164                                                           om_->auxItems()[InEvent].basketSize_);
0165     }
0166 
0167     lumiTree_.addAuxiliary<LuminosityBlockAuxiliary>(
0168         BranchTypeToAuxiliaryBranchName(InLumi), pLumiAux_, om_->auxItems()[InLumi].basketSize_);
0169 
0170     runTree_.addAuxiliary<RunAuxiliary>(
0171         BranchTypeToAuxiliaryBranchName(InRun), pRunAux_, om_->auxItems()[InRun].basketSize_);
0172 
0173     treePointers_.emplace_back(&eventTree_);
0174     treePointers_.emplace_back(&lumiTree_);
0175     treePointers_.emplace_back(&runTree_);
0176     for (auto& processBlockTree : processBlockTrees_) {
0177       treePointers_.emplace_back(processBlockTree.get());
0178     }
0179 
0180     for (unsigned int i = 0; i < treePointers_.size(); ++i) {
0181       RootOutputTree* theTree = treePointers_[i];
0182       for (auto& item : om_->selectedOutputItemList()[i]) {
0183         item.setProduct(nullptr);
0184         ProductDescription const& desc = *item.productDescription();
0185         theTree->addBranch(desc.branchName(),
0186                            desc.wrappedName(),
0187                            item.product(),
0188                            item.splitLevel(),
0189                            item.basketSize(),
0190                            item.productDescription()->produced());
0191         //make sure we always store product registry info for all branches we create
0192         branchesWithStoredHistory_.insert(item.branchID());
0193       }
0194     }
0195     // Don't split metadata tree or event description tree
0196     metaDataTree_ = RootOutputTree::makeTTree(filePtr_.get(), poolNames::metaDataTreeName(), 0);
0197     parentageTree_ = RootOutputTree::makeTTree(filePtr_.get(), poolNames::parentageTreeName(), 0);
0198     parameterSetsTree_ = RootOutputTree::makeTTree(filePtr_.get(), poolNames::parameterSetsTreeName(), 0);
0199 
0200     if (overrideGUID.empty()) {
0201       fid_ = FileID(createGlobalIdentifier());
0202     } else {
0203       if (not isValidGlobalIdentifier(overrideGUID)) {
0204         throw edm::Exception(errors::Configuration)
0205             << "GUID to be used for output file is not valid (is '" << overrideGUID << "')";
0206       }
0207       fid_ = FileID(overrideGUID);
0208     }
0209 
0210     // For the Job Report, get a vector of branch names in the "Events" tree.
0211     // Also create a hash of all the branch names in the "Events" tree
0212     // in a deterministic order, except use the full class name instead of the friendly class name.
0213     // To avoid extra string copies, we create a vector of pointers into the product registry,
0214     // and use a custom comparison operator for sorting.
0215     std::vector<std::string> branchNames;
0216     std::vector<ProductDescription const*> branches;
0217     branchNames.reserve(om_->selectedOutputItemList()[InEvent].size());
0218     branches.reserve(om->selectedOutputItemList()[InEvent].size());
0219     for (auto const& item : om_->selectedOutputItemList()[InEvent]) {
0220       branchNames.push_back(item.productDescription()->branchName());
0221       branches.push_back(item.productDescription());
0222     }
0223     // Now sort the branches for the hash.
0224     sort_all(branches, sorterForJobReportHash);
0225     // Now, make a concatenated string.
0226     std::ostringstream oss;
0227     char const underscore = '_';
0228     for (auto const& branch : branches) {
0229       ProductDescription const& bd = *branch;
0230       oss << bd.fullClassName() << underscore << bd.moduleLabel() << underscore << bd.productInstanceName()
0231           << underscore << bd.processName() << underscore;
0232     }
0233     std::string stringrep = oss.str();
0234     cms::Digest md5alg(stringrep);
0235 
0236     // Register the output file with the JobReport service
0237     // and get back the token for it.
0238     std::string moduleName = "PoolOutputModule";
0239     Service<JobReport> reportSvc;
0240     reportToken_ = reportSvc->outputFileOpened(file_,
0241                                                logicalFile_,        // PFN and LFN
0242                                                om_->catalog(),      // catalog
0243                                                moduleName,          // module class name
0244                                                om_->moduleLabel(),  // module label
0245                                                fid_.fid(),          // file id (guid)
0246                                                std::string(),       // data type (not yet known, so string is empty).
0247                                                md5alg.digest().toString(),  // branch hash
0248                                                branchNames);                // branch names being written
0249   }
0250 
0251   namespace {
0252     void maybeIssueWarning(int whyNotFastClonable, std::string const& ifileName, std::string const& ofileName) {
0253       // No message if fast cloning was deliberately disabled, or if there are no events to copy anyway.
0254       if ((whyNotFastClonable & (FileBlock::DisabledInConfigFile | FileBlock::NoRootInputSource |
0255                                  FileBlock::NotProcessingEvents | FileBlock::NoEventsInFile)) != 0) {
0256         return;
0257       }
0258 
0259       // There will be a message stating every reason that fast cloning was not possible.
0260       // If at one or more of the reasons was because of something the user explicitly specified (e.g. event selection, skipping events),
0261       // or if the input file was in an old format, the message will be informational.  Otherwise, the message will be a warning.
0262       bool isWarning = true;
0263       std::ostringstream message;
0264       message << "Fast copying of file " << ifileName << " to file " << ofileName << " is disabled because:\n";
0265       if ((whyNotFastClonable & FileBlock::HasSecondaryFileSequence) != 0) {
0266         message << "a SecondaryFileSequence was specified.\n";
0267         whyNotFastClonable &= ~(FileBlock::HasSecondaryFileSequence);
0268         isWarning = false;
0269       }
0270       if ((whyNotFastClonable & FileBlock::FileTooOld) != 0) {
0271         message << "the input file is in an old format.\n";
0272         whyNotFastClonable &= ~(FileBlock::FileTooOld);
0273         isWarning = false;
0274       }
0275       if ((whyNotFastClonable & FileBlock::EventsToBeSorted) != 0) {
0276         message << "events need to be sorted.\n";
0277         whyNotFastClonable &= ~(FileBlock::EventsToBeSorted);
0278       }
0279       if ((whyNotFastClonable & FileBlock::RunOrLumiNotContiguous) != 0) {
0280         message << "a run or a lumi is not contiguous in the input file.\n";
0281         whyNotFastClonable &= ~(FileBlock::RunOrLumiNotContiguous);
0282       }
0283       if ((whyNotFastClonable & FileBlock::EventsOrLumisSelectedByID) != 0) {
0284         message << "events or lumis were selected or skipped by ID.\n";
0285         whyNotFastClonable &= ~(FileBlock::EventsOrLumisSelectedByID);
0286         isWarning = false;
0287       }
0288       if ((whyNotFastClonable & FileBlock::InitialEventsSkipped) != 0) {
0289         message << "initial events, lumis or runs were skipped.\n";
0290         whyNotFastClonable &= ~(FileBlock::InitialEventsSkipped);
0291         isWarning = false;
0292       }
0293       if ((whyNotFastClonable & FileBlock::DuplicateEventsRemoved) != 0) {
0294         message << "some events were skipped because of duplicate checking.\n";
0295         whyNotFastClonable &= ~(FileBlock::DuplicateEventsRemoved);
0296       }
0297       if ((whyNotFastClonable & FileBlock::MaxEventsTooSmall) != 0) {
0298         message << "some events were not copied because of maxEvents limit.\n";
0299         whyNotFastClonable &= ~(FileBlock::MaxEventsTooSmall);
0300         isWarning = false;
0301       }
0302       if ((whyNotFastClonable & FileBlock::MaxLumisTooSmall) != 0) {
0303         message << "some events were not copied because of maxLumis limit.\n";
0304         whyNotFastClonable &= ~(FileBlock::MaxLumisTooSmall);
0305         isWarning = false;
0306       }
0307       if ((whyNotFastClonable & FileBlock::ParallelProcesses) != 0) {
0308         message << "parallel processing was specified.\n";
0309         whyNotFastClonable &= ~(FileBlock::ParallelProcesses);
0310         isWarning = false;
0311       }
0312       if ((whyNotFastClonable & FileBlock::EventSelectionUsed) != 0) {
0313         message << "an EventSelector was specified.\n";
0314         whyNotFastClonable &= ~(FileBlock::EventSelectionUsed);
0315         isWarning = false;
0316       }
0317       if ((whyNotFastClonable & FileBlock::OutputMaxEventsTooSmall) != 0) {
0318         message << "some events were not copied because of maxEvents output limit.\n";
0319         whyNotFastClonable &= ~(FileBlock::OutputMaxEventsTooSmall);
0320         isWarning = false;
0321       }
0322       if ((whyNotFastClonable & FileBlock::SplitLevelMismatch) != 0) {
0323         message << "the split level or basket size of a branch or branches was modified.\n";
0324         whyNotFastClonable &= ~(FileBlock::SplitLevelMismatch);
0325       }
0326       if ((whyNotFastClonable & FileBlock::BranchMismatch) != 0) {
0327         message << "The format of a data product has changed.\n";
0328         whyNotFastClonable &= ~(FileBlock::BranchMismatch);
0329       }
0330       assert(whyNotFastClonable == FileBlock::CanFastClone);
0331       if (isWarning) {
0332         LogWarning("FastCloningDisabled") << message.str();
0333       } else {
0334         LogInfo("FastCloningDisabled") << message.str();
0335       }
0336     }
0337   }  // namespace
0338 
0339   void RootOutputFile::beginInputFile(FileBlock const& fb, int remainingEvents) {
0340     // Reset per input file information
0341     whyNotFastClonable_ = om_->whyNotFastClonable();
0342     canFastCloneAux_ = false;
0343 
0344     if (fb.tree() != nullptr) {
0345       whyNotFastClonable_ |= fb.whyNotFastClonable();
0346 
0347       if (remainingEvents >= 0 && remainingEvents < fb.tree()->GetEntries()) {
0348         whyNotFastClonable_ |= FileBlock::OutputMaxEventsTooSmall;
0349       }
0350 
0351       bool match = eventTree_.checkSplitLevelsAndBasketSizes(fb.tree());
0352       if (!match) {
0353         if (om_->overrideInputFileSplitLevels()) {
0354           // We may be fast copying.  We must disable fast copying if the split levels
0355           // or basket sizes do not match.
0356           whyNotFastClonable_ |= FileBlock::SplitLevelMismatch;
0357         } else {
0358           // We are using the input split levels and basket sizes from the first input file
0359           // for copied output branches.  In this case, we throw an exception if any branches
0360           // have different split levels or basket sizes in a subsequent input file.
0361           // If the mismatch is in the first file, there is a bug somewhere, so we assert.
0362           assert(om_->inputFileCount() > 1);
0363           throw Exception(errors::MismatchedInputFiles, "RootOutputFile::beginInputFile()")
0364               << "Merge failure because input file " << file_ << " has different ROOT split levels or basket sizes\n"
0365               << "than previous files.  To allow merging in spite of this, use the configuration parameter\n"
0366               << "overrideInputFileSplitLevels=cms.untracked.bool(True)\n"
0367               << "in every PoolOutputModule.\n";
0368         }
0369       }
0370 
0371       // Since this check can be time consuming, we do it only if we would otherwise fast clone.
0372       if (whyNotFastClonable_ == FileBlock::CanFastClone) {
0373         if (!eventTree_.checkIfFastClonable(fb.tree())) {
0374           whyNotFastClonable_ |= FileBlock::BranchMismatch;
0375         }
0376       }
0377 
0378       // reasons for whyNotFastClonable that are also inconsistent with a merge job
0379       constexpr auto setSubBranchBasketConditions =
0380           FileBlock::EventsOrLumisSelectedByID | FileBlock::InitialEventsSkipped | FileBlock::MaxEventsTooSmall |
0381           FileBlock::MaxLumisTooSmall | FileBlock::EventSelectionUsed | FileBlock::OutputMaxEventsTooSmall |
0382           FileBlock::SplitLevelMismatch | FileBlock::BranchMismatch;
0383 
0384       if (om_->inputFileCount() == 1) {
0385         if (om_->mergeJob()) {
0386           // for merge jobs always forward the compression mode
0387           auto infile = fb.tree()->GetCurrentFile();
0388           if (infile != nullptr) {
0389             filePtr_->SetCompressionSettings(infile->GetCompressionSettings());
0390           }
0391         }
0392 
0393         // if we aren't fast cloning, and the reason why is consistent with a
0394         // merge job or is only because of parallel processes, then forward all
0395         // the sub-branch basket sizes
0396         if (whyNotFastClonable_ != FileBlock::CanFastClone &&
0397             ((om_->mergeJob() && (whyNotFastClonable_ & setSubBranchBasketConditions) == 0) ||
0398              (whyNotFastClonable_ == FileBlock::ParallelProcesses))) {
0399           eventTree_.setSubBranchBasketSizes(fb.tree());
0400         }
0401       }
0402 
0403       // We now check if we can fast copy the auxiliary branches.
0404       // We can do so only if we can otherwise fast copy,
0405       // the input file has the current format (these branches are in the Events Tree),
0406       // there are no newly dropped or produced products,
0407       // no metadata has been dropped,
0408       // ID's have not been modified,
0409       // and the branch list indexes do not need modification.
0410 
0411       // Note: Fast copy of the EventProductProvenance branch is unsafe
0412       // unless we can enforce that the parentage information for a fully copied
0413       // output file will be the same as for the input file, with nothing dropped.
0414       // This has never been enforced, and, withthe EDAlias feature, it may no longer
0415       // work by accident.
0416       // So, for now, we do not enable fast cloning of the non-product branches.
0417       /*
0418       canFastCloneAux_ = (whyNotFastClonable_ == FileBlock::CanFastClone) &&
0419                           fb.fileFormatVersion().noMetaDataTrees() &&
0420                           !om_->hasNewlyDroppedBranch()[InEvent] &&
0421                           !fb.hasNewlyDroppedBranch()[InEvent] &&
0422                           om_->dropMetaData() == PoolOutputModule::DropNone &&
0423                           !reg->anyProductProduced() &&
0424                           !fb.modifiedIDs() &&
0425                           fb.branchListIndexesUnchanged();
0426       */
0427 
0428       // Report the fast copying status.
0429       Service<JobReport> reportSvc;
0430       reportSvc->reportFastCopyingStatus(reportToken_, fb.fileName(), whyNotFastClonable_ == FileBlock::CanFastClone);
0431     } else {
0432       whyNotFastClonable_ |= FileBlock::NoRootInputSource;
0433     }
0434 
0435     eventTree_.maybeFastCloneTree(
0436         whyNotFastClonable_ == FileBlock::CanFastClone, canFastCloneAux_, fb.tree(), om_->basketOrder());
0437 
0438     // Possibly issue warning or informational message if we haven't fast cloned.
0439     if (fb.tree() != nullptr && whyNotFastClonable_ != FileBlock::CanFastClone) {
0440       maybeIssueWarning(whyNotFastClonable_, fb.fileName(), file_);
0441     }
0442 
0443     if (om_->compactEventAuxiliary() &&
0444         (whyNotFastClonable_ & (FileBlock::EventsOrLumisSelectedByID | FileBlock::InitialEventsSkipped |
0445                                 FileBlock::EventSelectionUsed)) == 0) {
0446       long long int reserve = remainingEvents;
0447       if (fb.tree() != nullptr) {
0448         reserve = reserve > 0 ? std::min(fb.tree()->GetEntries(), reserve) : fb.tree()->GetEntries();
0449       }
0450       if (reserve > 0) {
0451         compactEventAuxiliary_.reserve(compactEventAuxiliary_.size() + reserve);
0452       }
0453     }
0454   }
0455 
0456   void RootOutputFile::respondToCloseInputFile(FileBlock const&) {
0457     // We can't do setEntries() on the event tree if the EventAuxiliary branch is empty & disabled
0458     if (not om_->compactEventAuxiliary()) {
0459       eventTree_.setEntries();
0460     }
0461     lumiTree_.setEntries();
0462     runTree_.setEntries();
0463   }
0464 
0465   bool RootOutputFile::shouldWeCloseFile() const {
0466     unsigned int const oneK = 1024;
0467     Long64_t size = filePtr_->GetSize() / oneK;
0468     return (size >= om_->maxFileSize());
0469   }
0470 
0471   void RootOutputFile::writeOne(EventForOutput const& e) {
0472     // Auxiliary branch
0473     pEventAux_ = &e.eventAuxiliary();
0474 
0475     // Because getting the data may cause an exception to be thrown we want to do that
0476     // first before writing anything to the file about this event
0477     // NOTE: pEventAux_, pBranchListIndexes_, pEventSelectionIDs_, and pEventEntryInfoVector_
0478     // must be set before calling fillBranches since they get written out in that routine.
0479     assert(pEventAux_->processHistoryID() == e.processHistoryID());
0480     pBranchListIndexes_ = &e.branchListIndexes();
0481     pEventToProcessBlockIndexes_ = &e.eventToProcessBlockIndexes();
0482 
0483     // Note: The EventSelectionIDVector should have a one to one correspondence with the processes in the process history.
0484     // Therefore, a new entry should be added if and only if the current process has been added to the process history,
0485     // which is done if and only if there is a produced product.
0486     EventSelectionIDVector esids = e.eventSelectionIDs();
0487     if (e.productRegistry().anyProductProduced() || !om_->wantAllEvents()) {
0488       esids.push_back(om_->selectorConfig());
0489     }
0490     pEventSelectionIDs_ = &esids;
0491     ProductProvenanceRetriever const* provRetriever = e.productProvenanceRetrieverPtr();
0492     assert(provRetriever);
0493     unsigned int ttreeIndex = InEvent;
0494     fillBranches(InEvent, e, ttreeIndex, pEventEntryInfoVector_, provRetriever);
0495 
0496     // Add the dataType to the job report if it hasn't already been done
0497     if (!dataTypeReported_) {
0498       Service<JobReport> reportSvc;
0499       std::string dataType("MC");
0500       if (pEventAux_->isRealData())
0501         dataType = "Data";
0502       reportSvc->reportDataType(reportToken_, dataType);
0503       dataTypeReported_ = true;
0504     }
0505 
0506     // Store the process history.
0507     processHistoryRegistry_.registerProcessHistory(e.processHistory());
0508     // Store the reduced ID in the IndexIntoFile
0509     ProcessHistoryID reducedPHID = processHistoryRegistry_.reducedProcessHistoryID(e.processHistoryID());
0510     // Add event to index
0511     indexIntoFile_.addEntry(
0512         reducedPHID, pEventAux_->run(), pEventAux_->luminosityBlock(), pEventAux_->event(), eventEntryNumber_);
0513     ++eventEntryNumber_;
0514 
0515     if (om_->compactEventAuxiliary()) {
0516       compactEventAuxiliary_.push_back(*pEventAux_);
0517     }
0518 
0519     // Report event written
0520     Service<JobReport> reportSvc;
0521     reportSvc->eventWrittenToFile(reportToken_, e.id().run(), e.id().event());
0522     ++nEventsInLumi_;
0523   }
0524 
0525   void RootOutputFile::writeLuminosityBlock(LuminosityBlockForOutput const& lb) {
0526     // Auxiliary branch
0527     // NOTE: lumiAux_ must be filled before calling fillBranches since it gets written out in that routine.
0528     lumiAux_ = lb.luminosityBlockAuxiliary();
0529     // Use the updated process historyID
0530     lumiAux_.setProcessHistoryID(lb.processHistoryID());
0531     // Store the process history.
0532     processHistoryRegistry_.registerProcessHistory(lb.processHistory());
0533     // Store the reduced ID in the IndexIntoFile
0534     ProcessHistoryID reducedPHID = processHistoryRegistry_.reducedProcessHistoryID(lb.processHistoryID());
0535     // Add lumi to index.
0536     indexIntoFile_.addEntry(reducedPHID, lumiAux_.run(), lumiAux_.luminosityBlock(), 0U, lumiEntryNumber_);
0537     ++lumiEntryNumber_;
0538     unsigned int ttreeIndex = InLumi;
0539     fillBranches(InLumi, lb, ttreeIndex);
0540     lumiTree_.optimizeBaskets(10ULL * 1024 * 1024);
0541 
0542     Service<JobReport> reportSvc;
0543     reportSvc->reportLumiSection(reportToken_, lb.id().run(), lb.id().luminosityBlock(), nEventsInLumi_);
0544     nEventsInLumi_ = 0;
0545   }
0546 
0547   void RootOutputFile::writeRun(RunForOutput const& r) {
0548     // Auxiliary branch
0549     // NOTE: runAux_ must be filled before calling fillBranches since it gets written out in that routine.
0550     runAux_ = r.runAuxiliary();
0551     // Use the updated process historyID
0552     runAux_.setProcessHistoryID(r.processHistoryID());
0553     // Store the process history.
0554     processHistoryRegistry_.registerProcessHistory(r.processHistory());
0555     // Store the reduced ID in the IndexIntoFile
0556     ProcessHistoryID reducedPHID = processHistoryRegistry_.reducedProcessHistoryID(r.processHistoryID());
0557     // Add run to index.
0558     indexIntoFile_.addEntry(reducedPHID, runAux_.run(), 0U, 0U, runEntryNumber_);
0559     r.mergeableRunProductMetadata()->addEntryToStoredMetadata(storedMergeableRunProductMetadata_);
0560     ++runEntryNumber_;
0561     unsigned int ttreeIndex = InRun;
0562     fillBranches(InRun, r, ttreeIndex);
0563     runTree_.optimizeBaskets(10ULL * 1024 * 1024);
0564 
0565     Service<JobReport> reportSvc;
0566     reportSvc->reportRunNumber(reportToken_, r.run());
0567   }
0568 
0569   void RootOutputFile::writeProcessBlock(ProcessBlockForOutput const& pb) {
0570     std::string const& processName = pb.processName();
0571     std::vector<std::string> const& processesWithProcessBlockProducts =
0572         om_->outputProcessBlockHelper().processesWithProcessBlockProducts();
0573     std::vector<std::string>::const_iterator it =
0574         std::find(processesWithProcessBlockProducts.cbegin(), processesWithProcessBlockProducts.cend(), processName);
0575     if (it == processesWithProcessBlockProducts.cend()) {
0576       return;
0577     }
0578     unsigned int ttreeIndex = InProcess + std::distance(processesWithProcessBlockProducts.cbegin(), it);
0579     fillBranches(InProcess, pb, ttreeIndex);
0580     treePointers_[ttreeIndex]->optimizeBaskets(10ULL * 1024 * 1024);
0581   }
0582 
0583   void RootOutputFile::writeParentageRegistry() {
0584     Parentage const* desc(nullptr);
0585 
0586     if (!parentageTree_->Branch(poolNames::parentageBranchName().c_str(), &desc, om_->basketSize(), 0))
0587       throw Exception(errors::FatalRootError) << "Failed to create a branch for Parentages in the output file";
0588 
0589     ParentageRegistry& ptReg = *ParentageRegistry::instance();
0590 
0591     std::vector<ParentageID> orderedIDs(parentageIDs_.size());
0592     for (auto const& parentageID : parentageIDs_) {
0593       orderedIDs[parentageID.second] = parentageID.first;
0594     }
0595     //now put them into the TTree in the correct order
0596     for (auto const& orderedID : orderedIDs) {
0597       desc = ptReg.getMapped(orderedID);
0598       //NOTE: some old format files have missing Parentage info
0599       // so a null value of desc can't be fatal.
0600       // Root will default construct an object in that case.
0601       parentageTree_->Fill();
0602     }
0603   }
0604 
0605   void RootOutputFile::writeFileFormatVersion() {
0606     FileFormatVersion fileFormatVersion(getFileFormatVersion());
0607     FileFormatVersion* pFileFmtVsn = &fileFormatVersion;
0608     TBranch* b =
0609         metaDataTree_->Branch(poolNames::fileFormatVersionBranchName().c_str(), &pFileFmtVsn, om_->basketSize(), 0);
0610     assert(b);
0611     b->Fill();
0612   }
0613 
0614   void RootOutputFile::writeFileIdentifier() {
0615     FileID* fidPtr = &fid_;
0616     TBranch* b = metaDataTree_->Branch(poolNames::fileIdentifierBranchName().c_str(), &fidPtr, om_->basketSize(), 0);
0617     assert(b);
0618     b->Fill();
0619   }
0620 
0621   void RootOutputFile::writeIndexIntoFile() {
0622     if (eventTree_.checkEntriesInReadBranches(eventEntryNumber_) == false) {
0623       Exception ex(errors::OtherCMS);
0624       ex << "The number of entries in at least one output TBranch whose entries\n"
0625             "were copied from the input does not match the number of events\n"
0626             "recorded in IndexIntoFile. This might (or might not) indicate a\n"
0627             "problem related to fast copy.";
0628       ex.addContext("Calling RootOutputFile::writeIndexIntoFile");
0629       throw ex;
0630     }
0631     indexIntoFile_.sortVector_Run_Or_Lumi_Entries();
0632     IndexIntoFile* iifPtr = &indexIntoFile_;
0633     TBranch* b = metaDataTree_->Branch(poolNames::indexIntoFileBranchName().c_str(), &iifPtr, om_->basketSize(), 0);
0634     assert(b);
0635     b->Fill();
0636   }
0637 
0638   void RootOutputFile::writeStoredMergeableRunProductMetadata() {
0639     storedMergeableRunProductMetadata_.optimizeBeforeWrite();
0640     StoredMergeableRunProductMetadata* ptr = &storedMergeableRunProductMetadata_;
0641     TBranch* b =
0642         metaDataTree_->Branch(poolNames::mergeableRunProductMetadataBranchName().c_str(), &ptr, om_->basketSize(), 0);
0643     assert(b);
0644     b->Fill();
0645   }
0646 
0647   void RootOutputFile::writeProcessHistoryRegistry() {
0648     fillProcessHistoryBranch(metaDataTree_.get(), om_->basketSize(), processHistoryRegistry_);
0649   }
0650 
0651   void RootOutputFile::writeBranchIDListRegistry() {
0652     BranchIDLists const* p = om_->branchIDLists();
0653     TBranch* b = metaDataTree_->Branch(poolNames::branchIDListBranchName().c_str(), &p, om_->basketSize(), 0);
0654     assert(b);
0655     b->Fill();
0656   }
0657 
0658   void RootOutputFile::writeThinnedAssociationsHelper() {
0659     ThinnedAssociationsHelper const* p = om_->thinnedAssociationsHelper();
0660     TBranch* b =
0661         metaDataTree_->Branch(poolNames::thinnedAssociationsHelperBranchName().c_str(), &p, om_->basketSize(), 0);
0662     assert(b);
0663     b->Fill();
0664   }
0665 
0666   void RootOutputFile::writeParameterSetRegistry() {
0667     fillParameterSetBranch(parameterSetsTree_.get(), om_->basketSize());
0668   }
0669 
0670   void RootOutputFile::writeProductDescriptionRegistry(ProductRegistry const& iReg) {
0671     // Make a local copy of the ProductRegistry, removing any transient or pruned products.
0672     using ProductList = ProductRegistry::ProductList;
0673     ProductRegistry pReg(iReg.productList());
0674     ProductList& pList = const_cast<ProductList&>(pReg.productList());
0675     for (auto const& prod : pList) {
0676       if (prod.second.branchID() != prod.second.originalBranchID()) {
0677         if (branchesWithStoredHistory_.find(prod.second.branchID()) != branchesWithStoredHistory_.end()) {
0678           branchesWithStoredHistory_.insert(prod.second.originalBranchID());
0679         }
0680       }
0681     }
0682     std::set<BranchID>::iterator end = branchesWithStoredHistory_.end();
0683     for (ProductList::iterator it = pList.begin(); it != pList.end();) {
0684       if (branchesWithStoredHistory_.find(it->second.branchID()) == end) {
0685         // avoid invalidating iterator on deletion
0686         ProductList::iterator itCopy = it;
0687         ++it;
0688         pList.erase(itCopy);
0689 
0690       } else {
0691         ++it;
0692       }
0693     }
0694 
0695     ProductRegistry* ppReg = &pReg;
0696     TBranch* b = metaDataTree_->Branch(poolNames::productDescriptionBranchName().c_str(), &ppReg, om_->basketSize(), 0);
0697     assert(b);
0698     b->Fill();
0699   }
0700   void RootOutputFile::writeProductDependencies() {
0701     ProductDependencies& pDeps = const_cast<ProductDependencies&>(om_->productDependencies());
0702     ProductDependencies* ppDeps = &pDeps;
0703     TBranch* b =
0704         metaDataTree_->Branch(poolNames::productDependenciesBranchName().c_str(), &ppDeps, om_->basketSize(), 0);
0705     assert(b);
0706     b->Fill();
0707   }
0708 
0709   // For duplicate removal and to determine if fast cloning is possible, the input
0710   // module by default reads the entire EventAuxiliary branch when it opens the
0711   // input files.  If EventAuxiliary is written in the usual way, this results
0712   // in many small reads scattered throughout the file, which can have very poor
0713   // performance characteristics on some filesystems.  As a workaround, we save
0714   // EventAuxiliary and write it at the end of the file.
0715 
0716   void RootOutputFile::writeEventAuxiliary() {
0717     constexpr std::size_t maxEaBasketSize = 4 * 1024 * 1024;
0718 
0719     if (om_->compactEventAuxiliary()) {
0720       auto tree = eventTree_.tree();
0721       auto const& bname = BranchTypeToAuxiliaryBranchName(InEvent).c_str();
0722 
0723       tree->SetBranchStatus(bname, true);
0724       auto basketsize =
0725           std::min(maxEaBasketSize,
0726                    compactEventAuxiliary_.size() * (sizeof(EventAuxiliary) + 26));  // 26 is an empirical fudge factor
0727       tree->SetBasketSize(bname, basketsize);
0728       auto b = tree->GetBranch(bname);
0729 
0730       assert(b);
0731 
0732       LogDebug("writeEventAuxiliary") << "EventAuxiliary ratio extras/GUIDs/all = "
0733                                       << compactEventAuxiliary_.extrasSize() << "/"
0734                                       << compactEventAuxiliary_.guidsSize() << "/" << compactEventAuxiliary_.size();
0735 
0736       for (auto const& aux : compactEventAuxiliary_) {
0737         const auto ea = aux.eventAuxiliary();
0738         pEventAux_ = &ea;
0739         // Fill EventAuxiliary branch
0740         b->Fill();
0741       }
0742       eventTree_.setEntries();
0743     }
0744   }
0745 
0746   void RootOutputFile::writeProcessBlockHelper() {
0747     if (!om_->outputProcessBlockHelper().processesWithProcessBlockProducts().empty()) {
0748       StoredProcessBlockHelper storedProcessBlockHelper(
0749           om_->outputProcessBlockHelper().processesWithProcessBlockProducts());
0750       om_->outputProcessBlockHelper().fillCacheIndices(storedProcessBlockHelper);
0751 
0752       StoredProcessBlockHelper* pStoredProcessBlockHelper = &storedProcessBlockHelper;
0753       TBranch* b = metaDataTree_->Branch(
0754           poolNames::processBlockHelperBranchName().c_str(), &pStoredProcessBlockHelper, om_->basketSize(), 0);
0755       assert(b);
0756       b->Fill();
0757     }
0758   }
0759 
0760   void RootOutputFile::finishEndFile() {
0761     std::string_view status = "beginning";
0762     std::string_view value = "";
0763     try {
0764       metaDataTree_->SetEntries(-1);
0765       status = "writeTTree() for metadata";
0766       RootOutputTree::writeTTree(metaDataTree_);
0767       status = "writeTTree() for ParameterSets";
0768       RootOutputTree::writeTTree(parameterSetsTree_);
0769 
0770       status = "writeTTree() for parentage";
0771       RootOutputTree::writeTTree(parentageTree_);
0772 
0773       // Create branch aliases for all the branches in the
0774       // events/lumis/runs/processblock trees. The loop is over
0775       // all types of data products.
0776       status = "writeTree() for ";
0777       for (unsigned int i = 0; i < treePointers_.size(); ++i) {
0778         std::string processName;
0779         BranchType branchType = InProcess;
0780         if (i < InProcess) {
0781           branchType = static_cast<BranchType>(i);
0782         } else {
0783           processName = om_->outputProcessBlockHelper().processesWithProcessBlockProducts()[i - InProcess];
0784         }
0785         setBranchAliases(treePointers_[i]->tree(), om_->keptProducts()[branchType], processName);
0786         value = treePointers_[i]->tree()->GetName();
0787         treePointers_[i]->writeTree();
0788       }
0789 
0790       // close the file -- mfp
0791       // Just to play it safe, zero all pointers to objects in the TFile to be closed.
0792       status = "closing TTrees";
0793       value = "";
0794       metaDataTree_ = parentageTree_ = nullptr;
0795       for (auto& treePointer : treePointers_) {
0796         treePointer->close();
0797         treePointer = nullptr;
0798       }
0799       status = "closing TFile";
0800       filePtr_->Close();
0801       filePtr_ = nullptr;  // propagate_const<T> has no reset() function
0802 
0803       // report that file has been closed
0804       status = "reporting to JobReport";
0805       Service<JobReport> reportSvc;
0806       reportSvc->outputFileClosed(reportToken_);
0807     } catch (cms::Exception& e) {
0808       e.addContext("Calling RootOutputFile::finishEndFile() while closing " + file_);
0809       e.addAdditionalInfo("While calling " + std::string(status) + std::string(value));
0810       throw;
0811     }
0812   }
0813 
0814   void RootOutputFile::setBranchAliases(TTree* tree,
0815                                         SelectedProducts const& branches,
0816                                         std::string const& processName) const {
0817     if (tree && tree->GetNbranches() != 0) {
0818       auto const& aliasForBranches = om_->aliasForBranches();
0819       for (auto const& selection : branches) {
0820         ProductDescription const& pd = *selection.first;
0821         if (pd.branchType() == InProcess && processName != pd.processName()) {
0822           continue;
0823         }
0824         std::string const& full = pd.branchName() + "obj";
0825         bool matched = false;
0826         for (auto const& matcher : aliasForBranches) {
0827           if (matcher.match(pd.branchName())) {
0828             tree->SetAlias(matcher.alias_.c_str(), full.c_str());
0829             matched = true;
0830           }
0831         }
0832         if (not matched and pd.branchAliases().empty()) {
0833           std::string const& alias = (pd.productInstanceName().empty() ? pd.moduleLabel() : pd.productInstanceName());
0834           tree->SetAlias(alias.c_str(), full.c_str());
0835         } else {
0836           for (auto const& alias : pd.branchAliases()) {
0837             tree->SetAlias(alias.c_str(), full.c_str());
0838           }
0839         }
0840       }
0841     }
0842   }
0843 
0844   void RootOutputFile::insertAncestors(ProductProvenance const& iGetParents,
0845                                        ProductProvenanceRetriever const* iMapper,
0846                                        bool produced,
0847                                        std::set<BranchID> const& iProducedIDs,
0848                                        std::set<StoredProductProvenance>& oToFill) {
0849     assert(om_->dropMetaData() != PoolOutputModule::DropAll);
0850     assert(produced || om_->dropMetaData() != PoolOutputModule::DropPrior);
0851     if (om_->dropMetaData() == PoolOutputModule::DropDroppedPrior && !produced)
0852       return;
0853     std::vector<BranchID> const& parentIDs = iGetParents.parentage().parents();
0854     for (auto const& parentID : parentIDs) {
0855       branchesWithStoredHistory_.insert(parentID);
0856       ProductProvenance const* info = iMapper->branchIDToProvenance(parentID);
0857       if (info) {
0858         if (om_->dropMetaData() == PoolOutputModule::DropNone ||
0859             (iProducedIDs.end() != iProducedIDs.find(info->branchID()))) {
0860           if (insertProductProvenance(*info, oToFill)) {
0861             //haven't seen this one yet
0862             insertAncestors(*info, iMapper, produced, iProducedIDs, oToFill);
0863           }
0864         }
0865       }
0866     }
0867   }
0868 
0869   void RootOutputFile::fillBranches(BranchType const& branchType,
0870                                     OccurrenceForOutput const& occurrence,
0871                                     unsigned int ttreeIndex,
0872                                     StoredProductProvenanceVector* productProvenanceVecPtr,
0873                                     ProductProvenanceRetriever const* provRetriever) {
0874     std::vector<std::unique_ptr<WrapperBase> > dummies;
0875 
0876     OutputItemList& items = om_->selectedOutputItemList()[ttreeIndex];
0877 
0878     bool const doProvenance =
0879         (productProvenanceVecPtr != nullptr) && (om_->dropMetaData() != PoolOutputModule::DropAll);
0880     bool const keepProvenanceForPrior = doProvenance && om_->dropMetaData() != PoolOutputModule::DropPrior;
0881 
0882     bool const fastCloning = (branchType == InEvent) && (whyNotFastClonable_ == FileBlock::CanFastClone);
0883     std::set<StoredProductProvenance> provenanceToKeep;
0884     //
0885     //If we are dropping some of the meta data we need to know
0886     // which BranchIDs were produced in this process because
0887     // we may be storing meta data for only those products
0888     // We do this only for event products.
0889     std::set<BranchID> producedBranches;
0890     if (doProvenance && branchType == InEvent && om_->dropMetaData() != PoolOutputModule::DropNone) {
0891       for (auto bd : occurrence.productRegistry().allProductDescriptions()) {
0892         if (bd->produced() && bd->branchType() == InEvent) {
0893           producedBranches.insert(bd->branchID());
0894         }
0895       }
0896     }
0897 
0898     // Loop over EDProduct branches, possibly fill the provenance, and write the branch.
0899     for (auto& item : items) {
0900       BranchID const& id = item.productDescription()->branchID();
0901       branchesWithStoredHistory_.insert(id);
0902 
0903       bool produced = item.productDescription()->produced();
0904       bool getProd =
0905           (produced || !fastCloning || treePointers_[ttreeIndex]->uncloned(item.productDescription()->branchName()));
0906       bool keepProvenance = doProvenance && (produced || keepProvenanceForPrior);
0907 
0908       WrapperBase const* product = nullptr;
0909       ProductProvenance const* productProvenance = nullptr;
0910       if (getProd) {
0911         BasicHandle result = occurrence.getByToken(item.token(), item.productDescription()->unwrappedTypeID());
0912         product = result.wrapper();
0913         if (result.isValid() && keepProvenance) {
0914           productProvenance = result.provenance()->productProvenance();
0915         }
0916         if (product == nullptr) {
0917           // No product with this ID is in the event.
0918           // Add a null product.
0919           TClass* cp = item.productDescription()->wrappedType().getClass();
0920           assert(cp != nullptr);
0921           int offset = cp->GetBaseClassOffset(wrapperBaseTClass_);
0922           void* p = cp->New();
0923           std::unique_ptr<WrapperBase> dummy = getWrapperBasePtr(p, offset);
0924           product = dummy.get();
0925           dummies.emplace_back(std::move(dummy));
0926         }
0927         item.setProduct(product);
0928       }
0929       if (keepProvenance && productProvenance == nullptr) {
0930         productProvenance = provRetriever->branchIDToProvenance(item.productDescription()->originalBranchID());
0931       }
0932       if (productProvenance) {
0933         insertProductProvenance(*productProvenance, provenanceToKeep);
0934         insertAncestors(*productProvenance, provRetriever, produced, producedBranches, provenanceToKeep);
0935       }
0936     }
0937 
0938     if (doProvenance)
0939       productProvenanceVecPtr->assign(provenanceToKeep.begin(), provenanceToKeep.end());
0940     treePointers_[ttreeIndex]->fillTree();
0941     if (doProvenance)
0942       productProvenanceVecPtr->clear();
0943   }
0944 
0945   bool RootOutputFile::insertProductProvenance(const edm::ProductProvenance& iProv,
0946                                                std::set<edm::StoredProductProvenance>& oToInsert) {
0947     StoredProductProvenance toStore;
0948     toStore.branchID_ = iProv.branchID().id();
0949     std::set<edm::StoredProductProvenance>::iterator itFound = oToInsert.find(toStore);
0950     if (itFound == oToInsert.end()) {
0951       //get the index to the ParentageID or insert a new value if not already present
0952       std::pair<std::map<edm::ParentageID, unsigned int>::iterator, bool> i =
0953           parentageIDs_.insert(std::make_pair(iProv.parentageID(), static_cast<unsigned int>(parentageIDs_.size())));
0954       toStore.parentageIDIndex_ = i.first->second;
0955       if (toStore.parentageIDIndex_ >= parentageIDs_.size()) {
0956         throw edm::Exception(errors::LogicError)
0957             << "RootOutputFile::insertProductProvenance\n"
0958             << "The parentage ID index value " << toStore.parentageIDIndex_
0959             << " is out of bounds.  The maximum value is currently " << parentageIDs_.size() - 1 << ".\n"
0960             << "This should never happen.\n"
0961             << "Please report this to the framework developers.";
0962       }
0963 
0964       oToInsert.insert(toStore);
0965       return true;
0966     }
0967     return false;
0968   }
0969 }  // namespace edm