Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-06 08:15:40

0001 //==========================================================================
0002 //  AIDA Detector description implementation
0003 //--------------------------------------------------------------------------
0004 // Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
0005 // All rights reserved.
0006 //
0007 // For the licensing terms see $DD4hepINSTALL/LICENSE.
0008 // For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
0009 //
0010 // Author     : M.Frank
0011 //
0012 //==========================================================================
0013 
0014 // Framework include files
0015 #include <DDG4/Geant4SensDetAction.inl>
0016 #include <DDG4/Geant4FastSimHandler.h>
0017 #include <DDG4/Geant4EventAction.h>
0018 #include <G4OpticalPhoton.hh>
0019 #include <G4VProcess.hh>
0020 
0021 
0022 /// Namespace for the AIDA detector description toolkit
0023 namespace dd4hep {
0024   
0025   /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
0026   namespace sim   {
0027 
0028     namespace {
0029       struct Geant4VoidSensitive {};
0030 
0031       /// Common code to handle the creation of a calorimeter hit.
0032       template <class HANDLER>
0033       void handleCalorimeterHit (VolumeID cell,
0034                                  const HitContribution& contrib,
0035                                  Geant4HitCollection& coll,
0036                                  const HANDLER& h,
0037                                  const Geant4Sensitive& sd,
0038                                  const Segmentation& segmentation)
0039       {
0040         typedef Geant4Calorimeter::Hit Hit;
0041         Hit* hit = coll.findByKey<Hit>(cell);
0042         if( !hit ) {
0043           DDSegmentation::Vector3D pos;
0044           Position global;
0045           /// No volume manager
0046           if( !sd.useVolumeManager() || !segmentation.isValid() )  {
0047             pos    = h.avgPosition();
0048             global = h.localToGlobal(pos);
0049           }
0050           else if( !segmentation.cellsSpanVolumes() ) {
0051             // Convert the position relative to the local readout volume
0052             // to a global position.
0053             pos = segmentation.position(cell);
0054             global = h.localToGlobal(pos);
0055           }
0056           else {
0057             // The segmentation can gang together multiple volumes.
0058             // In this case, we can't use the transformation we get from
0059             // the step --- the volume that actually contains the hit
0060             // may not be the same volume that the segmentation uses
0061             // for the local coordinate system.  We need to get the
0062             // actual volID used from the segmentation and then look
0063             // it up the volume manager to get the proper transformation.
0064             VolumeID volID = segmentation.volumeID(cell);
0065             VolumeManager vman_glob = VolumeManager::getVolumeManager(sd.detectorDescription());
0066             VolumeManager vman = vman_glob.subdetector(sd.id());
0067             VolumeManagerContext* vc = vman.lookupContext(volID);
0068             // explicit unit conversion; h.localToGlobal does it internally already
0069             pos = segmentation.position(cell);
0070             global = vc->localToWorld(Position(pos)) / dd4hep::mm;
0071           }
0072           hit = new Hit(global);
0073           hit->cellID = cell;
0074           coll.add(cell, hit);
0075           Geant4TouchableHandler handler(h.touchable());
0076           sd.printM2("%s> CREATE hit with deposit:%e MeV  Pos:%8.2f %8.2f %8.2f  %s  [%s]",
0077                      sd.c_name(),contrib.deposit,pos.X,pos.Y,pos.Z,handler.path().c_str(),
0078                      coll.GetName().c_str());
0079           if ( 0 == hit->cellID )  { // for debugging only!
0080             hit->cellID = cell;
0081             sd.except("+++ Invalid CELL ID for hit!");
0082           }
0083         }
0084         hit->truth.emplace_back(contrib);
0085         hit->energyDeposit += contrib.deposit;
0086       }
0087     }
0088 
0089     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0090     //               Geant4SensitiveAction<Geant4VoidSensitive>
0091     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0092     /** \addtogroup Geant4SDActionPlugin
0093      *
0094      * @{
0095      * \package Geant4VoidSensitiveAction
0096      * \brief Void Sensitive detector action to skip the processing of a detector
0097      *        without changing the entire DDG4 setup.
0098      *
0099      * @}
0100      */
0101 
0102     /// Define collections created by this sensitivie action object
0103     template <> void Geant4SensitiveAction<Geant4VoidSensitive>::defineCollections()    {
0104       m_collectionID = -1;
0105     }
0106 
0107     /// G4VSensitiveDetector interface: Method for generating hit(s) using the information of G4Step object.
0108     template <> bool
0109     Geant4SensitiveAction<Geant4VoidSensitive>::process(const G4Step*       /* step */,
0110                                                         G4TouchableHistory* /* hist */) {
0111       return true;
0112     }
0113     
0114     /// GFLASH/FastSim interface: Method for generating hit(s) using the information of G4Step object.
0115     template <> bool
0116     Geant4SensitiveAction<Geant4VoidSensitive>::processFastSim(const Geant4FastSimSpot* /* spot */,
0117                                                                G4TouchableHistory*      /* hist */) {
0118       return true;
0119     }
0120     typedef Geant4SensitiveAction<Geant4VoidSensitive> Geant4VoidSensitiveAction;
0121 
0122     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0123     //               Geant4SensitiveAction<Geant4Tracker>
0124     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0125     /** \addtogroup Geant4SDActionPlugin
0126      *
0127      * @{
0128      * \package Geant4TrackerAction
0129      * \brief Sensitive detector meant for tracking detectors, will produce one hit per step
0130      *
0131      * @}
0132      */
0133 
0134     /// Define collections created by this sensitivie action object
0135     template <> void Geant4SensitiveAction<Geant4Tracker>::defineCollections()    {
0136       m_collectionID = declareReadoutFilteredCollection<Geant4Tracker::Hit>();
0137     }
0138 
0139     /// Method for generating hit(s) using the information of G4Step object.
0140     template <> bool
0141     Geant4SensitiveAction<Geant4Tracker>::process(const G4Step* step,G4TouchableHistory* /* hist */) {
0142       typedef Geant4Tracker::Hit Hit;
0143       // Note: 1) We store in the hit the hit-direction, which is not the same as the track direction.
0144       //       2) The energy deposit is the difference between incoming and outcoming energy.
0145       Geant4StepHandler h(step);
0146       auto contrib = Hit::extractContribution(step);
0147       Direction hit_momentum = 0.5 * (h.preMom() + h.postMom());
0148       double    hit_deposit  = h.deposit();
0149       Hit* hit = new Hit(contrib, hit_momentum, hit_deposit);
0150 
0151       hit->cellID = cellID(step);
0152       if ( 0 == hit->cellID )  {
0153         hit->cellID = volumeID(step);
0154         except("+++ Invalid CELL ID for hit!");
0155       }
0156       collection(m_collectionID)->add(hit);
0157       mark(h.track);
0158       print("Hit with deposit:%f  Pos:%f %f %f ID=%016X",
0159             hit->energyDeposit,hit->position.X(),hit->position.Y(),hit->position.Z(),(void*)hit->cellID);
0160       Geant4TouchableHandler handler(step);
0161       print("    Geant4 path:%s",handler.path().c_str());
0162       return true;
0163     }
0164     
0165     /// GFlash/FastSim interface: Method for generating hit(s) using the information of Geant4FastSimSpot object.
0166     template <> bool
0167     Geant4SensitiveAction<Geant4Tracker>::processFastSim(const Geant4FastSimSpot* spot,
0168                                                          G4TouchableHistory* /* hist */)
0169     {
0170       typedef Geant4Tracker::Hit Hit;
0171       Geant4FastSimHandler h(spot);
0172       auto contrib = Hit::extractContribution(spot);
0173       Hit* hit = new Hit(contrib, h.momentum(), h.deposit());
0174 
0175       hit->cellID  = cellID(h.touchable(), h.avgPositionG4());
0176       if ( 0 == hit->cellID )  {
0177         hit->cellID = volumeID(h.touchable());
0178         except("+++ Invalid CELL ID for hit!");
0179       }
0180       collection(m_collectionID)->add(hit);
0181       mark(h.track);
0182       print("Hit with deposit:%f  Pos:%f %f %f ID=%016X",
0183             hit->energyDeposit,hit->position.X(),hit->position.Y(),hit->position.Z(),(void*)hit->cellID);
0184       Geant4TouchableHandler handler(h.touchable());
0185       print("    Geant4 path:%s",handler.path().c_str());
0186       return true;
0187     }
0188 
0189     typedef Geant4SensitiveAction<Geant4Tracker> Geant4TrackerAction;
0190 
0191     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0192     //               Geant4SensitiveAction<Geant4PhotonCounter>
0193     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0194     /** \addtogroup Geant4SDActionPlugin
0195      *
0196      * @{
0197      * \package Geant4OpticalTrackerAction
0198      * \brief Sensitive detector meant for photon detectors, will produce one hit per step
0199      *        for regular particles, but absorb optical photons fully on their first hit
0200      *
0201      * @}
0202      */
0203 
0204     /// Helper class to define properties of optical trackers. UNTESTED
0205     struct Geant4OpticalTracker {};
0206 
0207     /// Define collections created by this sensitivie action object
0208     template <> void Geant4SensitiveAction<Geant4OpticalTracker>::defineCollections()    {
0209       m_collectionID = declareReadoutFilteredCollection<Geant4Tracker::Hit>();
0210     }
0211 
0212     /// Method for generating hit(s) using the information of G4Step object.
0213     template <> bool
0214     Geant4SensitiveAction<Geant4OpticalTracker>::process(const G4Step* step,G4TouchableHistory* /* hist */) {
0215       // Note: 1) We store in the hit the hit-direction, which is not the same as the track direction.
0216       //       2) The energy deposit is the track momentum
0217       typedef Geant4Tracker::Hit Hit;
0218       Geant4StepHandler h(step);
0219       auto      contrib = Hit::extractContribution(step);
0220       Direction hit_momentum = 0.5 * (h.preMom() + h.postMom());
0221       double    hit_deposit  = contrib.deposit;
0222       Hit* hit = new Hit(contrib, hit_momentum, hit_deposit);
0223 
0224       if (h.trackDef() == G4OpticalPhoton::OpticalPhotonDefinition()) {
0225         step->GetTrack()->SetTrackStatus(fStopAndKill);
0226       }
0227       hit->cellID = cellID(step);
0228       if ( 0 == hit->cellID )  {
0229         hit->cellID = volumeID( step ) ;
0230         except("+++ Invalid CELL ID for hit!");
0231       }
0232       collection(m_collectionID)->add(hit);
0233       mark(h.track);
0234       print("Hit with deposit:%f  Pos:%f %f %f ID=%016X",
0235             hit->energyDeposit,hit->position.X(),hit->position.Y(),hit->position.Z(),(void*)hit->cellID);
0236       Geant4TouchableHandler handler(step);
0237       print("    Geant4 path:%s",handler.path().c_str());
0238       return true;
0239     }
0240 
0241     /// GFlash/FastSim interface: Method for generating hit(s) using the information of G4Step object.
0242     template <> bool
0243     Geant4SensitiveAction<Geant4OpticalTracker>::processFastSim(const Geant4FastSimSpot* spot,
0244                                                                 G4TouchableHistory* /* hist */)
0245     {
0246       typedef Geant4Tracker::Hit Hit;
0247       Geant4FastSimHandler h(spot);
0248       auto contrib = Hit::extractContribution(spot);
0249       Hit* hit = new Hit(contrib, h.momentum(), contrib.deposit);
0250 
0251       hit->cellID  = cellID(h.touchable(), h.avgPositionG4());
0252       if ( 0 == hit->cellID )  {
0253         hit->cellID      = volumeID(h.touchable());
0254         except("+++ Invalid CELL ID for hit!");
0255       }
0256       collection(m_collectionID)->add(hit);
0257       mark(h.track);
0258       print("Hit with deposit:%f  Pos:%f %f %f ID=%016X",
0259             hit->energyDeposit,hit->position.X(),hit->position.Y(),hit->position.Z(),(void*)hit->cellID);
0260       Geant4TouchableHandler handler(h.touchable());
0261       print("    Geant4 path:%s",handler.path().c_str());
0262       return true;
0263     }
0264     typedef Geant4SensitiveAction<Geant4OpticalTracker> Geant4OpticalTrackerAction;
0265 
0266     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0267     //               Geant4SensitiveAction<Calorimeter>
0268     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0269     /** \addtogroup Geant4SDActionPlugin
0270      *
0271      * @{
0272      * \package Geant4CalorimeterAction
0273      *
0274      * \brief Sensitive detector meant for calorimeters
0275 
0276      *
0277      * @}
0278      */
0279     /// Define collections created by this sensitivie action object
0280     template <> void Geant4SensitiveAction<Geant4Calorimeter>::defineCollections() {
0281       m_collectionID = declareReadoutFilteredCollection<Geant4Calorimeter::Hit>();
0282     }
0283 
0284     /// Method for generating hit(s) using the information of G4Step object.
0285     template <> bool
0286     Geant4SensitiveAction<Geant4Calorimeter>::process(const G4Step* step,G4TouchableHistory*) {
0287       typedef Geant4Calorimeter::Hit Hit;
0288       Geant4StepHandler    h(step);
0289       HitContribution      contrib = Hit::extractContribution(step);
0290       Geant4HitCollection* coll    = collection(m_collectionID);
0291       VolumeID cell = 0;
0292 
0293       try {
0294         cell = cellID(step);
0295       } catch(std::runtime_error &e) {
0296         std::stringstream out;
0297         out << std::setprecision(20) << std::scientific;
0298         out << "ERROR: " << e.what()  << std::endl;
0299         out << "Position: "
0300             << "Pre (" << std::setw(24) << step->GetPreStepPoint()->GetPosition() << ") "
0301             << "Post (" << std::setw(24) << step->GetPostStepPoint()->GetPosition() << ") "
0302             << std::endl;
0303         out << "Momentum: "
0304             << " Pre (" <<std::setw(24) << step->GetPreStepPoint() ->GetMomentum()  << ") "
0305             << " Post (" <<std::setw(24) << step->GetPostStepPoint()->GetMomentum() << ") "
0306             << std::endl;
0307         std::cout << out.str();
0308         return true;
0309       }
0310 
0311       handleCalorimeterHit(cell, contrib, *coll, h, *this, m_segmentation);
0312       mark(h.track);
0313       return true;
0314     }
0315     /// GFlash/FastSim interface: Method for generating hit(s) using the information of Geant4FastSimSpot object.
0316     template <> bool
0317     Geant4SensitiveAction<Geant4Calorimeter>::processFastSim(const Geant4FastSimSpot* spot,
0318                                                              G4TouchableHistory* /* hist */)
0319     {
0320       typedef Geant4Calorimeter::Hit Hit;
0321       Geant4FastSimHandler h(spot);
0322       HitContribution      contrib = Hit::extractContribution(spot);
0323       Geant4HitCollection* coll    = collection(m_collectionID);
0324       VolumeID cell = 0;
0325       
0326       try {
0327         cell = cellID(h.touchable(), h.avgPositionG4());
0328       } catch(std::runtime_error &e) {
0329         std::stringstream out;
0330         out << std::setprecision(20) << std::scientific;
0331         out << "ERROR: " << e.what()  << std::endl;
0332         out << "Position: (" << std::setw(24) << h.avgPositionG4() << ") " << std::endl;
0333         out << "Momentum: (" << std::setw(24) << h.momentumG4() << ") " << std::endl;
0334         std::cout << out.str();
0335         return true;
0336       }
0337       handleCalorimeterHit(cell, contrib, *coll, h, *this, m_segmentation);
0338       mark(h.track);
0339       return true;
0340     }
0341 
0342     typedef Geant4SensitiveAction<Geant4Calorimeter> Geant4CalorimeterAction;
0343 
0344     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0345     //               Geant4SensitiveAction<OpticalCalorimeter>
0346     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0347 
0348     /**
0349      *  \author  M.Frank
0350      *  \version 1.0
0351      *  \addtogroup Geant4SDActionPlugin
0352      *
0353      * @{
0354      * \package Geant4OpticalCalorimeterAction
0355      *
0356      * \brief Sensitive detector meant for optical calorimeters
0357      *
0358      * @}
0359      */
0360     /// Helper class to define properties of optical calorimeters. UNTESTED
0361     struct Geant4OpticalCalorimeter {};
0362 
0363     /// Define collections created by this sensitivie action object
0364     template <> void Geant4SensitiveAction<Geant4OpticalCalorimeter>::defineCollections() {
0365       m_collectionID = declareReadoutFilteredCollection<Geant4Calorimeter::Hit>();
0366     }
0367     /// Method for generating hit(s) using the information of G4Step object.
0368     template <> bool
0369     Geant4SensitiveAction<Geant4OpticalCalorimeter>::process(const G4Step* step,G4TouchableHistory*) {
0370       G4Track * track =  step->GetTrack();
0371       // check that particle is optical photon:
0372       if( track->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition() )  {
0373         return false;
0374       }
0375       else if ( track->GetCreatorProcess()->G4VProcess::GetProcessName() != "Cerenkov")  {
0376         track->SetTrackStatus(fStopAndKill);
0377         return false;
0378       }
0379       else {
0380         typedef Geant4Calorimeter::Hit Hit;
0381         Geant4StepHandler h(step);
0382         Geant4HitCollection*  coll    = collection(m_collectionID);
0383         HitContribution       contrib = Hit::extractContribution(step);
0384         Position              pos     = h.prePos();
0385         Hit* hit = coll->find<Hit>(PositionCompare<Hit,Position>(pos));
0386         if ( !hit ) {
0387           hit = new Hit(pos);
0388           hit->cellID = volumeID(step);
0389           coll->add(hit);
0390           if ( 0 == hit->cellID )  {
0391             hit->cellID = volumeID(step);
0392             except("+++ Invalid CELL ID for hit!");
0393           }
0394         }
0395         hit->energyDeposit += contrib.deposit;
0396         hit->truth.emplace_back(contrib);
0397         track->SetTrackStatus(fStopAndKill); // don't step photon any further
0398         mark(h.track);
0399         return true;
0400       }
0401     }
0402 
0403     /// GFlash/FastSim interface: Method for generating hit(s) using the information of Geant4FastSimSpot object.
0404     template <> bool
0405     Geant4SensitiveAction<Geant4OpticalCalorimeter>::processFastSim(const Geant4FastSimSpot* spot,
0406                                                                     G4TouchableHistory* /* hist */)
0407     {
0408       typedef Geant4Calorimeter::Hit Hit;
0409       Geant4FastSimHandler   h(spot);
0410       const G4Track* track = h.track;
0411       if( track->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition() )  {
0412         return false;
0413       }
0414       else if ( track->GetCreatorProcess()->G4VProcess::GetProcessName() != "Cerenkov")  {
0415         return false;
0416       }
0417       else {
0418         Geant4HitCollection* coll = collection(m_collectionID);
0419         HitContribution   contrib = Hit::extractContribution(spot);
0420         Position          pos     = h.avgPosition();
0421         Hit* hit = coll->find<Hit>(PositionCompare<Hit,Position>(pos));
0422         if ( !hit ) {
0423           hit = new Hit(pos);
0424           hit->cellID = volumeID(h.touchable());
0425           coll->add(hit);
0426           if ( 0 == hit->cellID )  {
0427             hit->cellID = volumeID(h.touchable());
0428             except("+++ Invalid CELL ID for hit!");
0429           }
0430         }
0431         hit->energyDeposit += contrib.deposit;
0432         hit->truth.emplace_back(contrib);
0433         mark(h.track);
0434         return true;
0435       }
0436     }
0437     typedef Geant4SensitiveAction<Geant4OpticalCalorimeter>  Geant4OpticalCalorimeterAction;
0438 
0439 
0440     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0441     //               Geant4SensitiveAction<ScintillatorCalorimeter>
0442     //               For scintillator with Geant4 BirksLaw effect
0443     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0444 
0445 
0446     /* \addtogroup Geant4SDActionPlugin
0447      *
0448      * @{
0449      * \package Geant4ScintillatorCalorimeterAction
0450      * \brief Sensitive detector meant for scintillator calorimeters
0451      *
0452      * This sensitive action will apply Birks' law to the energy deposits
0453      *
0454      * @}
0455      */
0456     /// Class to implement the standard sensitive detector for scintillator calorimeters
0457     struct Geant4ScintillatorCalorimeter {};
0458 
0459     /// Define collections created by this sensitivie action object
0460     template <> void Geant4SensitiveAction<Geant4ScintillatorCalorimeter>::defineCollections() {
0461       m_collectionID = declareReadoutFilteredCollection<Geant4Calorimeter::Hit>();
0462     }
0463     /// Method for generating hit(s) using the information of G4Step object.
0464     template <> bool
0465     Geant4SensitiveAction<Geant4ScintillatorCalorimeter>::process(const G4Step* step,G4TouchableHistory*) {
0466       typedef Geant4Calorimeter::Hit Hit;
0467       Geant4StepHandler    h(step);
0468       HitContribution      contrib = Hit::extractContribution(step,true);
0469       Geant4HitCollection* coll    = collection(m_collectionID);
0470       VolumeID cell = 0;
0471       try {
0472         cell = cellID(step);
0473       } catch(std::runtime_error &e) {
0474         std::stringstream out;
0475         out << std::setprecision(20) << std::scientific;
0476         out << "ERROR: " << e.what()  << std::endl;
0477         out << "Position: "
0478             << "Pre (" << std::setw(24) << step->GetPreStepPoint()->GetPosition() << ") "
0479             << "Post (" << std::setw(24) << step->GetPostStepPoint()->GetPosition() << ") "
0480             << std::endl;
0481         out << "Momentum: "
0482             << " Pre (" <<std::setw(24) << step->GetPreStepPoint() ->GetMomentum()  << ") "
0483             << " Post (" <<std::setw(24) << step->GetPostStepPoint()->GetMomentum() << ") "
0484             << std::endl;
0485         std::cout << out.str();
0486         return true;
0487       }
0488       handleCalorimeterHit(cell, contrib, *coll, h, *this, m_segmentation);
0489       mark(h.track);
0490       return true;
0491     }
0492 
0493     /// GFlash/FastSim interface: Method for generating hit(s) using the information of Geant4FastSimSpot object.
0494     template <> bool
0495     Geant4SensitiveAction<Geant4ScintillatorCalorimeter>::processFastSim(const Geant4FastSimSpot* spot,
0496                                                                          G4TouchableHistory* /* hist */)
0497     {
0498       typedef Geant4Calorimeter::Hit Hit;
0499       Geant4FastSimHandler h(spot);
0500       HitContribution         contrib = Hit::extractContribution(spot);
0501       Geant4HitCollection*    coll    = collection(m_collectionID);
0502       VolumeID cell = 0;
0503       
0504       try {
0505         cell = cellID(h.touchable(), h.avgPositionG4());
0506       } catch(std::runtime_error &e) {
0507         std::stringstream out;
0508         out << std::setprecision(20) << std::scientific;
0509         out << "ERROR: " << e.what()  << std::endl;
0510         out << "Position: (" << std::setw(24) << h.avgPositionG4() << ") " << std::endl;
0511         out << "Momentum: (" << std::setw(24) << h.momentumG4() << ") " << std::endl;
0512         std::cout << out.str();
0513         return true;
0514       }
0515       handleCalorimeterHit(cell, contrib, *coll, h, *this, m_segmentation);
0516       mark(h.track);
0517       return true;
0518     }
0519 
0520     typedef Geant4SensitiveAction<Geant4ScintillatorCalorimeter> Geant4ScintillatorCalorimeterAction;
0521 
0522     /**
0523      *
0524      * \addtogroup Geant4SDActionPlugin
0525      * @{
0526      * \package Geant4TrackerCombineAction
0527      *
0528      * \brief Sensitive detector meant for tracking detectors will combine
0529      * multiple steps of the same track in the same sensitive volume into a
0530      * single hit
0531 
0532      *
0533      * @}
0534      */
0535     /// Geant4 sensitive detector combining all deposits of one G4Track within one sensitive element.
0536     /**
0537      *  Geant4SensitiveAction<TrackerCombine>
0538      *
0539      *
0540      *  \author  M.Frank
0541      *  \version 1.0
0542      *  \ingroup DD4HEP_SIMULATION
0543      */
0544     struct TrackerCombine {
0545       Geant4Tracker::Hit pre, post;
0546       Position           mean_pos;
0547       Geant4Sensitive*   sensitive { nullptr };
0548       G4VPhysicalVolume* firstSpotVolume  { nullptr };
0549       double             mean_time {  0e0 };
0550       double             e_cut     {  0e0 };
0551       int                current   {   -1 };
0552       int                combined  {    0 };
0553       long long int      cell      {    0 };
0554 
0555       TrackerCombine() : pre(), post()   {
0556       }
0557 
0558       /// Start a new hit
0559       void start_collecting(const G4Track* track)   {
0560         pre.truth.deposit = 0.0;
0561         current = pre.truth.trackID;
0562         sensitive->mark(track);
0563         mean_pos.SetXYZ(0,0,0);
0564         mean_time = 0;
0565         post.copyFrom(pre);
0566         combined = 0;
0567         cell = 0;
0568       }
0569       void start(const G4Step* step, const G4StepPoint* point)   {
0570         pre.storePoint(step,point);
0571         start_collecting(step->GetTrack());
0572         firstSpotVolume = step->GetPreStepPoint()->GetTouchableHandle()->GetVolume();
0573       }
0574       void start(const Geant4FastSimSpot* spot)   {
0575         pre.storePoint(spot);
0576         start_collecting(spot->primary);
0577         firstSpotVolume = spot->volume();
0578       }
0579 
0580       /// Update energy and track information during hit info accumulation
0581       void update_collected_hit(const G4VTouchable* h, G4ThreeVector&& global)    {
0582         pre.truth.deposit += post.truth.deposit;
0583         mean_pos.SetX(mean_pos.x()+post.position.x()*post.truth.deposit);
0584         mean_pos.SetY(mean_pos.y()+post.position.y()*post.truth.deposit);
0585         mean_pos.SetZ(mean_pos.z()+post.position.z()*post.truth.deposit);
0586         mean_time += post.truth.time*post.truth.deposit;
0587         if ( 0 == cell )   {
0588           cell = sensitive->cellID(h, global);
0589           if ( 0 == cell )  {
0590             cell = sensitive->volumeID(h) ;
0591             sensitive->except("+++ Invalid CELL ID for hit!");
0592           }
0593         }
0594         ++combined;
0595       }
0596       void update(const Geant4StepHandler& h) {
0597         post.storePoint(h.step, h.post);
0598         update_collected_hit(h.preTouchable(), h.avgPositionG4()); // Compute cellID
0599       }
0600       void update(const Geant4FastSimHandler& h)   {
0601         post.storePoint(h.spot);
0602         update_collected_hit(h.touchable(), h.avgPositionG4());       // Compute cellID
0603       }
0604 
0605       /// Clear collected information and restart for new hit
0606       void clear()  {
0607         mean_pos.SetXYZ(0,0,0);
0608         mean_time = 0;
0609         post.clear();
0610         pre.clear();
0611         current = -1;
0612         combined = 0;
0613         cell = 0;
0614         firstSpotVolume = nullptr;
0615       }
0616 
0617       /// Helper function to decide if the hit has to be extracted and saved in the collection
0618       bool mustSaveTrack(const G4Track* tr)  const   {
0619         return current > 0 && current != tr->GetTrackID();
0620       }
0621 
0622       /// Extract hit information and add the created hit to the collection
0623       void extractHit(Geant4HitCollection* collection)   {
0624         if ( current == -1 ) {
0625           return;
0626         }
0627         double   depo = pre.truth.deposit;
0628         double   time = depo != 0 ? mean_time / depo : mean_time;
0629         Position pos  = depo != 0 ? mean_pos  / depo : mean_pos;
0630         Momentum mom  = 0.5 * (pre.momentum + post.momentum);
0631         double   path_len = (post.position - pre.position).R();
0632         Geant4Tracker::Hit* hit = new Geant4Tracker::Hit(pre.truth.trackID, pre.truth.pdgID,
0633                                                          depo, time, path_len, pos, mom);
0634         hit->cellID   = cell;
0635         collection->add(hit);
0636         sensitive->printM2("+++ TrackID:%6d [%s] CREATE hit combination with %2d deposit(s):"
0637                            " %e MeV  Pos:%8.2f %8.2f %8.2f",
0638                            pre.truth.trackID,sensitive->c_name(),combined,pre.truth.deposit/CLHEP::MeV,
0639                            pos.X()/CLHEP::mm,pos.Y()/CLHEP::mm,pos.Z()/CLHEP::mm);
0640         clear();
0641       }
0642 
0643 
0644       /// Method for generating hit(s) using the information of G4Step object.
0645       G4bool process(const G4Step* step, G4TouchableHistory* ) {
0646         Geant4StepHandler h(step);
0647         // std::cout << " process called - pre pos: " << h.prePos() << " post pos " << h.postPos() 
0648         //           << " edep: " << h.deposit() << std::endl ;
0649         void *prePV = h.volume(h.pre), *postPV = h.volume(h.post);
0650 
0651         Geant4HitCollection* coll = sensitive->collection(0);
0652         /// If we are handling a new track, then store the content of the previous one.
0653         if ( mustSaveTrack(h.track) )  {
0654           extractHit(coll);
0655         }
0656         /// Initialize the deposits of the next hit.
0657         if ( current < 0 )  {
0658           start(step, h.pre);
0659         }
0660         /// ....update .....
0661         update(h);
0662 
0663         if ( prePV != postPV ) {
0664           void* postSD = h.sd(h.post);
0665           extractHit(coll);
0666           if ( 0 != postSD )   {
0667             void* preSD = h.sd(h.pre);
0668             if ( preSD == postSD ) {
0669               start(step,h.post);
0670             }
0671           }
0672         }
0673         else if ( h.track->GetTrackStatus() == fStopAndKill ) {
0674           extractHit(coll);
0675         }
0676         return true;
0677       }
0678 
0679       /// Method for generating hit(s) using the information of fast simulation spot object.
0680       G4bool process(const Geant4FastSimSpot* spot, G4TouchableHistory* ) {
0681         Geant4FastSimHandler h(spot);
0682         G4VPhysicalVolume*   prePV = firstSpotVolume, *postPV = h.volume();
0683         Geant4HitCollection* coll  = sensitive->collection(0);
0684         /// If we are handling a new track, then store the content of the previous one.
0685         if ( mustSaveTrack(h.track) )  {
0686           extractHit(coll);
0687         }
0688         /// Initialize the deposits of the next hit.
0689         if ( current < 0 )  {
0690           start(spot);
0691         }
0692         /// ....update .....
0693         update(h);
0694 
0695         if ( firstSpotVolume && prePV != postPV )   {
0696           void* postSD = h.sd();
0697           extractHit(coll);
0698           if ( 0 != postSD )   {
0699             void* preSD = prePV ? prePV->GetLogicalVolume()->GetSensitiveDetector() : nullptr;
0700             if ( preSD == postSD ) {
0701               start(spot);
0702             }
0703           }
0704         }
0705         else if ( h.track->GetTrackStatus() == fStopAndKill ) {
0706           extractHit(coll);
0707         }
0708         return true;
0709       }
0710 
0711       /// Post-event action callback
0712       void endEvent(const G4Event* /* event */)   {
0713         // We need to add the possibly last added hit to the collection here.
0714         // otherwise the last hit would be assigned to the next event and the
0715         // MC truth would be screwed.
0716         //
0717         // Alternatively the 'update' method would become rather CPU consuming,
0718         // beacuse the extract action would have to be recalculated over and over.
0719         if ( current > 0 )   {
0720           Geant4HitCollection* coll = sensitive->collection(0);
0721           extractHit(coll);
0722         }
0723       }
0724     };
0725 
0726     /// Initialization overload for specialization
0727     template <> void Geant4SensitiveAction<TrackerCombine>::initialize() {
0728       eventAction().callAtEnd(&m_userData,&TrackerCombine::endEvent);
0729       m_userData.e_cut = m_sensitive.energyCutoff();
0730       m_userData.sensitive = this;
0731     }
0732 
0733     /// Define collections created by this sensitivie action object
0734     template <> void Geant4SensitiveAction<TrackerCombine>::defineCollections() {
0735       m_collectionID = declareReadoutFilteredCollection<Geant4Tracker::Hit>();
0736     }
0737 
0738     /// Method for generating hit(s) using the information of G4Step object.
0739     template <> void Geant4SensitiveAction<TrackerCombine>::clear(G4HCofThisEvent*) {
0740       m_userData.clear();
0741     }
0742 
0743     /// Method for generating hit(s) using the information of G4Step object.
0744     template <> G4bool
0745     Geant4SensitiveAction<TrackerCombine>::process(const G4Step* step, G4TouchableHistory* history) {
0746       return m_userData.process(step, history);
0747     }
0748 
0749     /// GFlash/FastSim interface: Method for generating hit(s) using the information of Geant4FastSimSpot object.
0750     template <> bool
0751     Geant4SensitiveAction<TrackerCombine>::processFastSim(const Geant4FastSimSpot* spot,
0752                                                           G4TouchableHistory*      history)    {
0753       return m_userData.process(spot, history);
0754     }
0755 
0756     typedef Geant4SensitiveAction<TrackerCombine>  Geant4TrackerCombineAction;
0757     typedef Geant4TrackerAction                    Geant4SimpleTrackerAction;
0758     typedef Geant4CalorimeterAction                Geant4SimpleCalorimeterAction;
0759     typedef Geant4OpticalCalorimeterAction         Geant4SimpleOpticalCalorimeterAction;
0760   }
0761 }
0762 
0763 using namespace dd4hep::sim;
0764 
0765 #include <DDG4/Factories.h>
0766 // Special void entry point
0767 DECLARE_GEANT4SENSITIVE(Geant4VoidSensitiveAction)
0768 // Standard factories used for simulation
0769 DECLARE_GEANT4SENSITIVE(Geant4TrackerAction)
0770 DECLARE_GEANT4SENSITIVE(Geant4OpticalTrackerAction)
0771 DECLARE_GEANT4SENSITIVE(Geant4TrackerCombineAction)
0772 DECLARE_GEANT4SENSITIVE(Geant4CalorimeterAction)
0773 DECLARE_GEANT4SENSITIVE(Geant4OpticalCalorimeterAction)
0774 DECLARE_GEANT4SENSITIVE(Geant4ScintillatorCalorimeterAction)
0775 
0776 // Need these factories for backwards compatibility
0777 DECLARE_GEANT4SENSITIVE(Geant4SimpleTrackerAction)
0778 DECLARE_GEANT4SENSITIVE(Geant4SimpleCalorimeterAction)
0779 DECLARE_GEANT4SENSITIVE(Geant4SimpleOpticalCalorimeterAction)