Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-09-14 02:33:50

0001 // system include files
0002 #include <memory>
0003 #include <string>
0004 
0005 // framework
0006 #include "FWCore/Framework/interface/ConsumesCollector.h"
0007 #include "FWCore/Framework/interface/Frameworkfwd.h"
0008 #include "FWCore/Framework/interface/stream/EDProducer.h"
0009 #include "FWCore/Framework/interface/EventSetup.h"
0010 #include "FWCore/Framework/interface/Event.h"
0011 #include "FWCore/Framework/interface/MakerMacros.h"
0012 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0013 #include "FWCore/Utilities/interface/StreamID.h"
0014 #include "FWCore/Framework/interface/LuminosityBlock.h"
0015 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0016 #include "FWCore/Framework/interface/ESWatcher.h"
0017 
0018 // data formats
0019 #include "SimDataFormats/GeneratorProducts/interface/HepMCProduct.h"
0020 #include "SimDataFormats/TrackingHit/interface/PSimHitContainer.h"
0021 #include "SimDataFormats/Track/interface/SimTrackContainer.h"
0022 #include "SimDataFormats/Vertex/interface/SimVertexContainer.h"
0023 #include "DataFormats/Common/interface/Handle.h"
0024 #include "DataFormats/Math/interface/LorentzVector.h"
0025 
0026 // fastsim
0027 #include "FastSimulation/Utilities/interface/RandomEngineAndDistribution.h"
0028 #include "FastSimulation/SimplifiedGeometryPropagator/interface/Geometry.h"
0029 #include "FastSimulation/SimplifiedGeometryPropagator/interface/SimplifiedGeometry.h"
0030 #include "FastSimulation/SimplifiedGeometryPropagator/interface/Decayer.h"
0031 #include "FastSimulation/SimplifiedGeometryPropagator/interface/LayerNavigator.h"
0032 #include "FastSimulation/SimplifiedGeometryPropagator/interface/Particle.h"
0033 #include "FastSimulation/SimplifiedGeometryPropagator/interface/ParticleFilter.h"
0034 #include "FastSimulation/SimplifiedGeometryPropagator/interface/InteractionModel.h"
0035 #include "FastSimulation/SimplifiedGeometryPropagator/interface/InteractionModelFactory.h"
0036 #include "FastSimulation/SimplifiedGeometryPropagator/interface/ParticleManager.h"
0037 #include "FastSimulation/Particle/interface/makeParticle.h"
0038 
0039 // Hack for calorimetry
0040 #include "FastSimulation/Event/interface/FSimTrack.h"
0041 #include "FastSimulation/Calorimetry/interface/CalorimetryManager.h"
0042 #include "FastSimulation/CaloGeometryTools/interface/CaloGeometryHelper.h"
0043 #include "Geometry/Records/interface/CaloGeometryRecord.h"
0044 #include "Geometry/CaloGeometry/interface/CaloGeometry.h"
0045 #include "Geometry/Records/interface/CaloTopologyRecord.h"
0046 #include "FastSimulation/ShowerDevelopment/interface/FastHFShowerLibrary.h"
0047 
0048 ///////////////////////////////////////////////
0049 // Author: L. Vanelderen, S. Kurz
0050 // Date: 29 May 2017
0051 //////////////////////////////////////////////////////////
0052 
0053 //! The core class of the new SimplifiedGeometryPropagator.
0054 /*!
0055     Coordinates the propagation of all particles, this means it does the following loop:
0056     1) Get particle from ParticleManager
0057     2) Call LayerNavigator to move particle to next intersection with layer
0058     3) Loop over all the interactions and add secondaries to the event
0059     4) Repeat steps 2), 3) until particle left the tracker, lost all its energy or is about to decay
0060     5) If particle is about to decay: do decay and add secondaries to the event
0061     6) Restart from 1) with the next particle
0062     7) If last particle was propagated add SimTracks, SimVertices, SimHits,... to the event
0063 */
0064 class FastSimProducer : public edm::stream::EDProducer<> {
0065 public:
0066   explicit FastSimProducer(const edm::ParameterSet&);
0067   ~FastSimProducer() override { ; }
0068 
0069 private:
0070   void beginStream(edm::StreamID id) override;
0071   void produce(edm::Event&, const edm::EventSetup&) override;
0072   void endStream() override;
0073   virtual FSimTrack createFSimTrack(fastsim::Particle* particle,
0074                                     fastsim::ParticleManager* particleManager,
0075                                     HepPDT::ParticleDataTable const& particleTable);
0076 
0077   edm::EDGetTokenT<edm::HepMCProduct> genParticlesToken_;  //!< Token to get the genParticles
0078   fastsim::Geometry geometry_;                             //!< The definition of the tracker according to python config
0079   fastsim::Geometry caloGeometry_;                         //!< Hack to interface "old" calo to "new" tracking
0080   double beamPipeRadius_;                                  //!< The radius of the beampipe
0081   double deltaRchargedMother_;              //!< Cut on deltaR for ClosestChargedDaughter algorithm (FastSim tracking)
0082   fastsim::ParticleFilter particleFilter_;  //!< Decides which particles have to be propagated
0083   std::unique_ptr<RandomEngineAndDistribution> _randomEngine;  //!< The random engine
0084 
0085   bool simulateCalorimetry;
0086   edm::ESWatcher<CaloGeometryRecord> watchCaloGeometry_;
0087   edm::ESWatcher<CaloTopologyRecord> watchCaloTopology_;
0088   std::unique_ptr<CalorimetryManager> myCalorimetry;  // unfortunately, default constructor cannot be called
0089   bool simulateMuons;
0090 
0091   fastsim::Decayer decayer_;  //!< Handles decays of non-stable particles using pythia
0092   std::vector<std::unique_ptr<fastsim::InteractionModel> > interactionModels_;  //!< All defined interaction models
0093   std::map<std::string, fastsim::InteractionModel*> interactionModelMap_;  //!< Each interaction model has a unique name
0094   static const std::string MESSAGECATEGORY;  //!< Category of debugging messages ("FastSimulation")
0095   const edm::ESGetToken<HepPDT::ParticleDataTable, edm::DefaultRecord> particleDataTableESToken_;
0096   edm::ESGetToken<CaloGeometry, CaloGeometryRecord> caloGeometryESToken_;
0097   edm::ESGetToken<CaloTopology, CaloTopologyRecord> caloTopologyESToken_;
0098 };
0099 
0100 const std::string FastSimProducer::MESSAGECATEGORY = "FastSimulation";
0101 
0102 FastSimProducer::FastSimProducer(const edm::ParameterSet& iConfig)
0103     : genParticlesToken_(consumes<edm::HepMCProduct>(iConfig.getParameter<edm::InputTag>("src"))),
0104       geometry_(iConfig.getParameter<edm::ParameterSet>("trackerDefinition"), consumesCollector()),
0105       caloGeometry_(iConfig.getParameter<edm::ParameterSet>("caloDefinition"), consumesCollector()),
0106       beamPipeRadius_(iConfig.getParameter<double>("beamPipeRadius")),
0107       deltaRchargedMother_(iConfig.getParameter<double>("deltaRchargedMother")),
0108       particleFilter_(iConfig.getParameter<edm::ParameterSet>("particleFilter")),
0109       _randomEngine(nullptr),
0110       simulateCalorimetry(iConfig.getParameter<bool>("simulateCalorimetry")),
0111       simulateMuons(iConfig.getParameter<bool>("simulateMuons")),
0112       particleDataTableESToken_(esConsumes()) {
0113   if (simulateCalorimetry) {
0114     caloGeometryESToken_ = esConsumes();
0115     caloTopologyESToken_ = esConsumes();
0116   }
0117 
0118   //----------------
0119   // define interaction models
0120   //---------------
0121 
0122   const edm::ParameterSet& modelCfgs = iConfig.getParameter<edm::ParameterSet>("interactionModels");
0123   for (const std::string& modelName : modelCfgs.getParameterNames()) {
0124     const edm::ParameterSet& modelCfg = modelCfgs.getParameter<edm::ParameterSet>(modelName);
0125     std::string modelClassName(modelCfg.getParameter<std::string>("className"));
0126     // Use plugin-factory to create model
0127     std::unique_ptr<fastsim::InteractionModel> interactionModel(
0128         fastsim::InteractionModelFactory::get()->create(modelClassName, modelName, modelCfg));
0129     if (!interactionModel.get()) {
0130       throw cms::Exception("FastSimProducer") << "InteractionModel " << modelName << " could not be created";
0131     }
0132     // Add model to list
0133     interactionModels_.push_back(std::move(interactionModel));
0134     // and create the map
0135     interactionModelMap_[modelName] = interactionModels_.back().get();
0136   }
0137 
0138   //----------------
0139   // calorimetry
0140   //---------------
0141 
0142   if (simulateCalorimetry) {
0143     myCalorimetry =
0144         std::make_unique<CalorimetryManager>(nullptr,
0145                                              iConfig.getParameter<edm::ParameterSet>("Calorimetry"),
0146                                              iConfig.getParameter<edm::ParameterSet>("MaterialEffectsForMuonsInECAL"),
0147                                              iConfig.getParameter<edm::ParameterSet>("MaterialEffectsForMuonsInHCAL"),
0148                                              iConfig.getParameter<edm::ParameterSet>("GFlash"),
0149                                              consumesCollector());
0150   }
0151 
0152   //----------------
0153   // register products
0154   //----------------
0155 
0156   // SimTracks and SimVertices
0157   produces<edm::SimTrackContainer>();
0158   produces<edm::SimVertexContainer>();
0159   // products of interaction models, i.e. simHits
0160   for (auto& interactionModel : interactionModels_) {
0161     interactionModel->registerProducts(producesCollector());
0162   }
0163   produces<edm::PCaloHitContainer>("EcalHitsEB");
0164   produces<edm::PCaloHitContainer>("EcalHitsEE");
0165   produces<edm::PCaloHitContainer>("EcalHitsES");
0166   produces<edm::PCaloHitContainer>("HcalHits");
0167   produces<edm::SimTrackContainer>("MuonSimTracks");
0168 }
0169 
0170 void FastSimProducer::beginStream(const edm::StreamID id) {
0171   _randomEngine = std::make_unique<RandomEngineAndDistribution>(id);
0172 }
0173 
0174 void FastSimProducer::produce(edm::Event& iEvent, const edm::EventSetup& iSetup) {
0175   LogDebug(MESSAGECATEGORY) << "   produce";
0176 
0177   geometry_.update(iSetup, interactionModelMap_);
0178   caloGeometry_.update(iSetup, interactionModelMap_);
0179 
0180   // Define containers for SimTracks, SimVertices
0181   std::unique_ptr<edm::SimTrackContainer> simTracks_(new edm::SimTrackContainer);
0182   std::unique_ptr<edm::SimVertexContainer> simVertices_(new edm::SimVertexContainer);
0183 
0184   // Get the particle data table (in case lifetime or charge of GenParticles not set)
0185   auto const& pdt = iSetup.getData(particleDataTableESToken_);
0186 
0187   // Get the GenParticle collection
0188   edm::Handle<edm::HepMCProduct> genParticles;
0189   iEvent.getByToken(genParticlesToken_, genParticles);
0190 
0191   // Load the ParticleManager which returns the particles that have to be propagated
0192   // Creates a fastsim::Particle out of a GenParticle/secondary
0193   fastsim::ParticleManager particleManager(*genParticles->GetEvent(),
0194                                            pdt,
0195                                            beamPipeRadius_,
0196                                            deltaRchargedMother_,
0197                                            particleFilter_,
0198                                            *simTracks_,
0199                                            *simVertices_);
0200 
0201   //  Initialize the calorimeter geometry
0202   if (simulateCalorimetry) {
0203     if (watchCaloGeometry_.check(iSetup) || watchCaloTopology_.check(iSetup)) {
0204       auto const& pG = iSetup.getData(caloGeometryESToken_);
0205       myCalorimetry->getCalorimeter()->setupGeometry(pG);
0206 
0207       auto const& theCaloTopology = iSetup.getData(caloTopologyESToken_);
0208       myCalorimetry->getCalorimeter()->setupTopology(theCaloTopology);
0209       myCalorimetry->getCalorimeter()->initialize(geometry_.getMagneticFieldZ(math::XYZTLorentzVector(0., 0., 0., 0.)));
0210 
0211       myCalorimetry->getHFShowerLibrary()->initHFShowerLibrary(iSetup);
0212     }
0213 
0214     // Important: this also cleans the calorimetry information from the last event
0215     myCalorimetry->initialize(_randomEngine.get());
0216   }
0217 
0218   // The vector of SimTracks needed for the CalorimetryManager
0219   std::vector<FSimTrack> myFSimTracks;
0220 
0221   LogDebug(MESSAGECATEGORY) << "################################"
0222                             << "\n###############################";
0223 
0224   // loop over particles
0225   for (std::unique_ptr<fastsim::Particle> particle = particleManager.nextParticle(*_randomEngine); particle != nullptr;
0226        particle = particleManager.nextParticle(*_randomEngine)) {
0227     LogDebug(MESSAGECATEGORY) << "\n   moving NEXT particle: " << *particle;
0228 
0229     // -----------------------------
0230     // This condition is necessary because of hack for calorimetry
0231     // -> The CalorimetryManager should also be implemented based on this new FastSim classes (Particle.h) in a future project.
0232     // A second loop (below) loops over all parts of the calorimetry in order to create a track of the old FastSim class FSimTrack.
0233     // The condition below (R<128, z<302) makes sure that the particle geometrically is outside the tracker boundaries
0234     // -----------------------------
0235 
0236     if (particle->position().Perp2() < 128. * 128. && std::abs(particle->position().Z()) < 302.) {
0237       // move the particle through the layers
0238       fastsim::LayerNavigator layerNavigator(geometry_);
0239       const fastsim::SimplifiedGeometry* layer = nullptr;
0240 
0241       // moveParticleToNextLayer(..) returns 0 in case that particle decays
0242       // in this case particle is propagated up to its decay vertex
0243       while (layerNavigator.moveParticleToNextLayer(*particle, layer)) {
0244         LogDebug(MESSAGECATEGORY) << "   moved to next layer: " << *layer;
0245         LogDebug(MESSAGECATEGORY) << "   new state: " << *particle;
0246 
0247         // Hack to interface "old" calo to "new" tracking
0248         // Particle reached calorimetry so stop further propagation
0249         if (layer->getCaloType() == fastsim::SimplifiedGeometry::TRACKERBOUNDARY) {
0250           layer = nullptr;
0251           // particle no longer is on a layer
0252           particle->resetOnLayer();
0253           break;
0254         }
0255 
0256         // break after 25 ns: only happens for particles stuck in loops
0257         if (particle->position().T() > 25) {
0258           layer = nullptr;
0259           // particle no longer is on a layer
0260           particle->resetOnLayer();
0261           break;
0262         }
0263 
0264         // perform interaction between layer and particle
0265         // do only if there is actual material
0266         if (layer->getThickness(particle->position(), particle->momentum()) > 1E-10) {
0267           int nSecondaries = 0;
0268           // loop on interaction models
0269           for (fastsim::InteractionModel* interactionModel : layer->getInteractionModels()) {
0270             LogDebug(MESSAGECATEGORY) << "   interact with " << *interactionModel;
0271             std::vector<std::unique_ptr<fastsim::Particle> > secondaries;
0272             interactionModel->interact(*particle, *layer, secondaries, *_randomEngine);
0273             nSecondaries += secondaries.size();
0274             particleManager.addSecondaries(particle->position(), particle->simTrackIndex(), secondaries, layer);
0275           }
0276 
0277           // kinematic cuts: particle might e.g. lost all its energy
0278           if (!particleFilter_.acceptsEn(*particle)) {
0279             // Add endvertex if particle did not create any secondaries
0280             if (nSecondaries == 0)
0281               particleManager.addEndVertex(particle.get());
0282             layer = nullptr;
0283             break;
0284           }
0285         }
0286 
0287         LogDebug(MESSAGECATEGORY) << "--------------------------------"
0288                                   << "\n-------------------------------";
0289       }
0290 
0291       // do decays
0292       if (!particle->isStable() && particle->remainingProperLifeTimeC() < 1E-10) {
0293         LogDebug(MESSAGECATEGORY) << "Decaying particle...";
0294         std::vector<std::unique_ptr<fastsim::Particle> > secondaries;
0295         decayer_.decay(*particle, secondaries, _randomEngine->theEngine());
0296         LogDebug(MESSAGECATEGORY) << "   decay has " << secondaries.size() << " products";
0297         particleManager.addSecondaries(particle->position(), particle->simTrackIndex(), secondaries);
0298         continue;
0299       }
0300 
0301       LogDebug(MESSAGECATEGORY) << "################################"
0302                                 << "\n###############################";
0303     }
0304 
0305     // -----------------------------
0306     // Hack to interface "old" calorimetry with "new" propagation in tracker
0307     // The CalorimetryManager has to know which particle could in principle hit which parts of the calorimeter
0308     // I think it's a bit strange to propagate the particle even further (and even decay it) if it already hits
0309     // some part of the calorimetry but this is how the code works...
0310     // -----------------------------
0311 
0312     if (particle->position().Perp2() >= 128. * 128. || std::abs(particle->position().Z()) >= 302.) {
0313       LogDebug(MESSAGECATEGORY) << "\n   moving particle to calorimetry: " << *particle;
0314 
0315       // create FSimTrack (this is the object the old propagation uses)
0316       myFSimTracks.push_back(createFSimTrack(particle.get(), &particleManager, pdt));
0317       // particle was decayed
0318       if (!particle->isStable() && particle->remainingProperLifeTimeC() < 1E-10) {
0319         continue;
0320       }
0321 
0322       LogDebug(MESSAGECATEGORY) << "################################"
0323                                 << "\n###############################";
0324     }
0325 
0326     // -----------------------------
0327     // End Hack
0328     // -----------------------------
0329 
0330     LogDebug(MESSAGECATEGORY) << "################################"
0331                               << "\n###############################";
0332   }
0333 
0334   // store simTracks and simVertices
0335   iEvent.put(std::move(simTracks_));
0336   iEvent.put(std::move(simVertices_));
0337   // store products of interaction models, i.e. simHits
0338   for (auto& interactionModel : interactionModels_) {
0339     interactionModel->storeProducts(iEvent);
0340   }
0341 
0342   // -----------------------------
0343   // Calorimetry Manager
0344   // -----------------------------
0345   if (simulateCalorimetry) {
0346     for (auto myFSimTrack : myFSimTracks) {
0347       myCalorimetry->reconstructTrack(myFSimTrack, _randomEngine.get());
0348     }
0349   }
0350 
0351   // -----------------------------
0352   // Store Hits
0353   // -----------------------------
0354   std::unique_ptr<edm::PCaloHitContainer> p4(new edm::PCaloHitContainer);
0355   std::unique_ptr<edm::PCaloHitContainer> p5(new edm::PCaloHitContainer);
0356   std::unique_ptr<edm::PCaloHitContainer> p6(new edm::PCaloHitContainer);
0357   std::unique_ptr<edm::PCaloHitContainer> p7(new edm::PCaloHitContainer);
0358 
0359   std::unique_ptr<edm::SimTrackContainer> m1(new edm::SimTrackContainer);
0360 
0361   if (simulateCalorimetry) {
0362     myCalorimetry->loadFromEcalBarrel(*p4);
0363     myCalorimetry->loadFromEcalEndcap(*p5);
0364     myCalorimetry->loadFromPreshower(*p6);
0365     myCalorimetry->loadFromHcal(*p7);
0366     if (simulateMuons) {
0367       myCalorimetry->harvestMuonSimTracks(*m1);
0368     }
0369   }
0370   iEvent.put(std::move(p4), "EcalHitsEB");
0371   iEvent.put(std::move(p5), "EcalHitsEE");
0372   iEvent.put(std::move(p6), "EcalHitsES");
0373   iEvent.put(std::move(p7), "HcalHits");
0374   iEvent.put(std::move(m1), "MuonSimTracks");
0375 }
0376 
0377 void FastSimProducer::endStream() { _randomEngine.reset(); }
0378 
0379 FSimTrack FastSimProducer::createFSimTrack(fastsim::Particle* particle,
0380                                            fastsim::ParticleManager* particleManager,
0381                                            HepPDT::ParticleDataTable const& particleTable) {
0382   FSimTrack myFSimTrack(particle->pdgId(),
0383                         particleManager->getSimTrack(particle->simTrackIndex()).momentum(),
0384                         particle->simVertexIndex(),
0385                         particle->genParticleIndex(),
0386                         particle->simTrackIndex(),
0387                         particle->charge(),
0388                         particle->position(),
0389                         particle->momentum(),
0390                         particleManager->getSimVertex(particle->simVertexIndex()));
0391 
0392   // move the particle through the caloLayers
0393   fastsim::LayerNavigator caloLayerNavigator(caloGeometry_);
0394   const fastsim::SimplifiedGeometry* caloLayer = nullptr;
0395 
0396   // moveParticleToNextLayer(..) returns 0 in case that particle decays
0397   // in this case particle is propagated up to its decay vertex
0398   while (caloLayerNavigator.moveParticleToNextLayer(*particle, caloLayer)) {
0399     LogDebug(MESSAGECATEGORY) << "   moved to next caloLayer: " << *caloLayer;
0400     LogDebug(MESSAGECATEGORY) << "   new state: " << *particle;
0401 
0402     // break after 25 ns: only happens for particles stuck in loops
0403     if (particle->position().T() > 50) {
0404       caloLayer = nullptr;
0405       break;
0406     }
0407 
0408     //////////
0409     // Define ParticlePropagators (RawParticle) needed for CalorimetryManager and save them
0410     //////////
0411 
0412     RawParticle PP = makeParticle(&particleTable, particle->pdgId(), particle->momentum(), particle->position());
0413 
0414     // no material
0415     if (caloLayer->getThickness(particle->position(), particle->momentum()) < 1E-10) {
0416       // unfortunately needed for CalorimetryManager
0417       if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::ECAL) {
0418         if (!myFSimTrack.onEcal()) {
0419           myFSimTrack.setEcal(PP, 0);
0420         }
0421       } else if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::HCAL) {
0422         if (!myFSimTrack.onHcal()) {
0423           myFSimTrack.setHcal(PP, 0);
0424         }
0425       } else if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::VFCAL) {
0426         if (!myFSimTrack.onVFcal()) {
0427           myFSimTrack.setVFcal(PP, 0);
0428         }
0429       }
0430 
0431       // not necessary to continue propagation
0432       if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::VFCAL) {
0433         myFSimTrack.setGlobal();
0434         caloLayer = nullptr;
0435         break;
0436       }
0437 
0438       continue;
0439     }
0440 
0441     // Stupid variable used by the old propagator
0442     // For details check BaseParticlePropagator.h
0443     int success = 0;
0444     if (caloLayer->isForward()) {
0445       success = 2;
0446       // particle moves inwards
0447       if (particle->position().Z() * particle->momentum().Z() < 0) {
0448         success *= -1;
0449       }
0450     } else {
0451       success = 1;
0452       // particle moves inwards
0453       if (particle->momentum().X() * particle->position().X() + particle->momentum().Y() * particle->position().Y() <
0454           0) {
0455         success *= -1;
0456       }
0457     }
0458 
0459     // Save the hit
0460     if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::PRESHOWER1) {
0461       if (!myFSimTrack.onLayer1()) {
0462         myFSimTrack.setLayer1(PP, success);
0463       }
0464     }
0465 
0466     if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::PRESHOWER2) {
0467       if (!myFSimTrack.onLayer2()) {
0468         myFSimTrack.setLayer2(PP, success);
0469       }
0470     }
0471 
0472     if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::ECAL) {
0473       if (!myFSimTrack.onEcal()) {
0474         myFSimTrack.setEcal(PP, success);
0475       }
0476     }
0477 
0478     if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::HCAL) {
0479       if (!myFSimTrack.onHcal()) {
0480         myFSimTrack.setHcal(PP, success);
0481       }
0482     }
0483 
0484     if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::VFCAL) {
0485       if (!myFSimTrack.onVFcal()) {
0486         myFSimTrack.setVFcal(PP, success);
0487       }
0488     }
0489 
0490     // Particle reached end of detector
0491     if (caloLayer->getCaloType() == fastsim::SimplifiedGeometry::VFCAL) {
0492       myFSimTrack.setGlobal();
0493       caloLayer = nullptr;
0494       break;
0495     }
0496 
0497     LogDebug(MESSAGECATEGORY) << "--------------------------------"
0498                               << "\n-------------------------------";
0499   }
0500 
0501   // do decays
0502   // don't have to worry about daughters if particle already within the calorimetry
0503   // since they will be rejected by the vertex cut of the ParticleFilter
0504   if (!particle->isStable() && particle->remainingProperLifeTimeC() < 1E-10) {
0505     LogDebug(MESSAGECATEGORY) << "Decaying particle...";
0506     std::vector<std::unique_ptr<fastsim::Particle> > secondaries;
0507     decayer_.decay(*particle, secondaries, _randomEngine->theEngine());
0508     LogDebug(MESSAGECATEGORY) << "   decay has " << secondaries.size() << " products";
0509     particleManager->addSecondaries(particle->position(), particle->simTrackIndex(), secondaries);
0510   }
0511 
0512   return myFSimTrack;
0513 }
0514 
0515 DEFINE_FWK_MODULE(FastSimProducer);