Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-06-07 02:29:43

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/BranchDescription.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/BranchChildren.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 "FWCore/Framework/interface/ConstProductRegistry.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(BranchDescription const* lh, BranchDescription 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::kZLIB);
0129     } else if (om_->compressionAlgorithm() == std::string("LZMA")) {
0130       filePtr_->SetCompressionAlgorithm(ROOT::kLZMA);
0131     } else if (om_->compressionAlgorithm() == std::string("ZSTD")) {
0132       filePtr_->SetCompressionAlgorithm(ROOT::kZSTD);
0133     } else if (om_->compressionAlgorithm() == std::string("LZ4")) {
0134       filePtr_->SetCompressionAlgorithm(ROOT::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         BranchDescription const& desc = *item.branchDescription();
0185         theTree->addBranch(desc.branchName(),
0186                            desc.wrappedName(),
0187                            item.product(),
0188                            item.splitLevel(),
0189                            item.basketSize(),
0190                            item.branchDescription()->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<BranchDescription 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.branchDescription()->branchName());
0221       branches.push_back(item.branchDescription());
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       BranchDescription 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       Service<ConstProductRegistry> reg;
0419       canFastCloneAux_ = (whyNotFastClonable_ == FileBlock::CanFastClone) &&
0420                           fb.fileFormatVersion().noMetaDataTrees() &&
0421                           !om_->hasNewlyDroppedBranch()[InEvent] &&
0422                           !fb.hasNewlyDroppedBranch()[InEvent] &&
0423                           om_->dropMetaData() == PoolOutputModule::DropNone &&
0424                           !reg->anyProductProduced() &&
0425                           !fb.modifiedIDs() &&
0426                           fb.branchListIndexesUnchanged();
0427       */
0428 
0429       // Report the fast copying status.
0430       Service<JobReport> reportSvc;
0431       reportSvc->reportFastCopyingStatus(reportToken_, fb.fileName(), whyNotFastClonable_ == FileBlock::CanFastClone);
0432     } else {
0433       whyNotFastClonable_ |= FileBlock::NoRootInputSource;
0434     }
0435 
0436     eventTree_.maybeFastCloneTree(
0437         whyNotFastClonable_ == FileBlock::CanFastClone, canFastCloneAux_, fb.tree(), om_->basketOrder());
0438 
0439     // Possibly issue warning or informational message if we haven't fast cloned.
0440     if (fb.tree() != nullptr && whyNotFastClonable_ != FileBlock::CanFastClone) {
0441       maybeIssueWarning(whyNotFastClonable_, fb.fileName(), file_);
0442     }
0443 
0444     if (om_->compactEventAuxiliary() &&
0445         (whyNotFastClonable_ & (FileBlock::EventsOrLumisSelectedByID | FileBlock::InitialEventsSkipped |
0446                                 FileBlock::EventSelectionUsed)) == 0) {
0447       long long int reserve = remainingEvents;
0448       if (fb.tree() != nullptr) {
0449         reserve = reserve > 0 ? std::min(fb.tree()->GetEntries(), reserve) : fb.tree()->GetEntries();
0450       }
0451       if (reserve > 0) {
0452         compactEventAuxiliary_.reserve(compactEventAuxiliary_.size() + reserve);
0453       }
0454     }
0455   }
0456 
0457   void RootOutputFile::respondToCloseInputFile(FileBlock const&) {
0458     // We can't do setEntries() on the event tree if the EventAuxiliary branch is empty & disabled
0459     if (not om_->compactEventAuxiliary()) {
0460       eventTree_.setEntries();
0461     }
0462     lumiTree_.setEntries();
0463     runTree_.setEntries();
0464   }
0465 
0466   bool RootOutputFile::shouldWeCloseFile() const {
0467     unsigned int const oneK = 1024;
0468     Long64_t size = filePtr_->GetSize() / oneK;
0469     return (size >= om_->maxFileSize());
0470   }
0471 
0472   void RootOutputFile::writeOne(EventForOutput const& e) {
0473     // Auxiliary branch
0474     pEventAux_ = &e.eventAuxiliary();
0475 
0476     // Because getting the data may cause an exception to be thrown we want to do that
0477     // first before writing anything to the file about this event
0478     // NOTE: pEventAux_, pBranchListIndexes_, pEventSelectionIDs_, and pEventEntryInfoVector_
0479     // must be set before calling fillBranches since they get written out in that routine.
0480     assert(pEventAux_->processHistoryID() == e.processHistoryID());
0481     pBranchListIndexes_ = &e.branchListIndexes();
0482     pEventToProcessBlockIndexes_ = &e.eventToProcessBlockIndexes();
0483 
0484     // Note: The EventSelectionIDVector should have a one to one correspondence with the processes in the process history.
0485     // Therefore, a new entry should be added if and only if the current process has been added to the process history,
0486     // which is done if and only if there is a produced product.
0487     Service<ConstProductRegistry> reg;
0488     EventSelectionIDVector esids = e.eventSelectionIDs();
0489     if (reg->anyProductProduced() || !om_->wantAllEvents()) {
0490       esids.push_back(om_->selectorConfig());
0491     }
0492     pEventSelectionIDs_ = &esids;
0493     ProductProvenanceRetriever const* provRetriever = e.productProvenanceRetrieverPtr();
0494     assert(provRetriever);
0495     unsigned int ttreeIndex = InEvent;
0496     fillBranches(InEvent, e, ttreeIndex, pEventEntryInfoVector_, provRetriever);
0497 
0498     // Add the dataType to the job report if it hasn't already been done
0499     if (!dataTypeReported_) {
0500       Service<JobReport> reportSvc;
0501       std::string dataType("MC");
0502       if (pEventAux_->isRealData())
0503         dataType = "Data";
0504       reportSvc->reportDataType(reportToken_, dataType);
0505       dataTypeReported_ = true;
0506     }
0507 
0508     // Store the process history.
0509     processHistoryRegistry_.registerProcessHistory(e.processHistory());
0510     // Store the reduced ID in the IndexIntoFile
0511     ProcessHistoryID reducedPHID = processHistoryRegistry_.reducedProcessHistoryID(e.processHistoryID());
0512     // Add event to index
0513     indexIntoFile_.addEntry(
0514         reducedPHID, pEventAux_->run(), pEventAux_->luminosityBlock(), pEventAux_->event(), eventEntryNumber_);
0515     ++eventEntryNumber_;
0516 
0517     if (om_->compactEventAuxiliary()) {
0518       compactEventAuxiliary_.push_back(*pEventAux_);
0519     }
0520 
0521     // Report event written
0522     Service<JobReport> reportSvc;
0523     reportSvc->eventWrittenToFile(reportToken_, e.id().run(), e.id().event());
0524     ++nEventsInLumi_;
0525   }
0526 
0527   void RootOutputFile::writeLuminosityBlock(LuminosityBlockForOutput const& lb) {
0528     // Auxiliary branch
0529     // NOTE: lumiAux_ must be filled before calling fillBranches since it gets written out in that routine.
0530     lumiAux_ = lb.luminosityBlockAuxiliary();
0531     // Use the updated process historyID
0532     lumiAux_.setProcessHistoryID(lb.processHistoryID());
0533     // Store the process history.
0534     processHistoryRegistry_.registerProcessHistory(lb.processHistory());
0535     // Store the reduced ID in the IndexIntoFile
0536     ProcessHistoryID reducedPHID = processHistoryRegistry_.reducedProcessHistoryID(lb.processHistoryID());
0537     // Add lumi to index.
0538     indexIntoFile_.addEntry(reducedPHID, lumiAux_.run(), lumiAux_.luminosityBlock(), 0U, lumiEntryNumber_);
0539     ++lumiEntryNumber_;
0540     unsigned int ttreeIndex = InLumi;
0541     fillBranches(InLumi, lb, ttreeIndex);
0542     lumiTree_.optimizeBaskets(10ULL * 1024 * 1024);
0543 
0544     Service<JobReport> reportSvc;
0545     reportSvc->reportLumiSection(reportToken_, lb.id().run(), lb.id().luminosityBlock(), nEventsInLumi_);
0546     nEventsInLumi_ = 0;
0547   }
0548 
0549   void RootOutputFile::writeRun(RunForOutput const& r) {
0550     // Auxiliary branch
0551     // NOTE: runAux_ must be filled before calling fillBranches since it gets written out in that routine.
0552     runAux_ = r.runAuxiliary();
0553     // Use the updated process historyID
0554     runAux_.setProcessHistoryID(r.processHistoryID());
0555     // Store the process history.
0556     processHistoryRegistry_.registerProcessHistory(r.processHistory());
0557     // Store the reduced ID in the IndexIntoFile
0558     ProcessHistoryID reducedPHID = processHistoryRegistry_.reducedProcessHistoryID(r.processHistoryID());
0559     // Add run to index.
0560     indexIntoFile_.addEntry(reducedPHID, runAux_.run(), 0U, 0U, runEntryNumber_);
0561     r.mergeableRunProductMetadata()->addEntryToStoredMetadata(storedMergeableRunProductMetadata_);
0562     ++runEntryNumber_;
0563     unsigned int ttreeIndex = InRun;
0564     fillBranches(InRun, r, ttreeIndex);
0565     runTree_.optimizeBaskets(10ULL * 1024 * 1024);
0566 
0567     Service<JobReport> reportSvc;
0568     reportSvc->reportRunNumber(reportToken_, r.run());
0569   }
0570 
0571   void RootOutputFile::writeProcessBlock(ProcessBlockForOutput const& pb) {
0572     std::string const& processName = pb.processName();
0573     std::vector<std::string> const& processesWithProcessBlockProducts =
0574         om_->outputProcessBlockHelper().processesWithProcessBlockProducts();
0575     std::vector<std::string>::const_iterator it =
0576         std::find(processesWithProcessBlockProducts.cbegin(), processesWithProcessBlockProducts.cend(), processName);
0577     if (it == processesWithProcessBlockProducts.cend()) {
0578       return;
0579     }
0580     unsigned int ttreeIndex = InProcess + std::distance(processesWithProcessBlockProducts.cbegin(), it);
0581     fillBranches(InProcess, pb, ttreeIndex);
0582     treePointers_[ttreeIndex]->optimizeBaskets(10ULL * 1024 * 1024);
0583   }
0584 
0585   void RootOutputFile::writeParentageRegistry() {
0586     Parentage const* desc(nullptr);
0587 
0588     if (!parentageTree_->Branch(poolNames::parentageBranchName().c_str(), &desc, om_->basketSize(), 0))
0589       throw Exception(errors::FatalRootError) << "Failed to create a branch for Parentages in the output file";
0590 
0591     ParentageRegistry& ptReg = *ParentageRegistry::instance();
0592 
0593     std::vector<ParentageID> orderedIDs(parentageIDs_.size());
0594     for (auto const& parentageID : parentageIDs_) {
0595       orderedIDs[parentageID.second] = parentageID.first;
0596     }
0597     //now put them into the TTree in the correct order
0598     for (auto const& orderedID : orderedIDs) {
0599       desc = ptReg.getMapped(orderedID);
0600       //NOTE: some old format files have missing Parentage info
0601       // so a null value of desc can't be fatal.
0602       // Root will default construct an object in that case.
0603       parentageTree_->Fill();
0604     }
0605   }
0606 
0607   void RootOutputFile::writeFileFormatVersion() {
0608     FileFormatVersion fileFormatVersion(getFileFormatVersion());
0609     FileFormatVersion* pFileFmtVsn = &fileFormatVersion;
0610     TBranch* b =
0611         metaDataTree_->Branch(poolNames::fileFormatVersionBranchName().c_str(), &pFileFmtVsn, om_->basketSize(), 0);
0612     assert(b);
0613     b->Fill();
0614   }
0615 
0616   void RootOutputFile::writeFileIdentifier() {
0617     FileID* fidPtr = &fid_;
0618     TBranch* b = metaDataTree_->Branch(poolNames::fileIdentifierBranchName().c_str(), &fidPtr, om_->basketSize(), 0);
0619     assert(b);
0620     b->Fill();
0621   }
0622 
0623   void RootOutputFile::writeIndexIntoFile() {
0624     if (eventTree_.checkEntriesInReadBranches(eventEntryNumber_) == false) {
0625       Exception ex(errors::OtherCMS);
0626       ex << "The number of entries in at least one output TBranch whose entries\n"
0627             "were copied from the input does not match the number of events\n"
0628             "recorded in IndexIntoFile. This might (or might not) indicate a\n"
0629             "problem related to fast copy.";
0630       ex.addContext("Calling RootOutputFile::writeIndexIntoFile");
0631       throw ex;
0632     }
0633     indexIntoFile_.sortVector_Run_Or_Lumi_Entries();
0634     IndexIntoFile* iifPtr = &indexIntoFile_;
0635     TBranch* b = metaDataTree_->Branch(poolNames::indexIntoFileBranchName().c_str(), &iifPtr, om_->basketSize(), 0);
0636     assert(b);
0637     b->Fill();
0638   }
0639 
0640   void RootOutputFile::writeStoredMergeableRunProductMetadata() {
0641     storedMergeableRunProductMetadata_.optimizeBeforeWrite();
0642     StoredMergeableRunProductMetadata* ptr = &storedMergeableRunProductMetadata_;
0643     TBranch* b =
0644         metaDataTree_->Branch(poolNames::mergeableRunProductMetadataBranchName().c_str(), &ptr, om_->basketSize(), 0);
0645     assert(b);
0646     b->Fill();
0647   }
0648 
0649   void RootOutputFile::writeProcessHistoryRegistry() {
0650     fillProcessHistoryBranch(metaDataTree_.get(), om_->basketSize(), processHistoryRegistry_);
0651   }
0652 
0653   void RootOutputFile::writeBranchIDListRegistry() {
0654     BranchIDLists const* p = om_->branchIDLists();
0655     TBranch* b = metaDataTree_->Branch(poolNames::branchIDListBranchName().c_str(), &p, om_->basketSize(), 0);
0656     assert(b);
0657     b->Fill();
0658   }
0659 
0660   void RootOutputFile::writeThinnedAssociationsHelper() {
0661     ThinnedAssociationsHelper const* p = om_->thinnedAssociationsHelper();
0662     TBranch* b =
0663         metaDataTree_->Branch(poolNames::thinnedAssociationsHelperBranchName().c_str(), &p, om_->basketSize(), 0);
0664     assert(b);
0665     b->Fill();
0666   }
0667 
0668   void RootOutputFile::writeParameterSetRegistry() {
0669     fillParameterSetBranch(parameterSetsTree_.get(), om_->basketSize());
0670   }
0671 
0672   void RootOutputFile::writeProductDescriptionRegistry() {
0673     // Make a local copy of the ProductRegistry, removing any transient or pruned products.
0674     using ProductList = ProductRegistry::ProductList;
0675     Service<ConstProductRegistry> reg;
0676     ProductRegistry pReg(reg->productList());
0677     ProductList& pList = const_cast<ProductList&>(pReg.productList());
0678     for (auto const& prod : pList) {
0679       if (prod.second.branchID() != prod.second.originalBranchID()) {
0680         if (branchesWithStoredHistory_.find(prod.second.branchID()) != branchesWithStoredHistory_.end()) {
0681           branchesWithStoredHistory_.insert(prod.second.originalBranchID());
0682         }
0683       }
0684     }
0685     std::set<BranchID>::iterator end = branchesWithStoredHistory_.end();
0686     for (ProductList::iterator it = pList.begin(); it != pList.end();) {
0687       if (branchesWithStoredHistory_.find(it->second.branchID()) == end) {
0688         // avoid invalidating iterator on deletion
0689         ProductList::iterator itCopy = it;
0690         ++it;
0691         pList.erase(itCopy);
0692 
0693       } else {
0694         ++it;
0695       }
0696     }
0697 
0698     ProductRegistry* ppReg = &pReg;
0699     TBranch* b = metaDataTree_->Branch(poolNames::productDescriptionBranchName().c_str(), &ppReg, om_->basketSize(), 0);
0700     assert(b);
0701     b->Fill();
0702   }
0703   void RootOutputFile::writeProductDependencies() {
0704     BranchChildren& pDeps = const_cast<BranchChildren&>(om_->branchChildren());
0705     BranchChildren* ppDeps = &pDeps;
0706     TBranch* b =
0707         metaDataTree_->Branch(poolNames::productDependenciesBranchName().c_str(), &ppDeps, om_->basketSize(), 0);
0708     assert(b);
0709     b->Fill();
0710   }
0711 
0712   // For duplicate removal and to determine if fast cloning is possible, the input
0713   // module by default reads the entire EventAuxiliary branch when it opens the
0714   // input files.  If EventAuxiliary is written in the usual way, this results
0715   // in many small reads scattered throughout the file, which can have very poor
0716   // performance characteristics on some filesystems.  As a workaround, we save
0717   // EventAuxiliary and write it at the end of the file.
0718 
0719   void RootOutputFile::writeEventAuxiliary() {
0720     constexpr std::size_t maxEaBasketSize = 4 * 1024 * 1024;
0721 
0722     if (om_->compactEventAuxiliary()) {
0723       auto tree = eventTree_.tree();
0724       auto const& bname = BranchTypeToAuxiliaryBranchName(InEvent).c_str();
0725 
0726       tree->SetBranchStatus(bname, true);
0727       auto basketsize =
0728           std::min(maxEaBasketSize,
0729                    compactEventAuxiliary_.size() * (sizeof(EventAuxiliary) + 26));  // 26 is an empirical fudge factor
0730       tree->SetBasketSize(bname, basketsize);
0731       auto b = tree->GetBranch(bname);
0732 
0733       assert(b);
0734 
0735       LogDebug("writeEventAuxiliary") << "EventAuxiliary ratio extras/GUIDs/all = "
0736                                       << compactEventAuxiliary_.extrasSize() << "/"
0737                                       << compactEventAuxiliary_.guidsSize() << "/" << compactEventAuxiliary_.size();
0738 
0739       for (auto const& aux : compactEventAuxiliary_) {
0740         const auto ea = aux.eventAuxiliary();
0741         pEventAux_ = &ea;
0742         // Fill EventAuxiliary branch
0743         b->Fill();
0744       }
0745       eventTree_.setEntries();
0746     }
0747   }
0748 
0749   void RootOutputFile::writeProcessBlockHelper() {
0750     if (!om_->outputProcessBlockHelper().processesWithProcessBlockProducts().empty()) {
0751       StoredProcessBlockHelper storedProcessBlockHelper(
0752           om_->outputProcessBlockHelper().processesWithProcessBlockProducts());
0753       om_->outputProcessBlockHelper().fillCacheIndices(storedProcessBlockHelper);
0754 
0755       StoredProcessBlockHelper* pStoredProcessBlockHelper = &storedProcessBlockHelper;
0756       TBranch* b = metaDataTree_->Branch(
0757           poolNames::processBlockHelperBranchName().c_str(), &pStoredProcessBlockHelper, om_->basketSize(), 0);
0758       assert(b);
0759       b->Fill();
0760     }
0761   }
0762 
0763   void RootOutputFile::finishEndFile() {
0764     std::string_view status = "beginning";
0765     std::string_view value = "";
0766     try {
0767       metaDataTree_->SetEntries(-1);
0768       status = "writeTTree() for metadata";
0769       RootOutputTree::writeTTree(metaDataTree_);
0770       status = "writeTTree() for ParameterSets";
0771       RootOutputTree::writeTTree(parameterSetsTree_);
0772 
0773       status = "writeTTree() for parentage";
0774       RootOutputTree::writeTTree(parentageTree_);
0775 
0776       // Create branch aliases for all the branches in the
0777       // events/lumis/runs/processblock trees. The loop is over
0778       // all types of data products.
0779       status = "writeTree() for ";
0780       for (unsigned int i = 0; i < treePointers_.size(); ++i) {
0781         std::string processName;
0782         BranchType branchType = InProcess;
0783         if (i < InProcess) {
0784           branchType = static_cast<BranchType>(i);
0785         } else {
0786           processName = om_->outputProcessBlockHelper().processesWithProcessBlockProducts()[i - InProcess];
0787         }
0788         setBranchAliases(treePointers_[i]->tree(), om_->keptProducts()[branchType], processName);
0789         value = treePointers_[i]->tree()->GetName();
0790         treePointers_[i]->writeTree();
0791       }
0792 
0793       // close the file -- mfp
0794       // Just to play it safe, zero all pointers to objects in the TFile to be closed.
0795       status = "closing TTrees";
0796       value = "";
0797       metaDataTree_ = parentageTree_ = nullptr;
0798       for (auto& treePointer : treePointers_) {
0799         treePointer->close();
0800         treePointer = nullptr;
0801       }
0802       status = "closing TFile";
0803       filePtr_->Close();
0804       filePtr_ = nullptr;  // propagate_const<T> has no reset() function
0805 
0806       // report that file has been closed
0807       status = "reporting to JobReport";
0808       Service<JobReport> reportSvc;
0809       reportSvc->outputFileClosed(reportToken_);
0810     } catch (cms::Exception& e) {
0811       e.addContext("Calling RootOutputFile::finishEndFile() while closing " + file_);
0812       e.addAdditionalInfo("While calling " + std::string(status) + std::string(value));
0813       throw;
0814     }
0815   }
0816 
0817   void RootOutputFile::setBranchAliases(TTree* tree,
0818                                         SelectedProducts const& branches,
0819                                         std::string const& processName) const {
0820     if (tree && tree->GetNbranches() != 0) {
0821       for (auto const& selection : branches) {
0822         BranchDescription const& pd = *selection.first;
0823         if (pd.branchType() == InProcess && processName != pd.processName()) {
0824           continue;
0825         }
0826         std::string const& full = pd.branchName() + "obj";
0827         if (pd.branchAliases().empty()) {
0828           std::string const& alias = (pd.productInstanceName().empty() ? pd.moduleLabel() : pd.productInstanceName());
0829           tree->SetAlias(alias.c_str(), full.c_str());
0830         } else {
0831           for (auto const& alias : pd.branchAliases()) {
0832             tree->SetAlias(alias.c_str(), full.c_str());
0833           }
0834         }
0835       }
0836     }
0837   }
0838 
0839   void RootOutputFile::insertAncestors(ProductProvenance const& iGetParents,
0840                                        ProductProvenanceRetriever const* iMapper,
0841                                        bool produced,
0842                                        std::set<BranchID> const& iProducedIDs,
0843                                        std::set<StoredProductProvenance>& oToFill) {
0844     assert(om_->dropMetaData() != PoolOutputModule::DropAll);
0845     assert(produced || om_->dropMetaData() != PoolOutputModule::DropPrior);
0846     if (om_->dropMetaData() == PoolOutputModule::DropDroppedPrior && !produced)
0847       return;
0848     std::vector<BranchID> const& parentIDs = iGetParents.parentage().parents();
0849     for (auto const& parentID : parentIDs) {
0850       branchesWithStoredHistory_.insert(parentID);
0851       ProductProvenance const* info = iMapper->branchIDToProvenance(parentID);
0852       if (info) {
0853         if (om_->dropMetaData() == PoolOutputModule::DropNone ||
0854             (iProducedIDs.end() != iProducedIDs.find(info->branchID()))) {
0855           if (insertProductProvenance(*info, oToFill)) {
0856             //haven't seen this one yet
0857             insertAncestors(*info, iMapper, produced, iProducedIDs, oToFill);
0858           }
0859         }
0860       }
0861     }
0862   }
0863 
0864   void RootOutputFile::fillBranches(BranchType const& branchType,
0865                                     OccurrenceForOutput const& occurrence,
0866                                     unsigned int ttreeIndex,
0867                                     StoredProductProvenanceVector* productProvenanceVecPtr,
0868                                     ProductProvenanceRetriever const* provRetriever) {
0869     std::vector<std::unique_ptr<WrapperBase> > dummies;
0870 
0871     OutputItemList& items = om_->selectedOutputItemList()[ttreeIndex];
0872 
0873     bool const doProvenance =
0874         (productProvenanceVecPtr != nullptr) && (om_->dropMetaData() != PoolOutputModule::DropAll);
0875     bool const keepProvenanceForPrior = doProvenance && om_->dropMetaData() != PoolOutputModule::DropPrior;
0876 
0877     bool const fastCloning = (branchType == InEvent) && (whyNotFastClonable_ == FileBlock::CanFastClone);
0878     std::set<StoredProductProvenance> provenanceToKeep;
0879     //
0880     //If we are dropping some of the meta data we need to know
0881     // which BranchIDs were produced in this process because
0882     // we may be storing meta data for only those products
0883     // We do this only for event products.
0884     std::set<BranchID> producedBranches;
0885     if (doProvenance && branchType == InEvent && om_->dropMetaData() != PoolOutputModule::DropNone) {
0886       Service<ConstProductRegistry> preg;
0887       for (auto bd : preg->allBranchDescriptions()) {
0888         if (bd->produced() && bd->branchType() == InEvent) {
0889           producedBranches.insert(bd->branchID());
0890         }
0891       }
0892     }
0893 
0894     // Loop over EDProduct branches, possibly fill the provenance, and write the branch.
0895     for (auto& item : items) {
0896       BranchID const& id = item.branchDescription()->branchID();
0897       branchesWithStoredHistory_.insert(id);
0898 
0899       bool produced = item.branchDescription()->produced();
0900       bool getProd =
0901           (produced || !fastCloning || treePointers_[ttreeIndex]->uncloned(item.branchDescription()->branchName()));
0902       bool keepProvenance = doProvenance && (produced || keepProvenanceForPrior);
0903 
0904       WrapperBase const* product = nullptr;
0905       ProductProvenance const* productProvenance = nullptr;
0906       if (getProd) {
0907         BasicHandle result = occurrence.getByToken(item.token(), item.branchDescription()->unwrappedTypeID());
0908         product = result.wrapper();
0909         if (result.isValid() && keepProvenance) {
0910           productProvenance = result.provenance()->productProvenance();
0911         }
0912         if (product == nullptr) {
0913           // No product with this ID is in the event.
0914           // Add a null product.
0915           TClass* cp = item.branchDescription()->wrappedType().getClass();
0916           assert(cp != nullptr);
0917           int offset = cp->GetBaseClassOffset(wrapperBaseTClass_);
0918           void* p = cp->New();
0919           std::unique_ptr<WrapperBase> dummy = getWrapperBasePtr(p, offset);
0920           product = dummy.get();
0921           dummies.emplace_back(std::move(dummy));
0922         }
0923         item.setProduct(product);
0924       }
0925       if (keepProvenance && productProvenance == nullptr) {
0926         productProvenance = provRetriever->branchIDToProvenance(item.branchDescription()->originalBranchID());
0927       }
0928       if (productProvenance) {
0929         insertProductProvenance(*productProvenance, provenanceToKeep);
0930         insertAncestors(*productProvenance, provRetriever, produced, producedBranches, provenanceToKeep);
0931       }
0932     }
0933 
0934     if (doProvenance)
0935       productProvenanceVecPtr->assign(provenanceToKeep.begin(), provenanceToKeep.end());
0936     treePointers_[ttreeIndex]->fillTree();
0937     if (doProvenance)
0938       productProvenanceVecPtr->clear();
0939   }
0940 
0941   bool RootOutputFile::insertProductProvenance(const edm::ProductProvenance& iProv,
0942                                                std::set<edm::StoredProductProvenance>& oToInsert) {
0943     StoredProductProvenance toStore;
0944     toStore.branchID_ = iProv.branchID().id();
0945     std::set<edm::StoredProductProvenance>::iterator itFound = oToInsert.find(toStore);
0946     if (itFound == oToInsert.end()) {
0947       //get the index to the ParentageID or insert a new value if not already present
0948       std::pair<std::map<edm::ParentageID, unsigned int>::iterator, bool> i =
0949           parentageIDs_.insert(std::make_pair(iProv.parentageID(), static_cast<unsigned int>(parentageIDs_.size())));
0950       toStore.parentageIDIndex_ = i.first->second;
0951       if (toStore.parentageIDIndex_ >= parentageIDs_.size()) {
0952         throw edm::Exception(errors::LogicError)
0953             << "RootOutputFile::insertProductProvenance\n"
0954             << "The parentage ID index value " << toStore.parentageIDIndex_
0955             << " is out of bounds.  The maximum value is currently " << parentageIDs_.size() - 1 << ".\n"
0956             << "This should never happen.\n"
0957             << "Please report this to the framework developers.";
0958       }
0959 
0960       oToInsert.insert(toStore);
0961       return true;
0962     }
0963     return false;
0964   }
0965 }  // namespace edm