Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2022-05-12 01:51:32

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     metaDataTree_->SetEntries(-1);
0765     RootOutputTree::writeTTree(metaDataTree_);
0766     RootOutputTree::writeTTree(parameterSetsTree_);
0767 
0768     RootOutputTree::writeTTree(parentageTree_);
0769 
0770     // Create branch aliases for all the branches in the
0771     // events/lumis/runs/processblock trees. The loop is over
0772     // all types of data products.
0773     for (unsigned int i = 0; i < treePointers_.size(); ++i) {
0774       std::string processName;
0775       BranchType branchType = InProcess;
0776       if (i < InProcess) {
0777         branchType = static_cast<BranchType>(i);
0778       } else {
0779         processName = om_->outputProcessBlockHelper().processesWithProcessBlockProducts()[i - InProcess];
0780       }
0781       setBranchAliases(treePointers_[i]->tree(), om_->keptProducts()[branchType], processName);
0782       treePointers_[i]->writeTree();
0783     }
0784 
0785     // close the file -- mfp
0786     // Just to play it safe, zero all pointers to objects in the TFile to be closed.
0787     metaDataTree_ = parentageTree_ = nullptr;
0788     for (auto& treePointer : treePointers_) {
0789       treePointer->close();
0790       treePointer = nullptr;
0791     }
0792     filePtr_->Close();
0793     filePtr_ = nullptr;  // propagate_const<T> has no reset() function
0794 
0795     // report that file has been closed
0796     Service<JobReport> reportSvc;
0797     reportSvc->outputFileClosed(reportToken_);
0798   }
0799 
0800   void RootOutputFile::setBranchAliases(TTree* tree,
0801                                         SelectedProducts const& branches,
0802                                         std::string const& processName) const {
0803     if (tree && tree->GetNbranches() != 0) {
0804       for (auto const& selection : branches) {
0805         BranchDescription const& pd = *selection.first;
0806         if (pd.branchType() == InProcess && processName != pd.processName()) {
0807           continue;
0808         }
0809         std::string const& full = pd.branchName() + "obj";
0810         if (pd.branchAliases().empty()) {
0811           std::string const& alias = (pd.productInstanceName().empty() ? pd.moduleLabel() : pd.productInstanceName());
0812           tree->SetAlias(alias.c_str(), full.c_str());
0813         } else {
0814           for (auto const& alias : pd.branchAliases()) {
0815             tree->SetAlias(alias.c_str(), full.c_str());
0816           }
0817         }
0818       }
0819     }
0820   }
0821 
0822   void RootOutputFile::insertAncestors(ProductProvenance const& iGetParents,
0823                                        ProductProvenanceRetriever const* iMapper,
0824                                        bool produced,
0825                                        std::set<BranchID> const& iProducedIDs,
0826                                        std::set<StoredProductProvenance>& oToFill) {
0827     assert(om_->dropMetaData() != PoolOutputModule::DropAll);
0828     assert(produced || om_->dropMetaData() != PoolOutputModule::DropPrior);
0829     if (om_->dropMetaData() == PoolOutputModule::DropDroppedPrior && !produced)
0830       return;
0831     std::vector<BranchID> const& parentIDs = iGetParents.parentage().parents();
0832     for (auto const& parentID : parentIDs) {
0833       branchesWithStoredHistory_.insert(parentID);
0834       ProductProvenance const* info = iMapper->branchIDToProvenance(parentID);
0835       if (info) {
0836         if (om_->dropMetaData() == PoolOutputModule::DropNone ||
0837             (iProducedIDs.end() != iProducedIDs.find(info->branchID()))) {
0838           if (insertProductProvenance(*info, oToFill)) {
0839             //haven't seen this one yet
0840             insertAncestors(*info, iMapper, produced, iProducedIDs, oToFill);
0841           }
0842         }
0843       }
0844     }
0845   }
0846 
0847   void RootOutputFile::fillBranches(BranchType const& branchType,
0848                                     OccurrenceForOutput const& occurrence,
0849                                     unsigned int ttreeIndex,
0850                                     StoredProductProvenanceVector* productProvenanceVecPtr,
0851                                     ProductProvenanceRetriever const* provRetriever) {
0852     std::vector<std::unique_ptr<WrapperBase> > dummies;
0853 
0854     OutputItemList& items = om_->selectedOutputItemList()[ttreeIndex];
0855 
0856     bool const doProvenance =
0857         (productProvenanceVecPtr != nullptr) && (om_->dropMetaData() != PoolOutputModule::DropAll);
0858     bool const keepProvenanceForPrior = doProvenance && om_->dropMetaData() != PoolOutputModule::DropPrior;
0859 
0860     bool const fastCloning = (branchType == InEvent) && (whyNotFastClonable_ == FileBlock::CanFastClone);
0861     std::set<StoredProductProvenance> provenanceToKeep;
0862     //
0863     //If we are dropping some of the meta data we need to know
0864     // which BranchIDs were produced in this process because
0865     // we may be storing meta data for only those products
0866     // We do this only for event products.
0867     std::set<BranchID> producedBranches;
0868     if (doProvenance && branchType == InEvent && om_->dropMetaData() != PoolOutputModule::DropNone) {
0869       Service<ConstProductRegistry> preg;
0870       for (auto bd : preg->allBranchDescriptions()) {
0871         if (bd->produced() && bd->branchType() == InEvent) {
0872           producedBranches.insert(bd->branchID());
0873         }
0874       }
0875     }
0876 
0877     // Loop over EDProduct branches, possibly fill the provenance, and write the branch.
0878     for (auto& item : items) {
0879       BranchID const& id = item.branchDescription()->branchID();
0880       branchesWithStoredHistory_.insert(id);
0881 
0882       bool produced = item.branchDescription()->produced();
0883       bool getProd =
0884           (produced || !fastCloning || treePointers_[ttreeIndex]->uncloned(item.branchDescription()->branchName()));
0885       bool keepProvenance = doProvenance && (produced || keepProvenanceForPrior);
0886 
0887       WrapperBase const* product = nullptr;
0888       ProductProvenance const* productProvenance = nullptr;
0889       if (getProd) {
0890         BasicHandle result = occurrence.getByToken(item.token(), item.branchDescription()->unwrappedTypeID());
0891         product = result.wrapper();
0892         if (result.isValid() && keepProvenance) {
0893           productProvenance = result.provenance()->productProvenance();
0894         }
0895         if (product == nullptr) {
0896           // No product with this ID is in the event.
0897           // Add a null product.
0898           TClass* cp = item.branchDescription()->wrappedType().getClass();
0899           assert(cp != nullptr);
0900           int offset = cp->GetBaseClassOffset(wrapperBaseTClass_);
0901           void* p = cp->New();
0902           std::unique_ptr<WrapperBase> dummy = getWrapperBasePtr(p, offset);
0903           product = dummy.get();
0904           dummies.emplace_back(std::move(dummy));
0905         }
0906         item.setProduct(product);
0907       }
0908       if (keepProvenance && productProvenance == nullptr) {
0909         productProvenance = provRetriever->branchIDToProvenance(item.branchDescription()->originalBranchID());
0910       }
0911       if (productProvenance) {
0912         insertProductProvenance(*productProvenance, provenanceToKeep);
0913         insertAncestors(*productProvenance, provRetriever, produced, producedBranches, provenanceToKeep);
0914       }
0915     }
0916 
0917     if (doProvenance)
0918       productProvenanceVecPtr->assign(provenanceToKeep.begin(), provenanceToKeep.end());
0919     treePointers_[ttreeIndex]->fillTree();
0920     if (doProvenance)
0921       productProvenanceVecPtr->clear();
0922   }
0923 
0924   bool RootOutputFile::insertProductProvenance(const edm::ProductProvenance& iProv,
0925                                                std::set<edm::StoredProductProvenance>& oToInsert) {
0926     StoredProductProvenance toStore;
0927     toStore.branchID_ = iProv.branchID().id();
0928     std::set<edm::StoredProductProvenance>::iterator itFound = oToInsert.find(toStore);
0929     if (itFound == oToInsert.end()) {
0930       //get the index to the ParentageID or insert a new value if not already present
0931       std::pair<std::map<edm::ParentageID, unsigned int>::iterator, bool> i =
0932           parentageIDs_.insert(std::make_pair(iProv.parentageID(), static_cast<unsigned int>(parentageIDs_.size())));
0933       toStore.parentageIDIndex_ = i.first->second;
0934       if (toStore.parentageIDIndex_ >= parentageIDs_.size()) {
0935         throw edm::Exception(errors::LogicError)
0936             << "RootOutputFile::insertProductProvenance\n"
0937             << "The parentage ID index value " << toStore.parentageIDIndex_
0938             << " is out of bounds.  The maximum value is currently " << parentageIDs_.size() - 1 << ".\n"
0939             << "This should never happen.\n"
0940             << "Please report this to the framework hypernews forum 'hn-cms-edmFramework@cern.ch'.\n";
0941       }
0942 
0943       oToInsert.insert(toStore);
0944       return true;
0945     }
0946     return false;
0947   }
0948 }  // namespace edm