Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:17:53

0001 #include "PHG4ZDCSteppingAction.h"
0002 
0003 #include "PHG4ZDCDetector.h"
0004 
0005 #include <phparameter/PHParameters.h>
0006 
0007 #include <g4main/PHG4Hit.h>
0008 #include <g4main/PHG4HitContainer.h>
0009 #include <g4main/PHG4Hitv1.h>
0010 #include <g4main/PHG4Shower.h>
0011 #include <g4main/PHG4SteppingAction.h>  // for PHG4SteppingAction
0012 #include <g4main/PHG4TrackUserInfoV1.h>
0013 
0014 #include <phool/PHRandomSeed.h>
0015 #include <phool/getClass.h>
0016 
0017 #include <Geant4/G4DynamicParticle.hh>  // for G4DynamicParticle
0018 #include <Geant4/G4IonisParamMat.hh>    // for G4IonisParamMat
0019 #include <Geant4/G4Material.hh>         // for G4Material
0020 #include <Geant4/G4MaterialCutsCouple.hh>
0021 #include <Geant4/G4ParticleDefinition.hh>      // for G4ParticleDefinition
0022 #include <Geant4/G4ReferenceCountedHandle.hh>  // for G4ReferenceCountedHandle
0023 #include <Geant4/G4Step.hh>
0024 #include <Geant4/G4StepPoint.hh>   // for G4StepPoint
0025 #include <Geant4/G4StepStatus.hh>  // for fGeomBoundary, fAtRest...
0026 #include <Geant4/G4String.hh>      // for G4String
0027 #include <Geant4/G4SystemOfUnits.hh>
0028 #include <Geant4/G4ThreeVector.hh>  // for G4ThreeVector
0029 #include <Geant4/G4TouchableHandle.hh>
0030 #include <Geant4/G4Track.hh>                  // for G4Track
0031 #include <Geant4/G4TrackStatus.hh>            // for fStopAndKill
0032 #include <Geant4/G4Types.hh>                  // for G4double
0033 #include <Geant4/G4VPhysicalVolume.hh>        // for G4VPhysicalVolume
0034 #include <Geant4/G4VTouchable.hh>             // for G4VTouchable
0035 #include <Geant4/G4VUserTrackInformation.hh>  // for G4VUserTrackInformation
0036 
0037 #include <TSystem.h>
0038 
0039 #include <gsl/gsl_randist.h>
0040 #include <gsl/gsl_rng.h>
0041 
0042 #include <array>
0043 #include <cmath>
0044 #include <iostream>
0045 #include <string>  // for basic_string, operator+
0046 
0047 class PHCompositeNode;
0048 
0049 //____________________________________________________________________________..
0050 PHG4ZDCSteppingAction::PHG4ZDCSteppingAction(PHG4ZDCDetector* detector, const PHParameters* parameters)
0051   : PHG4SteppingAction(detector->GetName())
0052   , m_Detector(detector)
0053   , m_Params(parameters)
0054   , m_IsActiveFlag(m_Params->get_int_param("active"))
0055   , absorbertruth(m_Params->get_int_param("absorberactive"))
0056   , m_IsBlackHole(m_Params->get_int_param("blackhole"))
0057 {
0058   RandomGenerator = gsl_rng_alloc(gsl_rng_mt19937);
0059   unsigned int seed = PHRandomSeed();
0060   gsl_rng_set(RandomGenerator, seed);
0061 }
0062 
0063 PHG4ZDCSteppingAction::~PHG4ZDCSteppingAction()
0064 {
0065   // if the last hit was a zero energie deposit hit, it is just reset
0066   // and the memory is still allocated, so we need to delete it here
0067   // if the last hit was saved, hit is a nullptr pointer which are
0068   // legal to delete (it results in a no operation)
0069   delete m_Hit;
0070   gsl_rng_free(RandomGenerator);
0071 }
0072 
0073 //____________________________________________________________________________..
0074 bool PHG4ZDCSteppingAction::UserSteppingAction(const G4Step* aStep, bool /*was_used*/)
0075 {
0076   G4TouchableHandle touch = aStep->GetPreStepPoint()->GetTouchableHandle();
0077   G4VPhysicalVolume* volume = touch->GetVolume();
0078 
0079   // m_Detector->IsInZDC(volume)
0080   // returns
0081   //  0 is outside of ZDC
0082   //  1 is inside scintillator
0083   // -1 is inside absorber or support structure (dead material)
0084 
0085   int whichactive = m_Detector->IsInZDC(volume);
0086 
0087   if (!whichactive)
0088   {
0089     return false;
0090   }
0091 
0092   int layer_id = m_Detector->get_Layer();
0093   int idx_k = -1;
0094   int idx_j = -1;
0095 
0096   if (whichactive > 0)  // in scintillator or fiber
0097   {
0098     /* Find indices of scintillator / tower containing this step */
0099     // FindIndex(touch, idx_j, idx_k);
0100     if (whichactive == 2)
0101     {
0102       FindIndexZDC(touch, idx_j, idx_k);
0103     }
0104     if (whichactive == 1)
0105     {
0106       FindIndexSMD(touch, idx_j, idx_k);
0107     }
0108   }
0109   /* Get energy deposited by this step */
0110   G4double edep = aStep->GetTotalEnergyDeposit() / GeV;
0111   G4double eion = (aStep->GetTotalEnergyDeposit() - aStep->GetNonIonizingEnergyDeposit()) / GeV;
0112   G4double light_yield = 0;
0113 
0114   /* Get pointer to associated Geant4 track */
0115   const G4Track* aTrack = aStep->GetTrack();
0116 
0117   // if this block stops everything, just put all kinetic energy into edep
0118   if (m_IsBlackHole)
0119   {
0120     edep = aTrack->GetKineticEnergy() / GeV;
0121     G4Track* killtrack = const_cast<G4Track*>(aTrack);
0122     killtrack->SetTrackStatus(fStopAndKill);
0123   }
0124 
0125   /* Make sure we are in a volume */
0126   if (m_IsActiveFlag)
0127   {
0128     /* Check if particle is 'geantino' */
0129     bool geantino = false;
0130     if (aTrack->GetParticleDefinition()->GetPDGEncoding() == 0 &&
0131         aTrack->GetParticleDefinition()->GetParticleName().find("geantino") != std::string::npos)
0132     {
0133       geantino = true;
0134     }
0135 
0136     /* Get Geant4 pre- and post-step points */
0137     G4StepPoint* prePoint = aStep->GetPreStepPoint();
0138     G4StepPoint* postPoint = aStep->GetPostStepPoint();
0139 
0140     // if prepoint is in fiber
0141     if (whichactive > 1)
0142     {
0143       double charge = aTrack->GetParticleDefinition()->GetPDGCharge();
0144       // if charged particle
0145       if (charge != 0)
0146       {
0147         // check if prepoint in active volume & postpoint out of active volume
0148         G4VPhysicalVolume* postvolume = postPoint->GetTouchableHandle()->GetVolume();
0149         int postactive = m_Detector->IsInZDC(postvolume);
0150         // postpoint outside fiber
0151         if (!postactive)
0152         {
0153           // get particle information here
0154           int pid = aTrack->GetParticleDefinition()->GetPDGEncoding();
0155           // calculate incidence angle
0156           const G4DynamicParticle* dypar = aTrack->GetDynamicParticle();
0157           const G4ThreeVector& pdirect = dypar->GetMomentumDirection();
0158           // this triggers cppcheck, the code is good and the warning is suppressed
0159           // cppcheck-suppress [duplicateAssignExpression, unmatchedSuppression]
0160           double dy = sqrt(2) / 2.;
0161           double dz = sqrt(2) / 2.;
0162           if (idx_j == 1)
0163           {
0164             dz = -dz;
0165           }
0166           double CosTheta = pdirect.y() * dy + pdirect.z() * dz;
0167           double angle = acos(CosTheta) * 180.0 / M_PI;
0168           if (pid == 11 || pid == -11)
0169           {
0170             // find energy
0171             G4double E = dypar->GetTotalEnergy();
0172             // electron response here
0173             double avg_ph = ZDCEResponse(E, angle);
0174             avg_ph *= 0.16848;
0175             // use Poisson Distribution here
0176             int n_ph = gsl_ran_poisson(RandomGenerator, avg_ph);
0177             light_yield += n_ph;
0178           }
0179           else
0180           {
0181             G4double E = dypar->GetTotalEnergy();
0182             G4double P = dypar->GetTotalMomentum();
0183             double beta = P / E;
0184             double avg_ph = ZDCResponse(beta, angle);
0185             avg_ph *= 0.16848;
0186             int n_ph = gsl_ran_poisson(RandomGenerator, avg_ph);
0187             light_yield += n_ph;
0188           }
0189         }
0190       }
0191     }
0192     switch (prePoint->GetStepStatus())
0193     {
0194     case fGeomBoundary:
0195     case fUndefined:
0196       if (!m_Hit)
0197       {
0198         m_Hit = new PHG4Hitv1();
0199       }
0200 
0201       /* Set hit location (space point) */
0202       m_Hit->set_x(0, prePoint->GetPosition().x() / cm);
0203       m_Hit->set_y(0, prePoint->GetPosition().y() / cm);
0204       m_Hit->set_z(0, prePoint->GetPosition().z() / cm);
0205 
0206       /* Set hit time */
0207       m_Hit->set_t(0, prePoint->GetGlobalTime() / nanosecond);
0208 
0209       // set the track ID
0210       m_Hit->set_trkid(aTrack->GetTrackID());
0211       /* set intial energy deposit */
0212       m_Hit->set_edep(0);
0213 
0214       /* Now add the hit to the hit collection */
0215       // here we do things which are different between scintillator and absorber hits
0216       if (whichactive > 0)
0217       {
0218         m_CurrentHitContainer = m_HitContainer;
0219         m_Hit->set_eion(0);
0220         m_Hit->set_light_yield(0);  // for scintillator only, initialize light yields
0221         /* Set hit location (tower index) */
0222         m_Hit->set_index_k(idx_k);
0223         m_Hit->set_index_j(idx_j);
0224       }
0225       else
0226       {
0227         if (whichactive == -1)
0228         {
0229           m_CurrentHitContainer = m_AbsorberHitContainer;
0230         }
0231         else
0232         {
0233           m_CurrentHitContainer = m_SupportHitContainer;
0234         }
0235       }
0236       // here we set what is common for scintillator and absorber hits
0237       if (G4VUserTrackInformation* p = aTrack->GetUserInformation())
0238       {
0239         if (PHG4TrackUserInfoV1* pp = dynamic_cast<PHG4TrackUserInfoV1*>(p))
0240         {
0241           m_Hit->set_trkid(pp->GetUserTrackId());
0242           m_Hit->set_shower_id(pp->GetShower()->get_id());
0243           m_CurrentShower = pp->GetShower();
0244         }
0245       }
0246       break;
0247     default:
0248       break;
0249     }
0250 
0251     if (whichactive == 1)
0252     {
0253       // light_yield = eion;
0254       light_yield = GetVisibleEnergyDeposition(aStep);  // for scintillator only, calculate light yields
0255       static bool once = true;
0256 
0257       if (once && edep > 0)
0258       {
0259         once = false;
0260 
0261         if (Verbosity() > 0)
0262         {
0263           std::cout << "PHG4ZDCSteppingAction::UserSteppingAction::"
0264                     //
0265                     << m_Detector->GetName() << " - "
0266                     << " use scintillating light model at each Geant4 steps. "
0267                     << "First step: "
0268                     << "Material = "
0269                     << aTrack->GetMaterialCutsCouple()->GetMaterial()->GetName()
0270                     << ", "
0271                     << "Birk Constant = "
0272                     << aTrack->GetMaterialCutsCouple()->GetMaterial()->GetIonisation()->GetBirksConstant()
0273                     << ","
0274                     << "edep = " << edep << ", "
0275                     << "eion = " << eion
0276                     << ", "
0277                     << "light_yield = " << light_yield << std::endl;
0278         }
0279       }
0280     }
0281 
0282     /* sum up the energy to get total deposited */
0283 
0284     m_Hit->set_edep(m_Hit->get_edep() + edep);
0285     if (whichactive > 0)
0286     {
0287       m_Hit->set_eion(m_Hit->get_eion() + eion);
0288       m_Hit->set_light_yield(m_Hit->get_light_yield() + light_yield);
0289     }
0290 
0291     // if any of these conditions is true this is the last step in
0292     // this volume and we need to save the hit
0293     // postPoint->GetStepStatus() == fGeomBoundary: track leaves this volume
0294     // postPoint->GetStepStatus() == fWorldBoundary: track leaves this world
0295     // (not sure if this will ever be the case)
0296     // aTrack->GetTrackStatus() == fStopAndKill: track ends
0297     if (postPoint->GetStepStatus() == fGeomBoundary ||
0298         postPoint->GetStepStatus() == fWorldBoundary ||
0299         postPoint->GetStepStatus() == fAtRestDoItProc ||
0300         aTrack->GetTrackStatus() == fStopAndKill)
0301     {
0302       // Update exit values
0303       m_Hit->set_x(1, postPoint->GetPosition().x() / cm);
0304       m_Hit->set_y(1, postPoint->GetPosition().y() / cm);
0305       m_Hit->set_z(1, postPoint->GetPosition().z() / cm);
0306 
0307       m_Hit->set_t(1, postPoint->GetGlobalTime() / nanosecond);
0308 
0309       // special case for geantinos
0310       if (geantino)
0311       {
0312         m_Hit->set_edep(-1);  // only energy=0 g4hits get dropped, this way geantinos survive the g4hit compression
0313         if (whichactive > 0)
0314         {
0315           m_Hit->set_eion(-1);
0316           m_Hit->set_light_yield(-1);
0317         }
0318       }
0319       // save only hits with energy deposit (or -1 for geantino)
0320       if (m_Hit->get_edep())
0321       {
0322         m_CurrentHitContainer->AddHit(layer_id, m_Hit);
0323         if (m_CurrentShower)
0324         {
0325           m_CurrentShower->add_g4hit_id(m_CurrentHitContainer->GetID(), m_Hit->get_hit_id());
0326         }
0327         // ownership has been transferred to container, set to null
0328         if (m_Hit->get_edep() > 0 && (whichactive > 0 || absorbertruth > 0))
0329         {
0330           if (G4VUserTrackInformation* p = aTrack->GetUserInformation())
0331           {
0332             if (PHG4TrackUserInfoV1* pp = dynamic_cast<PHG4TrackUserInfoV1*>(p))
0333             {
0334               pp->SetKeep(1);  // we want to keep the track
0335             }
0336           }
0337         }
0338         // so we will create a new hit for the next track
0339         m_Hit = nullptr;
0340       }
0341       else
0342       {
0343         // if this hit has no energy deposit, just reset it for reuse
0344         // this means we have to delete it in the dtor. If this was
0345         // the last hit we processed the memory is still allocated
0346         m_Hit->Reset();
0347       }
0348     }
0349     return true;
0350   }
0351   else
0352   {
0353     return false;
0354   }
0355 }
0356 
0357 //____________________________________________________________________________..
0358 void PHG4ZDCSteppingAction::SetInterfacePointers(PHCompositeNode* topNode)
0359 {
0360   // now look for the map and grab a pointer to it.
0361   m_HitContainer = findNode::getClass<PHG4HitContainer>(topNode, m_HitNodeName);
0362   m_AbsorberHitContainer = findNode::getClass<PHG4HitContainer>(topNode, m_AbsorberNodeName);
0363   m_SupportHitContainer = findNode::getClass<PHG4HitContainer>(topNode, m_SupportNodeName);
0364   // if we do not find the node it's messed up.
0365   if (!m_HitContainer)
0366   {
0367     std::cout << "PHG4ZDCSteppingAction::SetTopNode - unable to find " << m_HitNodeName << std::endl;
0368     gSystem->Exit(1);
0369   }
0370   // this is perfectly fine if absorber hits are disabled
0371   if (!m_AbsorberHitContainer)
0372   {
0373     if (Verbosity() > 0)
0374     {
0375       std::cout << "PHG4ZDCSteppingAction::SetTopNode - unable to find " << m_AbsorberNodeName << std::endl;
0376     }
0377   }
0378   if (!m_SupportHitContainer)
0379   {
0380     if (Verbosity() > 0)
0381     {
0382       std::cout << "PHG4ZDCSteppingAction::SetTopNode - unable to find " << m_SupportNodeName << std::endl;
0383     }
0384   }
0385 }
0386 
0387 void PHG4ZDCSteppingAction::SetHitNodeName(const std::string& type, const std::string& name)
0388 {
0389   if (type == "G4HIT")
0390   {
0391     m_HitNodeName = name;
0392     return;
0393   }
0394   else if (type == "G4HIT_ABSORBER")
0395   {
0396     m_AbsorberNodeName = name;
0397     return;
0398   }
0399   else if (type == "G4HIT_SUPPORT")
0400   {
0401     m_SupportNodeName = name;
0402     return;
0403   }
0404   std::cout << "Invalid output hit node type " << type << std::endl;
0405   gSystem->Exit(1);
0406   return;
0407 }
0408 
0409 // getting index using copyno
0410 // if in ZDC PMMA fiber
0411 int PHG4ZDCSteppingAction::FindIndexZDC(G4TouchableHandle& touch, int& j, int& k)
0412 {
0413   G4VPhysicalVolume* envelope = touch->GetVolume(2);  // Get the world
0414   G4VPhysicalVolume* plate = touch->GetVolume(1);     // Get the fiber plate
0415 
0416   j = envelope->GetCopyNo();
0417   k = (plate->GetCopyNo()) / 27;
0418 
0419   return 0;
0420 }
0421 
0422 int PHG4ZDCSteppingAction::FindIndexSMD(G4TouchableHandle& touch, int& j, int& k)
0423 {
0424   int jshift = 10;
0425   int kshift = 10;
0426   G4VPhysicalVolume* envelope = touch->GetVolume(2);  // Get the envelope
0427   G4VPhysicalVolume* scint = touch->GetVolume(0);     // Get the fiber plate
0428 
0429   int whichzdc = envelope->GetCopyNo();
0430 
0431   j = scint->GetCopyNo() % 7;
0432   k = scint->GetCopyNo() / 7;
0433 
0434   if (whichzdc == 1)
0435   {
0436     j += 7;
0437   }
0438   // shift the index to avoid conflict with the ZDC index
0439   j += jshift;
0440   k += kshift;
0441 
0442   return 0;
0443 }
0444 
0445 double PHG4ZDCSteppingAction::ZDCResponse(double beta, double angle)
0446 {
0447   if (beta < m_BetaThersh)
0448   {
0449     return 0;
0450   }
0451   if (angle >= 90)
0452   {
0453     return 0;
0454   }
0455   for (int i = 1; i < 9; i++)
0456   {
0457     if (beta <= m_Beta[i])
0458     {
0459       std::array<double, 18> PMMAsub0 = m_PMMA05[i - 1];
0460       std::array<double, 18> PMMAsub1 = m_PMMA05[i];
0461       // find angle bin here and do 1D linear interpolation of angle
0462       int Abin = (int) angle / 5;
0463       if (Abin == 0)
0464       {
0465         Abin = 1;
0466       }
0467       double avg_ph0 = PMMAsub0[Abin - 1] + (PMMAsub0[Abin] - PMMAsub0[Abin - 1]) * (angle / 5 - Abin + 1);
0468       double avg_ph1 = PMMAsub1[Abin - 1] + (PMMAsub1[Abin] - PMMAsub1[Abin - 1]) * (angle / 5 - Abin + 1);
0469       if (avg_ph0 < 0)
0470       {
0471         avg_ph0 = 0;
0472       }
0473       if (avg_ph1 < 0)
0474       {
0475         avg_ph1 = 0;
0476       }
0477       // linear linear interpolation with beta
0478       double avg_ph = avg_ph0 + (avg_ph1 - avg_ph0) * (beta - m_Beta[i - 1]) / (m_Beta[i] - m_Beta[i - 1]);
0479       if (avg_ph < 0)
0480       {
0481         avg_ph = 0;
0482       }
0483       // use poisson?
0484       return avg_ph;
0485     }
0486   }
0487 
0488   return 0;
0489 }
0490 
0491 double PHG4ZDCSteppingAction::ZDCEResponse(double E, double angle)
0492 {
0493   if (E < m_E[0])
0494   {
0495     return 0;
0496   }
0497 
0498   if (E >= 0.05)
0499   {
0500     std::array<double, 36> PMMAsub0 = m_PMMA05E[10];
0501     int Abin = (int) angle / 5;
0502     if (Abin == 0)
0503     {
0504       Abin = 1;
0505     }
0506     double avg_ph = PMMAsub0[Abin - 1] + (PMMAsub0[Abin] - PMMAsub0[Abin - 1]) * (angle / 5 - Abin + 1);
0507     return avg_ph;
0508   }
0509   else
0510   {
0511     for (int i = 1; i < 11; i++)
0512     {
0513       if (E <= m_E[i])
0514       {
0515         std::array<double, 36> PMMAsub0 = m_PMMA05E[i - 1];
0516         std::array<double, 36> PMMAsub1 = m_PMMA05E[i];
0517 
0518         int Abin = (int) angle / 5;
0519         if (Abin == 0)
0520         {
0521           Abin = 1;
0522         }
0523         double avg_ph0 = PMMAsub0[Abin - 1] + (PMMAsub0[Abin] - PMMAsub0[Abin - 1]) * (angle / 5 - Abin + 1);
0524         double avg_ph1 = PMMAsub1[Abin - 1] + (PMMAsub1[Abin] - PMMAsub1[Abin - 1]) * (angle / 5 - Abin + 1);
0525         if (avg_ph0 < 0)
0526         {
0527           avg_ph0 = 0;
0528         }
0529         if (avg_ph1 < 0)
0530         {
0531           avg_ph1 = 0;
0532         }
0533         // linear linear interpolation with E
0534         double avg_ph = avg_ph0 + (avg_ph1 - avg_ph0) * (E - m_E[i - 1]) / (m_E[i] - m_E[i - 1]);
0535         if (avg_ph < 0)
0536         {
0537           avg_ph = 0;
0538         }
0539         // use poisson?
0540         return avg_ph;
0541       }
0542     }
0543   }
0544   std::cout << "out of range" << std::endl;
0545   return 0;
0546 }