Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:11:20

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