Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-13 08:21:05

0001 #include "G4HepEmGammaManager.hh"
0002 
0003 #include "G4HepEmData.hh"
0004 #include "G4HepEmParameters.hh"
0005 #include "G4HepEmTLData.hh"
0006 
0007 #include "G4HepEmConstants.hh"
0008 #include "G4HepEmMatCutData.hh"
0009 #include "G4HepEmMaterialData.hh"
0010 #include "G4HepEmGammaData.hh"
0011 
0012 #include "G4HepEmMath.hh"
0013 
0014 #include "G4HepEmRunUtils.hh"
0015 #include "G4HepEmTrack.hh"
0016 #include "G4HepEmElectronTrack.hh"
0017 #include "G4HepEmGammaTrack.hh"
0018 
0019 #include "G4HepEmGammaInteractionConversion.hh"
0020 #include "G4HepEmGammaInteractionCompton.hh"
0021 #include "G4HepEmGammaInteractionPhotoelectric.hh"
0022 
0023 #include <iostream>
0024 
0025 // Note: pStepLength will be set here i.e. this is the first access to it that
0026 //       will clear the previous step value.
0027 void G4HepEmGammaManager::HowFar(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmTLData* tlData) {
0028   G4HepEmGammaTrack* theGammaTrack = tlData->GetPrimaryGammaTrack();
0029   G4HepEmTrack* theTrack = theGammaTrack->GetTrack();
0030   // Sample the `number-of-interaction-left`
0031   if (theTrack->GetNumIALeft(0) <= 0.0) {
0032     theTrack->SetNumIALeft(-G4HepEmLog(tlData->GetRNGEngine()->flat()), 0);
0033   }
0034   HowFar(hepEmData, hepEmPars, theGammaTrack);
0035 }
0036 
0037 
0038 void G4HepEmGammaManager::HowFar(struct G4HepEmData* hepEmData, struct G4HepEmParameters* /*hepEmPars*/, G4HepEmGammaTrack* theGammaTrack) {
0039   G4HepEmTrack* theTrack = theGammaTrack->GetTrack();
0040   // get the total macroscopic cross section and use this to sample the actual step length
0041   // (till any of the possible discrete interactions: conversion, compton, photoelectric, gamma-nuclear)
0042   const double totMXSec = GetTotalMacXSec(hepEmData, theGammaTrack);
0043   const double totalMFP = (totMXSec>0.) ? 1./totMXSec : kALargeValue;
0044   // save the mac-xsec for the update of the `number-of-interaction-left`:
0045   theTrack->SetMFP(totalMFP, 0);
0046   // sample the proposed step length
0047   theTrack->SetGStepLength(totalMFP*theTrack->GetNumIALeft(0));
0048 }
0049 
0050 
0051 // NOTE: `SampleInteraction` needs to be invoked before that will set the winner process ID of the trimary track.
0052 //        This is not invoked here inside as it might be possible that gamma-nuclear happens in which case the caller
0053 //        needs to perfrom the interaction.
0054 void G4HepEmGammaManager::Perform(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmTLData* tlData) {
0055   G4HepEmTrack* theTrack = tlData->GetPrimaryGammaTrack()->GetTrack();
0056   // === 1. The `number-of-interaction-left` needs to be updated based on the actual
0057   //        step lenght and the energy deposit needs to be reset to 0.0
0058   // physical step length is the geometrical fo rgamma
0059   UpdateNumIALeft(theTrack);
0060   // reset energy deposit
0061   theTrack->SetEnergyDeposit(0.0);
0062   // === Gamma has pure Discrete interaction (if any)
0063   // 2. check if discrete process limited the step return otherwise (i.e. if
0064   //    boundary process limited the step)
0065   if (theTrack->GetOnBoundary()) {
0066     return;
0067   }
0068 
0069   // reset number of interaction left for the winner discrete process
0070   const int iDProc = theTrack->GetWinnerProcessIndex();
0071   theTrack->SetNumIALeft(-1.0, iDProc);
0072   //
0073   // perform the discrete part of the winner interaction
0074   switch (iDProc) {
0075     case 0: // invoke gamma Conversion to e-/e+ pairs: {
0076             G4HepEmGammaInteractionConversion::Perform(tlData, hepEmData);
0077             break;
0078     case 1: // invoke Compton scattering of gamma:
0079             G4HepEmGammaInteractionCompton::Perform(tlData, hepEmData);
0080             break;
0081     case 2: // invoke photoelectric process:
0082             G4HepEmGammaInteractionPhotoelectric::Perform(tlData, hepEmData);
0083             break;
0084     case 3: // NOTE: HepEm do not handle Gamma-nuclear interaction. It is done
0085             //       in the tracking manager calling the native Geant4 process
0086             break;
0087   }
0088   // Check if the final kinetic energy drops below the tracking cut and stop.
0089   const double finalEkin = theTrack->GetEKin();
0090   if (finalEkin > 0.0 && finalEkin <= hepEmPars->fGammaTrackingCut) {
0091     theTrack->SetEKin(0.0);
0092     theTrack->AddEnergyDeposit(finalEkin);
0093   }
0094 }
0095 
0096 
0097 void   G4HepEmGammaManager::UpdateNumIALeft(G4HepEmTrack* theTrack) {
0098   const double pStepLength = theTrack->GetGStepLength();
0099   // NOTE: only one `number of interaction length left` is used from now on
0100   //       for gamma that is the total, i.e. including all possible interactions,
0101   //       and the MFP[0] is the total MFP
0102   double*       preStepMFP = theTrack->GetMFP();
0103   double*    numInterALeft = theTrack->GetNumIALeft();
0104   numInterALeft[0] -= pStepLength/preStepMFP[0];
0105 }
0106 
0107 
0108 double  G4HepEmGammaManager::GetTotalMacXSec(const struct G4HepEmData* hepEmData, G4HepEmGammaTrack* theGammaTrack) {
0109   G4HepEmTrack* theTrack = theGammaTrack->GetTrack();
0110   const double   theEkin = theTrack->GetEKin();
0111   const double  theLEkin = theTrack->GetLogEKin();
0112   const int   theMatIndx = hepEmData->fTheMatCutData->fMatCutData[theTrack->GetMCIndex()].fHepEmMatIndex;
0113   // get the G4HepEmGammaData
0114   const G4HepEmGammaData* gmData = hepEmData->fTheGammaData;
0115   // find the kinetic energy window for the given `ekin`:
0116   // most common case first: ekin > 2m_ec^2
0117   if (theEkin > gmData->fEMax1) {
0118     // window 2: ekin \in [2mc^2,100 TeV]; spline with 4 `y_i` values and their second derivatives at each `E_i`
0119     // The total macroscopic ross section is the first `y` value so `iwhich=1`
0120     const int ndata  = gmData->fEGridSize2;
0121     const int istart = theMatIndx*gmData->fDataPerMat + gmData->fNumData0 + gmData->fNumData1; // start of data for this material and this energy window
0122     return GetSplineLog4(ndata, &(gmData->fMacXsecData[istart]), theEkin, theLEkin, gmData->fLogEMin2, gmData->fEILDelta2, 1);
0123   }
0124 
0125   // NOTE: in case of window 1 and 0, the macroscopic cross section for PE is stored in the track that is used:
0126   //       - to sample the interaction (Compton or PE if any) at the post step point (in `SampleInteraction` below)
0127   //       - if PE interactin happens, then used to sample the target atom in the mnodel
0128   //       While in case of window 2 above, the macroscopic corss section for PE is set in the track when sampling
0129   //       the interaction and only if PE happens.
0130   if (theEkin > gmData->fEMax0) {
0131     // window 1: ekin in [150 keV, 2mc^2]; linear interpolation with 2 `y_i` values at each `E_i`
0132     // The total macroscopic cross sectino is the first `y` value so `iwhich=1`
0133     const int ndata  = gmData->fEGridSize1;
0134     const int istart = theMatIndx*gmData->fDataPerMat + gmData->fNumData0; // start of data for this material and this energy window
0135     // might be better to interpolate both in this case and set the PEmxSec here
0136     double mx[2]; // [total mac. xsec, PE mac. xsec]
0137     GetLinearLog2(ndata, &(gmData->fMacXsecData[istart]), theEkin, theLEkin, gmData->fLogEMin1, gmData->fEILDelta1, mx);
0138     theGammaTrack->SetPEmxSec(mx[1]);
0139     return mx[0];
0140   }
0141 
0142   // window 0: ekin in [100 eV, 150 keV]; linear interpolation with one `y_i` values at each `E_i`
0143   //           which is not the total, but only the Compton scattering cross section so interpolate
0144   //           that and add the PE cross section (also stores the PE cross section in the track
0145   //           that can be used to select the interaction in this case)
0146   const int  ndata  = gmData->fEGridSize0;
0147   const int  istart = theMatIndx*gmData->fDataPerMat; // start of data for this material and this energy window
0148   const double comp = GetLinearLog(ndata, &(gmData->fMacXsecData[istart]), theEkin, theLEkin, gmData->fLogEMin0, gmData->fEILDelta0);
0149   const double   pe = G4HepEmMax(0.0, GetMacXSecPE(hepEmData, theMatIndx, theEkin));
0150   theGammaTrack->SetPEmxSec(pe);
0151   return comp+pe;
0152 }
0153 
0154 
0155 double G4HepEmGammaManager::GetMacXSecPE(const struct G4HepEmData* hepEmData, const int imat, const double ekin) {
0156   const G4HepEmMatData* matData = &hepEmData->fTheMaterialData->fMaterialData[imat];
0157   int interval = 0;
0158   if (ekin >= matData->fSandiaEnergies[0]) {
0159     // Optimization: linear search starting with intervals for higher energies.
0160     for (int i = matData->fNumOfSandiaIntervals - 1; i >= 0; i--) {
0161       if (ekin >= matData->fSandiaEnergies[i]) {
0162         interval = i;
0163         break;
0164       }
0165     }
0166   }
0167   const double* sandiaCof = &matData->fSandiaCoefficients[4 * interval];
0168   const double inv = 1 / ekin;
0169   return inv * (sandiaCof[0] + inv * (sandiaCof[1] + inv * (sandiaCof[2] + inv * sandiaCof[3])));
0170 }
0171 
0172 
0173 void G4HepEmGammaManager::SelectInteraction(const struct G4HepEmData* hepEmData, G4HepEmTLData* tlData) {
0174   G4HepEmGammaTrack* theGammaTrack = tlData->GetPrimaryGammaTrack();
0175   // selected interactin ID will be set into the track as the winner process index
0176   // and number of interaction length left will be reset to -1.0
0177   SampleInteraction(hepEmData, theGammaTrack, tlData->GetRNGEngine()->flat());
0178 }
0179 
0180 
0181 // Sample the discrete interaction that happens at the post setp point and sets the track filed
0182 // Also sets the PE macroscopic cross section in the gamma track if PE happens (garbage othewise)
0183 // pid = 0 --> Conversion
0184 // pid = 1 --> Compton scattering
0185 // pid = 2 --> Photoelectric effect
0186 // pid = 3 --> Gamma nuclear interaction
0187 void G4HepEmGammaManager::SampleInteraction(const struct G4HepEmData* hepEmData, G4HepEmGammaTrack* theGammaTrack, const double urnd) {
0188   G4HepEmTrack* theTrack = theGammaTrack->GetTrack();
0189   const double   theEkin = theTrack->GetEKin();
0190   const double  theTMFP  = theTrack->GetMFP(0); // only the total mean free path is stored (and that at[0])
0191   // reset num-ia-left as interaction will happen so we need to re-sample it at the beginning of the next step
0192   theTrack->SetNumIALeft(-1.0, 0);
0193   // get the G4HepEmGammaData
0194   const G4HepEmGammaData* gmData = hepEmData->fTheGammaData;
0195   // check kinetic energy window: most common case, ekin > 2m_ec^2
0196   if (theEkin > gmData->fEMax1) {
0197     const double  theLEkin = theTrack->GetLogEKin();
0198     const int   theMatIndx = hepEmData->fTheMatCutData->fMatCutData[theTrack->GetMCIndex()].fHepEmMatIndex;
0199     const int       ndata  = gmData->fEGridSize2;
0200     const int       istart = theMatIndx*gmData->fDataPerMat + gmData->fNumData0 + gmData->fNumData1; // start of data for this material and this energy window
0201     double mxSec = 0.0;
0202     double cProb = 0.0;
0203     int pid = 2; // the real pid is pid-2
0204     do {
0205       mxSec  = GetSplineLog4(ndata, &(gmData->fMacXsecData[istart]), theEkin, theLEkin, gmData->fLogEMin2, gmData->fEILDelta2, pid);
0206       cProb += mxSec*theTMFP;
0207       ++pid;
0208     } while (pid<6 && urnd>cProb);
0209     theTrack->SetWinnerProcessIndex(pid-3); // -3 because at the end ++pid
0210     theGammaTrack->SetPEmxSec(mxSec);
0211     return;
0212   }
0213   // Below the 2 electron mass kinetic energy limit:
0214   // Only Compton or PE possible and mac. xsec. for PE is already in the track
0215   // (was set during the step limit when the total mac. xsec. was calculated)
0216   const double mxPE = theGammaTrack->GetPEmxSec();
0217   const int pid = (urnd > theTMFP*mxPE) ? 1:2;
0218   theTrack->SetWinnerProcessIndex(pid);
0219 }