Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:13:25

0001 // -*- C++ -*-
0002 //
0003 //
0004 
0005 // class template ConcurrentGeneratorFilter<HAD> provides an EDFilter which uses
0006 // the hadronizer type HAD to generate partons, hadronize them,
0007 // and decay the resulting particles, in the CMS framework.
0008 
0009 // Some additional notes related to concurrency:
0010 //
0011 //     This is an unusual module in CMSSW because its hadronizers are in stream caches
0012 //     (one hadronizer per stream cache). The Framework expects objects in a stream
0013 //     cache to only be used in stream transitions associated with that stream. That
0014 //     is how the Framework provides thread safety and avoids data races. In this module
0015 //     a global transition needs to use one of the hadronizers. The
0016 //     globalBeginLuminosityBlockProduce method uses one hadronizer to create the
0017 //     GenLumiInfoHeader which is put in the LuminosityBlock. This hadronizer must be
0018 //     initialized for the lumi before creating the product. This creates a problem because
0019 //     the global method might run concurrently with the stream methods. There is extra
0020 //     complexity in this module to deal with that unusual usage of an object in a stream cache.
0021 //
0022 //     The solution of this issue is conceptually simple. The module explicitly makes
0023 //     globalBeginLuminosityBlock wait until the previous lumi is finished on one stream
0024 //     and also until streamEndRun is finished on that stream if there was a new run. It
0025 //     avoids doing work in streamBeginRun. There is extra complexity in this module to
0026 //     ensure thread safety that normally does not appear in modules (usually this kind of
0027 //     thing is handled in the Framework).
0028 //
0029 //     Two alternative solutions were considered when designing this implementation and
0030 //     possibly someday we might reimplement this using one of them if we find this
0031 //     complexity hard to maintain.
0032 //
0033 //     1. We could make an extra hadronizer only for the global transition. We rejected
0034 //     that idea because that would require extra memory and CPU resources.
0035 //
0036 //     2. We could put the GenLumiInfoHeader product into the LuminosityBlock at the end
0037 //     global transition. We didn't know whether anything depended on the product being
0038 //     present in the begin transition or how difficult it would be to remove such a dependence
0039 //     so we also rejected that alternative.
0040 //
0041 //     There might be other ways to deal with this concurrency issue. This issue became
0042 //     important when run concurrency support was implemented in the Framework. That support
0043 //     allowed the streamBeginRun and streamEndRun transitions to run concurrently with other
0044 //     transitions even in the case where the number of concurrent runs was limited to 1.
0045 
0046 #ifndef GeneratorInterface_Core_ConcurrentGeneratorFilter_h
0047 #define GeneratorInterface_Core_ConcurrentGeneratorFilter_h
0048 
0049 #include <memory>
0050 #include <string>
0051 #include <vector>
0052 #include <atomic>
0053 
0054 #include "FWCore/Framework/interface/global/EDFilter.h"
0055 #include "FWCore/Framework/interface/Event.h"
0056 #include "FWCore/Framework/interface/EventSetup.h"
0057 #include "FWCore/Framework/interface/FileBlock.h"
0058 #include "FWCore/Framework/interface/LuminosityBlock.h"
0059 #include "FWCore/Framework/interface/MakerMacros.h"
0060 #include "FWCore/Framework/interface/Run.h"
0061 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0062 #include "FWCore/ServiceRegistry/interface/RandomEngineSentry.h"
0063 #include "FWCore/Utilities/interface/BranchType.h"
0064 #include "FWCore/Utilities/interface/EDMException.h"
0065 #include "FWCore/Utilities/interface/EDGetToken.h"
0066 #include "FWCore/Utilities/interface/TypeID.h"
0067 #include "DataFormats/Provenance/interface/BranchDescription.h"
0068 #include "CLHEP/Random/RandomEngine.h"
0069 
0070 // LHE Run
0071 #include "SimDataFormats/GeneratorProducts/interface/LHERunInfoProduct.h"
0072 #include "GeneratorInterface/LHEInterface/interface/LHERunInfo.h"
0073 
0074 // LHE Event
0075 #include "SimDataFormats/GeneratorProducts/interface/LHEEventProduct.h"
0076 #include "GeneratorInterface/LHEInterface/interface/LHEEvent.h"
0077 
0078 #include "SimDataFormats/GeneratorProducts/interface/HepMCProduct.h"
0079 #include "SimDataFormats/GeneratorProducts/interface/GenRunInfoProduct.h"
0080 #include "SimDataFormats/GeneratorProducts/interface/GenLumiInfoHeader.h"
0081 #include "SimDataFormats/GeneratorProducts/interface/GenLumiInfoProduct.h"
0082 #include "SimDataFormats/GeneratorProducts/interface/GenEventInfoProduct.h"
0083 
0084 namespace edm {
0085   namespace gen {
0086     struct GenRunCache {
0087       mutable std::atomic<GenRunInfoProduct*> product_{nullptr};
0088       ~GenRunCache() { delete product_.load(); }
0089 
0090       // This is called from globalEndRunProduce which is known to
0091       // be safe as the framework would not be calling any other
0092       // methods of this module using this run at that time
0093       std::unique_ptr<GenRunInfoProduct> release() const noexcept {
0094         auto retValue = product_.load();
0095         product_.store(nullptr);
0096         return std::unique_ptr<GenRunInfoProduct>(retValue);
0097       }
0098     };
0099     struct GenLumiSummary {
0100       mutable std::unique_ptr<GenLumiInfoProduct> lumiInfo_;
0101     };
0102     template <typename HAD, typename DEC>
0103     struct GenStreamCache {
0104       GenStreamCache(ParameterSet const& iPSet) : hadronizer_{iPSet}, nEventsInLumiBlock_{0} {}
0105       HAD hadronizer_;
0106       std::unique_ptr<DEC> decayer_;
0107       unsigned int nEventsInLumiBlock_;
0108       bool initialized_ = false;
0109     };
0110     template <typename HAD, typename DEC>
0111     struct GenLumiCache {
0112       gen::GenStreamCache<HAD, DEC>* useInLumi_{nullptr};
0113       unsigned long long nGlobalBeginLumis_{0};
0114     };
0115   }  // namespace gen
0116 
0117   template <class HAD, class DEC>
0118   class ConcurrentGeneratorFilter : public global::EDFilter<EndRunProducer,
0119                                                             BeginLuminosityBlockProducer,
0120                                                             EndLuminosityBlockProducer,
0121                                                             RunCache<gen::GenRunCache>,
0122                                                             LuminosityBlockCache<gen::GenLumiCache<HAD, DEC>>,
0123                                                             LuminosityBlockSummaryCache<gen::GenLumiSummary>,
0124                                                             StreamCache<gen::GenStreamCache<HAD, DEC>>> {
0125   public:
0126     typedef HAD Hadronizer;
0127     typedef DEC Decayer;
0128 
0129     // The given ParameterSet will be passed to the contained Hadronizer object.
0130     explicit ConcurrentGeneratorFilter(ParameterSet const& ps);
0131 
0132     bool filter(StreamID id, Event& e, EventSetup const& es) const override;
0133     std::unique_ptr<gen::GenStreamCache<HAD, DEC>> beginStream(StreamID) const override;
0134     std::shared_ptr<gen::GenRunCache> globalBeginRun(Run const&, EventSetup const&) const override;
0135     std::shared_ptr<gen::GenLumiSummary> globalBeginLuminosityBlockSummary(LuminosityBlock const&,
0136                                                                            EventSetup const&) const override;
0137     std::shared_ptr<gen::GenLumiCache<HAD, DEC>> globalBeginLuminosityBlock(LuminosityBlock const&,
0138                                                                             EventSetup const&) const override;
0139     void globalBeginLuminosityBlockProduce(LuminosityBlock&, EventSetup const&) const override;
0140     void streamBeginLuminosityBlock(StreamID, LuminosityBlock const&, EventSetup const&) const override;
0141     void streamEndLuminosityBlock(StreamID, LuminosityBlock const&, EventSetup const&) const override;
0142     void streamEndLuminosityBlockSummary(StreamID,
0143                                          LuminosityBlock const&,
0144                                          EventSetup const&,
0145                                          gen::GenLumiSummary*) const override;
0146     void globalEndLuminosityBlock(LuminosityBlock const&, EventSetup const&) const override;
0147     void globalEndLuminosityBlockSummary(LuminosityBlock const&,
0148                                          EventSetup const&,
0149                                          gen::GenLumiSummary*) const override;
0150     void globalEndLuminosityBlockProduce(LuminosityBlock&,
0151                                          EventSetup const&,
0152                                          gen::GenLumiSummary const*) const override;
0153     void streamEndRun(StreamID, Run const&, EventSetup const&) const override;
0154     void globalEndRun(Run const&, EventSetup const&) const override;
0155     void globalEndRunProduce(Run&, EventSetup const&) const override;
0156 
0157   private:
0158     void initLumi(gen::GenStreamCache<HAD, DEC>* cache, LuminosityBlock const& index, EventSetup const& es) const;
0159     ParameterSet config_;
0160 
0161     // The following six variables depend on the fact that the Framework does
0162     // not execute global begin lumi transitions and global begin run transitions
0163     // concurrently. Within a transition, modules might execute concurrently,
0164     // but only one such transition will be active at a time.
0165     mutable std::atomic<gen::GenStreamCache<HAD, DEC>*> useInLumi_{nullptr};
0166     mutable std::atomic<unsigned long long> nextNGlobalBeginLumis_{1};
0167     mutable std::atomic<bool> streamEndRunComplete_{true};
0168     mutable unsigned long long nGlobalBeginRuns_{0};
0169     mutable unsigned long long nInitializedInGlobalLumiAfterNewRun_{0};
0170     mutable unsigned long long nGlobalBeginLumis_{0};
0171   };
0172 
0173   //------------------------------------------------------------------------
0174   //
0175   // Implementation
0176 
0177   template <class HAD, class DEC>
0178   ConcurrentGeneratorFilter<HAD, DEC>::ConcurrentGeneratorFilter(ParameterSet const& ps) : config_(ps) {
0179     // TODO:
0180     // Put the list of types produced by the filters here.
0181     // The current design calls for:
0182     //   * LHEGeneratorInfo
0183     //   * LHEEvent
0184     //   * HepMCProduct
0185     // But I can not find the LHEGeneratorInfo class; it might need to
0186     // be invented.
0187 
0188     this->template produces<HepMCProduct>("unsmeared");
0189     this->template produces<GenEventInfoProduct>();
0190     this->template produces<GenLumiInfoHeader, edm::Transition::BeginLuminosityBlock>();
0191     this->template produces<GenLumiInfoProduct, edm::Transition::EndLuminosityBlock>();
0192     this->template produces<GenRunInfoProduct, edm::Transition::EndRun>();
0193   }
0194 
0195   template <class HAD, class DEC>
0196   std::unique_ptr<gen::GenStreamCache<HAD, DEC>> ConcurrentGeneratorFilter<HAD, DEC>::beginStream(StreamID) const {
0197     auto cache = std::make_unique<gen::GenStreamCache<HAD, DEC>>(config_);
0198 
0199     if (config_.exists("ExternalDecays")) {
0200       ParameterSet ps1 = config_.getParameter<ParameterSet>("ExternalDecays");
0201       cache->decayer_.reset(new Decayer(ps1));
0202     }
0203 
0204     // We need a hadronizer during globalBeginLumiProduce, doesn't matter which one
0205     gen::GenStreamCache<HAD, DEC>* expected = nullptr;
0206     useInLumi_.compare_exchange_strong(expected, cache.get());
0207 
0208     return cache;
0209   }
0210 
0211   template <class HAD, class DEC>
0212   std::shared_ptr<gen::GenRunCache> ConcurrentGeneratorFilter<HAD, DEC>::globalBeginRun(Run const&,
0213                                                                                         EventSetup const&) const {
0214     ++nGlobalBeginRuns_;
0215     return std::make_shared<gen::GenRunCache>();
0216   }
0217 
0218   template <class HAD, class DEC>
0219   std::shared_ptr<gen::GenLumiSummary> ConcurrentGeneratorFilter<HAD, DEC>::globalBeginLuminosityBlockSummary(
0220       LuminosityBlock const&, EventSetup const&) const {
0221     return std::make_shared<gen::GenLumiSummary>();
0222   }
0223 
0224   template <class HAD, class DEC>
0225   void ConcurrentGeneratorFilter<HAD, DEC>::initLumi(gen::GenStreamCache<HAD, DEC>* cache,
0226                                                      LuminosityBlock const& lumi,
0227                                                      EventSetup const& es) const {
0228     cache->nEventsInLumiBlock_ = 0;
0229 
0230     // We need all copies to see same random # for begin lumi
0231     Service<RandomNumberGenerator> rng;
0232     auto enginePtr = rng->cloneEngine(lumi.index());
0233     cache->hadronizer_.setRandomEngine(enginePtr.get());
0234     if (cache->decayer_) {
0235       cache->decayer_->setRandomEngine(enginePtr.get());
0236     }
0237 
0238     auto unsetH = [](HAD* h) { h->setRandomEngine(nullptr); };
0239     auto unsetD = [](DEC* d) {
0240       if (d) {
0241         d->setRandomEngine(nullptr);
0242       }
0243     };
0244 
0245     std::unique_ptr<HAD, decltype(unsetH)> randomEngineSentry(&cache->hadronizer_, unsetH);
0246     std::unique_ptr<DEC, decltype(unsetD)> randomEngineSentryDecay(cache->decayer_.get(), unsetD);
0247 
0248     cache->hadronizer_.randomizeIndex(lumi, enginePtr.get());
0249 
0250     if (!cache->hadronizer_.readSettings(0))
0251       throw edm::Exception(errors::Configuration)
0252           << "Failed to read settings for the hadronizer " << cache->hadronizer_.classname() << " \n";
0253 
0254     if (cache->decayer_) {
0255       cache->decayer_->init(es);
0256       if (!cache->hadronizer_.declareStableParticles(cache->decayer_->operatesOnParticles()))
0257         throw edm::Exception(errors::Configuration)
0258             << "Failed to declare stable particles in hadronizer " << cache->hadronizer_.classname()
0259             << " for internal parton generation\n";
0260       if (!cache->hadronizer_.declareSpecialSettings(cache->decayer_->specialSettings()))
0261         throw edm::Exception(errors::Configuration)
0262             << "Failed to declare special settings in hadronizer " << cache->hadronizer_.classname() << "\n";
0263     }
0264 
0265     if (!cache->hadronizer_.initializeForInternalPartons())
0266       throw edm::Exception(errors::Configuration)
0267           << "Failed to initialize hadronizer " << cache->hadronizer_.classname()
0268           << " for internal parton generation\n";
0269 
0270     cache->initialized_ = true;
0271   }
0272 
0273   template <class HAD, class DEC>
0274   bool ConcurrentGeneratorFilter<HAD, DEC>::filter(StreamID id, Event& ev, EventSetup const& /* es */) const {
0275     auto cache = this->streamCache(id);
0276     RandomEngineSentry<HAD> randomEngineSentry(&cache->hadronizer_, ev.streamID());
0277     RandomEngineSentry<DEC> randomEngineSentryDecay(cache->decayer_.get(), ev.streamID());
0278 
0279     cache->hadronizer_.setEDMEvent(ev);
0280 
0281     bool passEvtGenSelector = false;
0282     std::unique_ptr<HepMC::GenEvent> event(nullptr);
0283 
0284     while (!passEvtGenSelector) {
0285       event.reset();
0286       cache->hadronizer_.setEDMEvent(ev);
0287 
0288       if (!cache->hadronizer_.generatePartonsAndHadronize())
0289         return false;
0290 
0291       // this is "fake" stuff
0292       // in principle, decays are done as part of full event generation,
0293       // except for particles that are marked as to be kept stable
0294       // but we currently keep in it the design, because we might want
0295       // to use such feature for other applications
0296       //
0297       if (!cache->hadronizer_.decay())
0298         return false;
0299 
0300       event = cache->hadronizer_.getGenEvent();
0301       if (!event.get())
0302         return false;
0303 
0304       //
0305       // The external decay driver is being added to the system, it should be called here
0306       //
0307       if (cache->decayer_) {
0308         auto t = cache->decayer_->decay(event.get());
0309         if (t != event.get()) {
0310           event.reset(t);
0311         }
0312       }
0313       if (!event.get())
0314         return false;
0315 
0316       passEvtGenSelector = cache->hadronizer_.select(event.get());
0317     }
0318 
0319     // check and perform if there're any unstable particles after
0320     // running external decay packages
0321     //
0322     // fisrt of all, put back modified event tree (after external decay)
0323     //
0324     cache->hadronizer_.resetEvent(std::move(event));
0325 
0326     //
0327     // now run residual decays
0328     //
0329     if (!cache->hadronizer_.residualDecay())
0330       return false;
0331 
0332     cache->hadronizer_.finalizeEvent();
0333 
0334     event = cache->hadronizer_.getGenEvent();
0335     if (!event.get())
0336       return false;
0337 
0338     event->set_event_number(ev.id().event());
0339 
0340     //
0341     // finally, form up EDM products !
0342     //
0343     std::unique_ptr<GenEventInfoProduct> genEventInfo(cache->hadronizer_.getGenEventInfo());
0344     if (!genEventInfo.get()) {
0345       // create GenEventInfoProduct from HepMC event in case hadronizer didn't provide one
0346       genEventInfo = std::make_unique<GenEventInfoProduct>(event.get());
0347     }
0348 
0349     ev.put(std::move(genEventInfo));
0350 
0351     std::unique_ptr<HepMCProduct> bare_product(new HepMCProduct());
0352     bare_product->addHepMCData(event.release());
0353     ev.put(std::move(bare_product), "unsmeared");
0354     cache->nEventsInLumiBlock_++;
0355     return true;
0356   }
0357 
0358   template <class HAD, class DEC>
0359   std::shared_ptr<gen::GenLumiCache<HAD, DEC>> ConcurrentGeneratorFilter<HAD, DEC>::globalBeginLuminosityBlock(
0360       edm::LuminosityBlock const&, edm::EventSetup const&) const {
0361     //need one of the streams to finish
0362     while (useInLumi_.load() == nullptr) {
0363     }
0364 
0365     ++nGlobalBeginLumis_;
0366 
0367     // streamEndRun also uses the hadronizer in the stream cache
0368     // so we also need to wait for it to finish if there is a new run
0369     if (nInitializedInGlobalLumiAfterNewRun_ < nGlobalBeginRuns_) {
0370       while (!streamEndRunComplete_.load()) {
0371       }
0372       nInitializedInGlobalLumiAfterNewRun_ = nGlobalBeginRuns_;
0373     }
0374 
0375     auto lumiCache = std::make_shared<gen::GenLumiCache<HAD, DEC>>();
0376     lumiCache->useInLumi_ = useInLumi_.load();
0377     lumiCache->nGlobalBeginLumis_ = nGlobalBeginLumis_;
0378     return lumiCache;
0379   }
0380 
0381   template <class HAD, class DEC>
0382   void ConcurrentGeneratorFilter<HAD, DEC>::globalBeginLuminosityBlockProduce(LuminosityBlock& lumi,
0383                                                                               EventSetup const& es) const {
0384     initLumi(useInLumi_, lumi, es);
0385     std::unique_ptr<GenLumiInfoHeader> genLumiInfoHeader(useInLumi_.load()->hadronizer_.getGenLumiInfoHeader());
0386     lumi.put(std::move(genLumiInfoHeader));
0387     useInLumi_.store(nullptr);
0388   }
0389 
0390   template <class HAD, class DEC>
0391   void ConcurrentGeneratorFilter<HAD, DEC>::streamBeginLuminosityBlock(StreamID id,
0392                                                                        LuminosityBlock const& lumi,
0393                                                                        EventSetup const& es) const {
0394     gen::GenStreamCache<HAD, DEC>* streamCachePtr = this->streamCache(id);
0395     if (this->luminosityBlockCache(lumi.index())->useInLumi_ != streamCachePtr) {
0396       initLumi(streamCachePtr, lumi, es);
0397     }
0398   }
0399 
0400   template <class HAD, class DEC>
0401   void ConcurrentGeneratorFilter<HAD, DEC>::streamEndLuminosityBlock(StreamID id,
0402                                                                      LuminosityBlock const&,
0403                                                                      EventSetup const&) const {
0404     this->streamCache(id)->hadronizer_.cleanLHE();
0405   }
0406 
0407   template <class HAD, class DEC>
0408   void ConcurrentGeneratorFilter<HAD, DEC>::streamEndLuminosityBlockSummary(StreamID id,
0409                                                                             LuminosityBlock const& lumi,
0410                                                                             EventSetup const&,
0411                                                                             gen::GenLumiSummary* iSummary) const {
0412     auto cache = this->streamCache(id);
0413     cache->hadronizer_.statistics();
0414     if (cache->decayer_)
0415       cache->decayer_->statistics();
0416 
0417     GenRunInfoProduct genRunInfo = GenRunInfoProduct(cache->hadronizer_.getGenRunInfo());
0418     std::vector<GenLumiInfoProduct::ProcessInfo> GenLumiProcess;
0419     const GenRunInfoProduct::XSec& xsec = genRunInfo.internalXSec();
0420     GenLumiInfoProduct::ProcessInfo temp;
0421     unsigned int nEvtInLumiBlock_ = cache->nEventsInLumiBlock_;
0422     temp.setProcess(0);
0423     temp.setLheXSec(xsec.value(), xsec.error());  // Pythia gives error of -1
0424     temp.setNPassPos(nEvtInLumiBlock_);
0425     temp.setNPassNeg(0);
0426     temp.setNTotalPos(nEvtInLumiBlock_);
0427     temp.setNTotalNeg(0);
0428     temp.setTried(nEvtInLumiBlock_, nEvtInLumiBlock_, nEvtInLumiBlock_);
0429     temp.setSelected(nEvtInLumiBlock_, nEvtInLumiBlock_, nEvtInLumiBlock_);
0430     temp.setKilled(nEvtInLumiBlock_, nEvtInLumiBlock_, nEvtInLumiBlock_);
0431     temp.setAccepted(0, -1, -1);
0432     temp.setAcceptedBr(0, -1, -1);
0433     GenLumiProcess.push_back(temp);
0434 
0435     GenLumiInfoProduct genLumiInfo;
0436     genLumiInfo.setHEPIDWTUP(-1);
0437     genLumiInfo.setProcessInfo(GenLumiProcess);
0438 
0439     if (iSummary->lumiInfo_) {
0440       iSummary->lumiInfo_->mergeProduct(genLumiInfo);
0441     } else {
0442       iSummary->lumiInfo_ = std::make_unique<GenLumiInfoProduct>(std::move(genLumiInfo));
0443     }
0444 
0445     cache->nEventsInLumiBlock_ = 0;
0446 
0447     gen::GenStreamCache<HAD, DEC>* streamCachePtr = this->streamCache(id);
0448     unsigned long long expected = this->luminosityBlockCache(lumi.index())->nGlobalBeginLumis_;
0449     unsigned long long nextValue = expected + 1;
0450     // This exchange should succeed and the conditional block should be executed only
0451     // for the first stream to try for each lumi.
0452     if (nextNGlobalBeginLumis_.compare_exchange_strong(expected, nextValue)) {
0453       streamEndRunComplete_ = false;
0454       useInLumi_ = streamCachePtr;
0455     }
0456   }
0457 
0458   template <class HAD, class DEC>
0459   void ConcurrentGeneratorFilter<HAD, DEC>::globalEndLuminosityBlock(edm::LuminosityBlock const&,
0460                                                                      edm::EventSetup const&) const {}
0461 
0462   template <class HAD, class DEC>
0463   void ConcurrentGeneratorFilter<HAD, DEC>::globalEndLuminosityBlockSummary(LuminosityBlock const&,
0464                                                                             EventSetup const&,
0465                                                                             gen::GenLumiSummary*) const {}
0466 
0467   template <class HAD, class DEC>
0468   void ConcurrentGeneratorFilter<HAD, DEC>::globalEndLuminosityBlockProduce(LuminosityBlock& lumi,
0469                                                                             EventSetup const&,
0470                                                                             gen::GenLumiSummary const* iSummary) const {
0471     lumi.put(std::move(iSummary->lumiInfo_));
0472   }
0473 
0474   template <class HAD, class DEC>
0475   void ConcurrentGeneratorFilter<HAD, DEC>::streamEndRun(StreamID id, Run const& run, EventSetup const&) const {
0476     auto rCache = this->runCache(run.index());
0477     auto cache = this->streamCache(id);
0478 
0479     // If relevant, record the integrated luminosity for this run
0480     // here.  To do so, we would need a standard function to invoke on
0481     // the contained hadronizer that would report the integrated
0482     // luminosity.
0483 
0484     if (cache->initialized_) {
0485       cache->hadronizer_.statistics();
0486       if (cache->decayer_)
0487         cache->decayer_->statistics();
0488     }
0489     GenRunInfoProduct& genRunInfo = cache->hadronizer_.getGenRunInfo();
0490     GenRunInfoProduct* expect = nullptr;
0491 
0492     std::unique_ptr<GenRunInfoProduct> griproduct(new GenRunInfoProduct(genRunInfo));
0493     // All the GenRunInfoProducts for all streams shoule be identical, therefore we only need one
0494     if (rCache->product_.compare_exchange_strong(expect, griproduct.get())) {
0495       griproduct.release();
0496     }
0497     if (cache == useInLumi_.load()) {
0498       streamEndRunComplete_ = true;
0499     }
0500   }
0501 
0502   template <class HAD, class DEC>
0503   void ConcurrentGeneratorFilter<HAD, DEC>::globalEndRun(Run const&, EventSetup const&) const {}
0504 
0505   template <class HAD, class DEC>
0506   void ConcurrentGeneratorFilter<HAD, DEC>::globalEndRunProduce(Run& run, EventSetup const&) const {
0507     run.put(this->runCache(run.index())->release());
0508   }
0509 
0510 }  // namespace edm
0511 
0512 #endif  // GeneratorInterface_Core_ConcurrentGeneratorFilter_h