Back to home page

EIC code displayed by LXR

 
 

    


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

0001 
0002 #include "G4HepEmElectronManager.hh"
0003 
0004 #include "G4HepEmData.hh"
0005 #include "G4HepEmParameters.hh"
0006 #include "G4HepEmTLData.hh"
0007 #include "G4HepEmRandomEngine.hh"
0008 
0009 #include "G4HepEmConstants.hh"
0010 #include "G4HepEmMatCutData.hh"
0011 #include "G4HepEmMaterialData.hh"
0012 #include "G4HepEmElectronData.hh"
0013 
0014 #include "G4HepEmMath.hh"
0015 
0016 #include "G4HepEmRunUtils.hh"
0017 #include "G4HepEmTrack.hh"
0018 #include "G4HepEmElectronTrack.hh"
0019 #include "G4HepEmMSCTrackData.hh"
0020 #include "G4HepEmGammaTrack.hh"
0021 #include "G4HepEmElectronInteractionIoni.hh"
0022 #include "G4HepEmElectronInteractionBrem.hh"
0023 #include "G4HepEmElectronEnergyLossFluctuation.hh"
0024 #include "G4HepEmElectronInteractionUMSC.hh"
0025 #include "G4HepEmPositronInteractionAnnihilation.hh"
0026 
0027 // tlData GetPrimaryElectronTrack needs to be set needs to be set based on the G4Track;
0028 
0029 //#define NOMSC
0030 //#define NOFLUCTUATION
0031 
0032 
0033 // Note: pStepLength will be set here i.e. this is the first access to it that
0034 //       will clear the previous step value.
0035 void G4HepEmElectronManager::HowFar(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmTLData* tlData) {
0036   G4HepEmElectronTrack* theElTrack = tlData->GetPrimaryElectronTrack();
0037   G4HepEmTrack* theTrack = theElTrack->GetTrack();
0038   // Sample the `number-of-interaction-left`
0039   for (int ip=0; ip<4; ++ip) {
0040     if (theTrack->GetNumIALeft(ip)<=0.) {
0041       theTrack->SetNumIALeft(-G4HepEmLog(tlData->GetRNGEngine()->flat()), ip);
0042     }
0043   }
0044   HowFar(hepEmData, hepEmPars, theElTrack, tlData->GetRNGEngine());
0045 }
0046 
0047 
0048 void G4HepEmElectronManager::HowFarToDiscreteInteraction(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmElectronTrack* theElTrack) {
0049   int indxWinnerProcess = -1;  // init to continous
0050   // === 1. Continuous energy loss limit
0051   double pStepLength     = kALargeValue;
0052   G4HepEmTrack* theTrack = theElTrack->GetTrack();
0053   const double   theEkin = theTrack->GetEKin();
0054   const double  theLEkin = theTrack->GetLogEKin();
0055   const int       theIMC = theTrack->GetMCIndex();
0056   const bool  isElectron = (theTrack->GetCharge() < 0.0);
0057 
0058   const G4HepEmElectronData* theElectronData = isElectron
0059                                                ? hepEmData->fTheElectronData
0060                                                : hepEmData->fThePositronData;
0061   //
0062   const double range  = GetRestRange(theElectronData, theIMC, theEkin, theLEkin);
0063   theElTrack->SetRange(range);
0064   const int indxRegion = hepEmData->fTheMatCutData->fMatCutData[theIMC].fG4RegionIndex;
0065   const double frange  = hepEmPars->fParametersPerRegion[indxRegion].fFinalRange;
0066   const double drange  = hepEmPars->fParametersPerRegion[indxRegion].fDRoverRange;
0067   pStepLength = (range > frange)
0068                 ? range*drange + frange*(1.0-drange)*(2.0-frange/range)
0069                 : range;
0070 //  std::cout << " pStepLength = " << pStepLength << " range = " << range << " frange = " << frange << std::endl;
0071   // === 2. Discrete limits due to eestricted Ioni and Brem (accounting e-loss)
0072   const int theImat = (hepEmData->fTheMatCutData->fMatCutData[theIMC]).fHepEmMatIndex;
0073   double mxSecs[4];
0074   // ioni, brem and annihilation to 2 gammas (only for e+), electron/positron nuclear
0075   mxSecs[0] = GetRestMacXSecForStepping(theElectronData, theIMC, theEkin, theLEkin, true);
0076   mxSecs[1] = GetRestMacXSecForStepping(theElectronData, theIMC, theEkin, theLEkin, false);
0077   mxSecs[2] = (isElectron)
0078               ? 0.0
0079               : ComputeMacXsecAnnihilationForStepping(theEkin, hepEmData->fTheMaterialData->fMaterialData[theImat].fElectronDensity);
0080   mxSecs[3] = GetMacXSecNuclearForStepping(theElectronData, theImat, theEkin, theLEkin);
0081   // compute mfp and see if we need to sample the `number-of-interaction-left`
0082   // before we use it to get the current discrete proposed step length
0083   for (int ip=0; ip<4; ++ip) {
0084     const double mxsec = mxSecs[ip];
0085     const double   mfp = (mxsec>0.) ? 1./mxsec : kALargeValue;
0086     // save the mac-xsec for the update of the `number-of-interaction-left`:
0087     // the `number-of-intercation-left` should be updated in the along-step-action
0088     // after the MSC has changed the step.
0089     theTrack->SetMFP(mfp, ip);
0090     // sample the proposed step length
0091     const double dStepLimit = mfp*theTrack->GetNumIALeft(ip);
0092     if (dStepLimit<pStepLength) {
0093       pStepLength = dStepLimit;
0094       indxWinnerProcess = ip;
0095     }
0096   }
0097 
0098   theElTrack->SetPStepLength(pStepLength);
0099   theTrack->SetWinnerProcessIndex(indxWinnerProcess);
0100   theTrack->SetGStepLength(pStepLength);
0101 }
0102 
0103 void G4HepEmElectronManager::HowFarToMSC(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmElectronTrack* theElTrack, G4HepEmRandomEngine* rnge) {
0104   //
0105   // Now MSC is called to see:
0106   // - if it limits the (true, i.e. physical) step length further
0107   // Then we perform the physical --> geometric step length conversion here:
0108   //  - it provides the projection of the transport vector along the original
0109   //    direction.
0110   // Note, this later also part of the MSC model and might also limit the true
0111   // step length further (see my note inside ConvertTrueToGeometricLength though).
0112   // Therefore, the check if MSC limited the step must be done after
0113   // the physical -->  geometric (i.e. true to geom.) conversion.
0114   //
0115 #ifndef NOMSC
0116   double pStepLength     = theElTrack->GetPStepLength();
0117   const double range     = theElTrack->GetRange();
0118   G4HepEmTrack* theTrack = theElTrack->GetTrack();
0119   const double   theEkin = theTrack->GetEKin();
0120   const double  theLEkin = theTrack->GetLogEKin();
0121   const int       theIMC = theTrack->GetMCIndex();
0122   const bool  isElectron = (theTrack->GetCharge() < 0.0);
0123 
0124   const G4HepEmElectronData* theElectronData = isElectron
0125                                                ? hepEmData->fTheElectronData
0126                                                : hepEmData->fThePositronData;
0127 
0128   const G4HepEmMCCData& theMatCutData = hepEmData->fTheMatCutData->fMatCutData[theIMC];
0129   const int theImat = theMatCutData.fHepEmMatIndex;
0130   const int theIreg = theMatCutData.fG4RegionIndex;
0131   G4HepEmMSCTrackData* mscData = theElTrack->GetMSCTrackData();
0132   // init some mscData for the case if we skipp calling msc due to very small step
0133   mscData->fTrueStepLength      = pStepLength;
0134   mscData->fZPathLength         = pStepLength;
0135   mscData->fIsActive            = false;
0136   mscData->SetDisplacement(0., 0., 0.);
0137   mscData->SetNewDirection(0., 0., 1.);
0138   // no msc in case of very small steps
0139   const double kGeomMinLength = 5.E-8; // 0.05 [nm]
0140   if (pStepLength > kGeomMinLength && theEkin > 1.0E-3) {
0141     mscData->fIsActive = true;
0142     // compute the fist transport mean free path
0143     mscData->fLambtr1  = GetTransportMFP(theElectronData, theImat, theEkin, theLEkin);
0144     G4HepEmElectronInteractionUMSC::StepLimit(hepEmData, hepEmPars, mscData, theEkin, theImat, theIreg, range,
0145                                               theTrack->GetSafety(), theTrack->GetOnBoundary(), isElectron, rnge);
0146     // If msc limited the true step length, then the G4HepEmMSCTrackData::fTrueStepLength member of
0147     // the input electron track is < pStepLengt. Otherwise its = pStepLengt.
0148     // Call the True --> Geometric conversion since that might limits further the true step Length:
0149     //   - convert the physical step length to geometrical one. The result will be
0150     //     written into mscData::fZPathLength.
0151     ConvertTrueToGeometricLength(hepEmData, mscData, theEkin, range, theIMC, isElectron);
0152     // check now if msc limited the step:
0153     const double mscTruStepLength = mscData->fTrueStepLength;
0154     if (mscTruStepLength < pStepLength) {
0155       // indicate continuous step limit as msc limited the step and set the new pStepLength
0156       theTrack->SetWinnerProcessIndex(-2);
0157       pStepLength = mscTruStepLength;
0158       theElTrack->SetPStepLength(pStepLength);
0159     }
0160     // set geometrical step length (protect agains wrong conversion, i.e. if gL > pL)
0161     theTrack->SetGStepLength(G4HepEmMin(mscData->fZPathLength, pStepLength));
0162   }
0163 #endif
0164 }
0165 
0166 void G4HepEmElectronManager::HowFar(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmElectronTrack* theElTrack, G4HepEmRandomEngine* rnge) {
0167   HowFarToDiscreteInteraction(hepEmData, hepEmPars, theElTrack);
0168   HowFarToMSC(hepEmData, hepEmPars, theElTrack, rnge);
0169 }
0170 
0171 void G4HepEmElectronManager::UpdatePStepLength(G4HepEmElectronTrack* theElTrack) {
0172   G4HepEmTrack*   theTrack = theElTrack->GetTrack();
0173   const double gStepLength = theTrack->GetGStepLength();
0174   double pStepLength       = gStepLength;
0175   // call MSC::ConvertGeometricToTrueLength that will provide the true (i.e. physical)
0176   // step length in the G4HepEmMSCTrackData::fTrueStepLength member.
0177   // NOTE: in case the step was NOT limited by boundary, we know the true step length since
0178   //       the particle went as far as we expected.
0179 #ifndef NOMSC
0180   const double        theRange = theElTrack->GetRange();
0181   G4HepEmMSCTrackData* mscData = theElTrack->GetMSCTrackData();
0182   if (mscData->fIsActive) {
0183     pStepLength = mscData->fTrueStepLength;
0184     // if we hit boundary or stopped before we wanted for any reasons: convert geom. -> true
0185     if (gStepLength < mscData->fZPathLength) {
0186       // the converted geom --> true step Length will be written into mscData::fTrueStepLength
0187       ConvertGeometricToTrueLength(mscData, theRange, gStepLength);
0188       // protect against wrong true --> geom --> true conevrsion: physical step
0189       // cannot be longer than before converted to geometrical
0190       pStepLength = G4HepEmMin(pStepLength, mscData->fTrueStepLength);
0191       // store the final true step length value
0192       mscData->fTrueStepLength = pStepLength;
0193     }
0194     // optimisation: do not sample msc and dispalcement in case of last (rangeing out) or short steps
0195     const double kGeomMinLength = 5.E-8; // 0.05 [nm]
0196     if (pStepLength <= kGeomMinLength || theRange <= pStepLength) {
0197       mscData->fIsActive = false;
0198     }
0199   }
0200 #endif
0201   // set the results of the geom ---> true in the primary e- etrack
0202   theElTrack->SetPStepLength(pStepLength);
0203 }
0204 
0205 void G4HepEmElectronManager::UpdateNumIALeft(G4HepEmElectronTrack* theElTrack) {
0206   const double pStepLength = theElTrack->GetPStepLength();
0207   G4HepEmTrack*   theTrack = theElTrack->GetTrack();
0208   double*    numInterALeft = theTrack->GetNumIALeft();
0209   double*       preStepMFP = theTrack->GetMFP();
0210   numInterALeft[0] -= pStepLength/preStepMFP[0];
0211   numInterALeft[1] -= pStepLength/preStepMFP[1];
0212   numInterALeft[2] -= pStepLength/preStepMFP[2];
0213   numInterALeft[3] -= pStepLength/preStepMFP[3];
0214 }
0215 
0216 bool G4HepEmElectronManager::ApplyMeanEnergyLoss(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmElectronTrack* theElTrack) {
0217   const double pStepLength = theElTrack->GetPStepLength();
0218 
0219   G4HepEmTrack* theTrack = theElTrack->GetTrack();
0220   const bool  isElectron = (theTrack->GetCharge() < 0.0);
0221   const double   theEkin = theTrack->GetEKin();
0222   const double  theRange = theElTrack->GetRange();
0223   // 0. stop tracking when reached the end (i.e. it has been ranged out by the limit)
0224   // @TODO: actually the tracking cut is around 1 keV and the min-table energy is 100 eV so the second should never
0225   //        under standard EM constructor configurations
0226   if (pStepLength >= theRange || theEkin <= hepEmPars->fMinLossTableEnergy) {
0227     // stop and deposit the remaining energy
0228     theTrack->SetEnergyDeposit(theEkin);
0229     theTrack->SetEKin(0.0);
0230     return true;
0231   }
0232   // 1. try linear energy loss approximation:
0233   const G4HepEmElectronData* elData = isElectron
0234                                       ? hepEmData->fTheElectronData
0235                                       : hepEmData->fThePositronData;
0236    // NOTE: this is the pre-step IMC !!!
0237   const int      theIMC = theTrack->GetMCIndex();
0238   const double theLEkin = theTrack->GetLogEKin();
0239   double eloss = pStepLength*GetRestDEDX(elData, theIMC, theEkin, theLEkin);
0240   // 2. use integral if linear energy loss is over the limit fraction
0241   const int indxRegion = hepEmData->fTheMatCutData->fMatCutData[theIMC].fG4RegionIndex;
0242   const double parLinELossLimit = hepEmPars->fParametersPerRegion[indxRegion].fLinELossLimit;
0243   if (eloss > theEkin*parLinELossLimit) {
0244     const double postStepRange = theRange - pStepLength;
0245     eloss = theEkin - GetInvRange(elData, theIMC, postStepRange);
0246   }
0247   eloss = G4HepEmMax(eloss, 0.0);
0248   if (eloss >= theEkin) {
0249     eloss = theEkin;
0250     theTrack->SetEKin(0);
0251     theTrack->SetEnergyDeposit(eloss);
0252     return true;
0253   }
0254   // 3. Compute the energy after the mean energy loss.
0255   double theEkinAfterMeanEloss = theEkin - eloss;
0256   theTrack->SetEKin(theEkinAfterMeanEloss);
0257   theTrack->SetEnergyDeposit(eloss);
0258   return false;
0259 }
0260 
0261 void G4HepEmElectronManager::SampleMSC(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmElectronTrack* theElTrack, G4HepEmRandomEngine* rnge) {
0262 #ifndef NOMSC
0263   const double pStepLength = theElTrack->GetPStepLength();
0264   G4HepEmTrack*   theTrack = theElTrack->GetTrack();
0265   const bool    isElectron = (theTrack->GetCharge() < 0.0);
0266   const int         theIMC = theTrack->GetMCIndex();
0267   const double preStepEkin = theElTrack->GetPreStepEKin();
0268   const double    theRange = theElTrack->GetRange();
0269 
0270   const G4HepEmElectronData* elData = isElectron
0271                                       ? hepEmData->fTheElectronData
0272                                       : hepEmData->fThePositronData;
0273 
0274   //
0275   //
0276   // Complete here the MSC part by computing the net angular deflection and dispalcement
0277   //
0278   // Smaple scattering in MSC and compute the new direction and displacement vectors (if any)
0279   // The new direction and dispalcement vectors, proposed by MSC, are given in mscData::fDirection and
0280   // mscData::fDisplacement.
0281   const double kTLimitMinfix = 1.0E-8; // 0.01 [nm] 1.0E-8 [mm]
0282   const double kTauSmall     = 1.0e-16;
0283   G4HepEmMSCTrackData* mscData = theElTrack->GetMSCTrackData();
0284   if (mscData->fIsActive && (pStepLength > G4HepEmMax(kTLimitMinfix, kTauSmall*mscData->fLambtr1))) {
0285     // only to make sure that we also use E2 = E1 under the same condition as in G4
0286     double postStepEkin  = preStepEkin;
0287     double postStepLEkin = theElTrack->GetPreStepLogEKin();
0288 #if G4VERSION_NUM >= 1100
0289     if (pStepLength > theRange*0.01)
0290 #endif
0291     {
0292       // At this point, we have only computed the mean energy loss. Query the
0293       // track and if energy loss fluctuations are *not* active, we safe a log
0294       // call in the next step.
0295       postStepEkin  = theTrack->GetEKin();
0296       postStepLEkin = theTrack->GetLogEKin();
0297     }
0298     // sample msc scattering:
0299     // - compute the fist transport mean free path at the post-step energy point
0300     const int           theImat = (hepEmData->fTheMatCutData->fMatCutData[theIMC]).fHepEmMatIndex;
0301     const double postStepTr1mfp = GetTransportMFP(elData, theImat, postStepEkin, postStepLEkin);
0302     // - sample scattering: including net angular deflection and lateral dispacement that will be
0303     //                      written into mscData::fDirection and mscData::fDisplacement
0304     const bool isPosCor   = hepEmPars->fIsMSCPositronCor;
0305     const bool isDisplace = hepEmPars->fIsMSCDisplacement;
0306     G4HepEmElectronInteractionUMSC::SampleScattering(hepEmData, mscData, pStepLength, preStepEkin, mscData->fLambtr1, postStepEkin, postStepTr1mfp,
0307                                     theImat, isElectron, isPosCor, isDisplace, rnge);
0308     // NOTE: displacement will be applied in the caller where we have access to the required Geant4 functionality
0309     //       (and if its length is longer than a small minimal length and we are not ended up on boundary)
0310     //
0311     // rotate direction and displacement vectors (if any) and update new direction of the primary
0312     if (!(mscData->fIsNoScatteringInMSC)) {
0313       RotateToReferenceFrame(mscData->fDirection, theTrack->GetDirection());
0314       if (mscData->fIsDisplace) {
0315         RotateToReferenceFrame(mscData->fDisplacement, theTrack->GetDirection());
0316       }
0317       // upadte new direction
0318       theTrack->SetDirection(mscData->fDirection);
0319     }
0320   }
0321 #endif
0322 }
0323 
0324 bool G4HepEmElectronManager::SampleLossFluctuations(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmElectronTrack* theElTrack, G4HepEmRandomEngine* rnge) {
0325   const double pStepLength = theElTrack->GetPStepLength();
0326   G4HepEmTrack*   theTrack = theElTrack->GetTrack();
0327   const bool    isElectron = (theTrack->GetCharge() < 0.0);
0328   const int         theIMC = theTrack->GetMCIndex();
0329 
0330   const double thePreStepEkin = theElTrack->GetPreStepEKin();
0331 
0332   // Previously ApplyMeanEnergyLoss computed the mean energy loss and stored the
0333   // result into the track.
0334   double finalEkin = theTrack->GetEKin();
0335   double eloss     = theTrack->GetEnergyDeposit();
0336   // sample energy loss fluctuations
0337 #ifndef NOFLUCTUATION
0338   const int iregion = hepEmData->fTheMatCutData->fMatCutData[theIMC].fG4RegionIndex;;
0339   const int isFluctuation = hepEmPars->fParametersPerRegion[iregion].fIsELossFluctuation;
0340   const double kFluctParMinEnergy  = 1.E-5; // 10 eV
0341   if (isFluctuation && eloss > kFluctParMinEnergy) {
0342     const G4HepEmMCCData& theMatCutData = hepEmData->fTheMatCutData->fMatCutData[theIMC];
0343     const double elCut   = theMatCutData.fSecElProdCutE;
0344     const int    theImat = theMatCutData.fHepEmMatIndex;
0345     const double meanExE = hepEmData->fTheMaterialData->fMaterialData[theImat].fMeanExEnergy;
0346     //
0347     const double tmax = isElectron ? 0.5*thePreStepEkin : thePreStepEkin;
0348     const double tcut = G4HepEmMin(elCut, tmax);
0349     eloss = G4HepEmElectronEnergyLossFluctuation::SampleEnergyLossFLuctuation(thePreStepEkin, tcut, tmax,
0350                                                   meanExE, pStepLength, eloss, rnge);
0351     eloss = G4HepEmMax(eloss, 0.0);
0352     // Update the final kinetic energy after loss fluctuations.
0353     finalEkin = thePreStepEkin - eloss;
0354   }
0355 #endif
0356   //
0357   // Check if the final kinetic energy drops below the tracking cut and stop.
0358   if (finalEkin <= hepEmPars->fElectronTrackingCut) {
0359     eloss     = thePreStepEkin;
0360     finalEkin = 0.0;
0361     theTrack->SetEKin(finalEkin);
0362     theTrack->SetEnergyDeposit(eloss);
0363     return true;
0364   }
0365   theTrack->SetEKin(finalEkin);
0366   theTrack->SetEnergyDeposit(eloss);
0367   return false;
0368 }
0369 
0370 // Here I can have my own transportation to be called BUT at the moment I cannot
0371 // skip the G4Transportation if I do it by myself !!!
0372 
0373 // Note: energy deposit will be set here i.e. this is the first access to it that
0374 //       will clear the previous step value.
0375 bool G4HepEmElectronManager::PerformContinuous(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmElectronTrack* theElTrack, G4HepEmRandomEngine* rnge) {
0376   theElTrack->SavePreStepEKin();
0377   //
0378   // === 1. MSC should be invoked to obtain the physics step Length
0379   UpdatePStepLength(theElTrack);
0380   const double pStepLength = theElTrack->GetPStepLength();
0381 
0382   if (pStepLength<=0.0) {
0383     return false;
0384   }
0385   // compute the energy loss first based on the new step length: it will be needed in the
0386   // MSC scatteirng and displacement computation here as well (that is done only if not
0387   // the last step with the particle).
0388   // But update the number of interaction length left before.
0389   //
0390   // === 2. The `number-of-interaction-left` needs to be updated based on the actual
0391   //        physical step Length
0392   UpdateNumIALeft(theElTrack);
0393   //
0394   // === 3. Continuous energy loss needs to be computed
0395   bool stopped = ApplyMeanEnergyLoss(hepEmData, hepEmPars, theElTrack);
0396   if (stopped) {
0397     return true;
0398   }
0399 
0400   // === 4. Sample MSC direction change and displacement.
0401   SampleMSC(hepEmData, hepEmPars, theElTrack, rnge);
0402 
0403   // === 5. Sample loss fluctuations.
0404   return SampleLossFluctuations(hepEmData, hepEmPars, theElTrack, rnge);
0405 }
0406 
0407 
0408 bool G4HepEmElectronManager::CheckDelta(struct G4HepEmData* hepEmData, G4HepEmTrack* theTrack, double rand) {
0409   const bool isElectron = (theTrack->GetCharge() < 0.0);
0410   const G4HepEmElectronData* elData = isElectron
0411                                       ? hepEmData->fTheElectronData
0412                                       : hepEmData->fThePositronData;
0413   const int iDProc      = theTrack->GetWinnerProcessIndex();
0414   const int theIMC      = theTrack->GetMCIndex();
0415   const int theMatIndex = hepEmData->fTheMatCutData->fMatCutData[theIMC].fHepEmMatIndex;
0416   const double theEkin  = theTrack->GetEKin();
0417   const double theLEkin = theTrack->GetLogEKin();
0418   const double mxsec = (iDProc<2 ? GetRestMacXSec(elData, theIMC, theEkin, theLEkin, iDProc==0) :
0419                        (iDProc<3 ? ComputeMacXsecAnnihilation(theEkin, hepEmData->fTheMaterialData->fMaterialData[theMatIndex].fElectronDensity)
0420                                  : GetMacXSecNuclear(elData, theMatIndex, theEkin, theLEkin))
0421                        );
0422   return mxsec <= 0.0 || rand > mxsec*theTrack->GetMFP(iDProc);
0423 }
0424 
0425 void G4HepEmElectronManager::PerformDiscrete(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmTLData* tlData) {
0426   G4HepEmElectronTrack* theElTrack = tlData->GetPrimaryElectronTrack();
0427   G4HepEmTrack*   theTrack = theElTrack->GetTrack();
0428   const bool isElectron = (theTrack->GetCharge() < 0.0);
0429 
0430   // 1. check if discrete process limited the step return otherwise (i.e. if
0431   //      continous or boundary process limited the step)
0432   const int iDProc = theTrack->GetWinnerProcessIndex();
0433   if (iDProc < 0 || theTrack->GetOnBoundary()) {
0434     return;
0435   }
0436   // reset number of interaction left for the winner discrete process
0437   theTrack->SetNumIALeft(-1.0, iDProc);
0438 
0439   // 2. check if delta interaction happens instead of the real discrete process
0440   if (CheckDelta(hepEmData, theTrack, tlData->GetRNGEngine()->flat())) {
0441     return;
0442   }
0443 
0444   // 3. perform the discrete part of the winner interaction
0445   const double theEkin = theTrack->GetEKin();
0446   switch (iDProc) {
0447     case 0: // invoke ioni (for e-/e+):
0448             G4HepEmElectronInteractionIoni::Perform(tlData, hepEmData, isElectron);
0449             break;
0450     case 1: // invoke brem (for e-/e+): either SB- or Rel-Brem
0451             G4HepEmElectronInteractionBrem::Perform(tlData, hepEmData, isElectron, theEkin < hepEmPars->fElectronBremModelLim);
0452             break;
0453     case 2: // invoke annihilation (in-flight) for e+
0454             G4HepEmPositronInteractionAnnihilation::Perform(tlData, false);
0455             break;
0456     case 3: // electron/positorn - nuclear interaction is not handled by HepEm: do nothing
0457             break;
0458   }
0459 }
0460 
0461 void G4HepEmElectronManager::Perform(struct G4HepEmData* hepEmData, struct G4HepEmParameters* hepEmPars, G4HepEmTLData* tlData) {
0462   G4HepEmElectronTrack* theElTrack = tlData->GetPrimaryElectronTrack();
0463   G4HepEmTrack*   theTrack = theElTrack->GetTrack();
0464   // Set default values to cover all early returns due to protection against
0465   // zero step lengths, conversion errors, etc.
0466   theTrack->SetEnergyDeposit(0);
0467   theElTrack->SetPStepLength(theTrack->GetGStepLength());
0468   const bool isElectron = (theTrack->GetCharge() < 0.0);
0469 
0470   if (theTrack->GetGStepLength()<=0.) return;
0471 
0472   bool stopped = PerformContinuous(hepEmData, hepEmPars, theElTrack, tlData->GetRNGEngine());
0473   if (stopped) {
0474     // call annihilation for e+ !!!
0475     if (!isElectron) {
0476       G4HepEmPositronInteractionAnnihilation::Perform(tlData, true);
0477     }
0478     return;
0479   }
0480 
0481   // === 4. Discrete part of the interaction (if any)
0482   PerformDiscrete(hepEmData, hepEmPars, tlData);
0483 }
0484 
0485 
0486 double  G4HepEmElectronManager::GetRestRange(const struct G4HepEmElectronData* elData, const int imc, const double ekin, const double lekin) {
0487   const int numELossData = elData->fELossEnergyGridSize;
0488   const int iRangeStarts = 5*numELossData*imc;
0489   // use the G4HepEmRunUtils function for interpolation
0490   const double     range = GetSplineLog(numELossData, elData->fELossEnergyGrid, &(elData->fELossData[iRangeStarts]), ekin, lekin, elData->fELossLogMinEkin, elData->fELossEILDelta);
0491   return G4HepEmMax(0.0, range);
0492 }
0493 
0494 
0495 double  G4HepEmElectronManager::GetRestDEDX(const struct G4HepEmElectronData* elData, const int imc, const double ekin, const double lekin) {
0496   const int numELossData = elData->fELossEnergyGridSize;
0497   const int  iDEDXStarts = numELossData*(5*imc + 2); // 5*imc*numELossData is where range-start + 2*numELossData
0498   // use the G4HepEmRunUtils function for interpolation
0499   const double      dedx = GetSplineLog(numELossData, elData->fELossEnergyGrid, &(elData->fELossData[iDEDXStarts]), ekin, lekin, elData->fELossLogMinEkin, elData->fELossEILDelta);
0500   return G4HepEmMax(0.0, dedx);
0501 }
0502 
0503 
0504 double  G4HepEmElectronManager::GetInvRange(const struct G4HepEmElectronData* elData, int imc, double range) {
0505   const int numELossData = elData->fELossEnergyGridSize;
0506   const int iRangeStarts = 5*numELossData*imc;
0507   // low-energy approximation
0508   const double minRange = elData->fELossData[iRangeStarts];
0509   if (range<minRange) {
0510     const double dum = range/minRange;
0511     return G4HepEmMax(0.0, elData->fELossEnergyGrid[0]*dum*dum);
0512   }
0513   // use the G4HepEmRunUtils function for finding the range bin index and for interpolation
0514   // find `i`, lower index of the range such that R_{i} <= r < R_{i+1}
0515   const int     iRlow = FindLowerBinIndex(&(elData->fELossData[iRangeStarts]), numELossData, range, 2);
0516   // interpolate: x,y and sd
0517   const double energy = GetSpline(&(elData->fELossData[iRangeStarts]), elData->fELossEnergyGrid, &(elData->fELossData[iRangeStarts+4*numELossData]), range, iRlow, 2);
0518   return G4HepEmMax(0.0, energy);
0519 }
0520 
0521 
0522 double  G4HepEmElectronManager::GetRestMacXSec(const struct G4HepEmElectronData* elData, const int imc, const double ekin, const double lekin, bool isioni) {
0523   const int iIoniStarts = elData->fResMacXSecStartIndexPerMatCut[imc];
0524   const int numIoniData = elData->fResMacXSecData[iIoniStarts]; // x3 for the 3 values and +5 at the beginning
0525   const int      iStart = (isioni) ? iIoniStarts : iIoniStarts + 3*numIoniData + 5;
0526   const int     numData = elData->fResMacXSecData[iStart];
0527   const double  minEKin = elData->fResMacXSecData[iStart+5];
0528   if (ekin<minEKin) {return 0.0; }
0529   // use the G4HepEmRunUtils function for interpolation
0530   const double    mxsec = GetSplineLog(numData, &(elData->fResMacXSecData[iStart+5]), ekin, lekin, elData->fResMacXSecData[iStart+3],elData->fResMacXSecData[iStart+4]);
0531   return G4HepEmMax(0.0, mxsec);
0532 }
0533 
0534 double  G4HepEmElectronManager::GetMacXSecNuclear(const struct G4HepEmElectronData* elData, const int imat, const double ekin, const double lekin) {
0535   if (ekin < elData->fENucEnergyGrid[0]) { return 0.0; }
0536   const int numEKin   = elData->fENucEnergyGridSize; // #energy point per material
0537   const int iStartMat = imat*2*numEKin; // value and second derivative at each energy point
0538   // use the G4HepEmRunUtils function for interpolation
0539   const double mxsec = GetSplineLog(numEKin, elData->fENucEnergyGrid, &(elData->fENucMacXsecData[iStartMat]), ekin, lekin, elData->fENucLogMinEkin, elData->fENucEILDelta);
0540   return G4HepEmMax(0.0, mxsec);
0541 }
0542 
0543 
0544 double  G4HepEmElectronManager::GetRestMacXSecForStepping(const struct G4HepEmElectronData* elData, const int imc, double ekin, double lekin, bool isioni) {
0545   constexpr double log08 = -0.22314355131420971;
0546   const int  iIoniStarts = elData->fResMacXSecStartIndexPerMatCut[imc];
0547   const int  numIoniData = elData->fResMacXSecData[iIoniStarts]; // x3 for the 3 values and +5 at the beginning
0548   const int       iStart = (isioni) ? iIoniStarts : iIoniStarts + 3*numIoniData + 5;
0549   const int      numData = elData->fResMacXSecData[iStart];
0550   const double mxsecMinE = elData->fResMacXSecData[iStart+5];
0551   const double mxsecMaxE = elData->fResMacXSecData[iStart+1];
0552   const double mxsecMaxV = elData->fResMacXSecData[iStart+2];
0553   if (ekin > mxsecMaxE) {
0554     // compute reduced energy: we assume that 1/lambda is higher at lower energy so we provide an overestimate
0555     const double ekinReduced = 0.8 * ekin;
0556     if (ekinReduced < mxsecMaxE) {
0557       return G4HepEmMax(0.0, mxsecMaxV);
0558     } else {
0559       // otherwise we are still on the right side of the maximum so provide 1/lambda at this reduced energy
0560       ekin   = ekinReduced;
0561       lekin += log08;
0562     }
0563   }
0564   if (ekin<mxsecMinE) {return 0.0; }
0565   // use the G4HepEmRunUtils function for interpolation
0566   const double mxsec = GetSplineLog(numData, &(elData->fResMacXSecData[iStart+5]), ekin, lekin, elData->fResMacXSecData[iStart+3], elData->fResMacXSecData[iStart+4]);
0567   return G4HepEmMax(0.0, mxsec);
0568 }
0569 
0570 double  G4HepEmElectronManager::GetMacXSecNuclearForStepping(const struct G4HepEmElectronData* elData, const int imat, const double ekin, const double lekin) {
0571   // assuming increasing macroscopic cross section with increasing energy
0572   return GetMacXSecNuclear(elData, imat, ekin, lekin);
0573 }
0574 
0575 
0576 double  G4HepEmElectronManager::GetTransportMFP(const struct G4HepEmElectronData* elData, const int im, const double ekin, const double lekin) {
0577   const int numEkin = elData->fELossEnergyGridSize;
0578   const int iStarts = 2*numEkin*im;
0579   // use the G4HepEmRunUtils function for interpolation
0580   const double tr1mxsec = G4HepEmMax(0.0, GetSplineLog(numEkin, elData->fELossEnergyGrid, &(elData->fTr1MacXSecData[iStarts]), ekin, lekin, elData->fELossLogMinEkin, elData->fELossEILDelta));
0581   return tr1mxsec > 0. ? 1./tr1mxsec : kALargeValue;
0582 }
0583 
0584 
0585 double G4HepEmElectronManager::ComputeMacXsecAnnihilation(const double ekin, const double electronDensity) {
0586   // Heitler model for e+e- -> 2g annihilation
0587   const double tau   = ekin*kInvElectronMassC2;
0588   const double gam   = tau + 1.0;
0589   const double gam2  = gam*gam;
0590   const double bg2   = tau * (tau+2.0);
0591   const double bg    = std::sqrt(bg2);
0592   return electronDensity*kPir02*((gam2+4.*gam+1.)*G4HepEmLog(gam+bg) - (gam+3.)*bg) / (bg2*(gam+1.));
0593 }
0594 
0595 
0596 double G4HepEmElectronManager::ComputeMacXsecAnnihilationForStepping(const double ekin, const double electronDensity) {
0597   // compute mxsec for the reduced energy (assuming that the mac-xsec decreasing with ekin)
0598   return ComputeMacXsecAnnihilation(0.8*ekin, electronDensity);
0599 }
0600 
0601 
0602 void G4HepEmElectronManager::ConvertTrueToGeometricLength(const G4HepEmData* hepEmData, G4HepEmMSCTrackData* mscData,
0603                                                          double ekin, double range, int imc, bool iselectron) {
0604   mscData->fPar1 = -1.;
0605   mscData->fPar2 =  0.;
0606   mscData->fPar3 =  0.;
0607   // NOTE: in theory, the continuous e-loss limits the step length such its at most the range.
0608   //       So this line below should never have any effects! Since this is the only part that
0609   //       might limits the true step length in the true --> geom conversion, we might drop this
0610   //       that makes possible the decision if MSC limited the step even before this conversion.
0611   mscData->fTrueStepLength = G4HepEmMin(mscData->fTrueStepLength, range);
0612   //  do the true -> geom transformation
0613   mscData->fZPathLength = mscData->fTrueStepLength;
0614   // z = t for very small true-path-length
0615   const double kTlimitMinfix2 = 1.0E-6; // 1 [nm]
0616   if (mscData->fTrueStepLength < kTlimitMinfix2) {
0617     return;
0618   }
0619   //
0620   const double kTauSmall  = 1.0e-16;
0621   const double kDtrl      = 0.05;
0622   const double tau        = mscData->fTrueStepLength / mscData->fLambtr1;
0623   if (tau < kTauSmall) {
0624     mscData->fZPathLength = G4HepEmMin(mscData->fTrueStepLength, mscData->fLambtr1);
0625   } else if (mscData->fTrueStepLength < range * kDtrl) {
0626     const double kTauLim  = 1.0e-6;
0627     mscData->fZPathLength = (tau < kTauLim)
0628                             ? mscData->fTrueStepLength * (1. - 0.5 * tau)
0629                             : mscData->fLambtr1 * (1. - G4HepEmExp(-tau));
0630   } else if (ekin < kElectronMassC2 || mscData->fTrueStepLength == range) {
0631     mscData->fPar1        = 1. / range;
0632     mscData->fPar2        = 1. / (mscData->fPar1 * mscData->fLambtr1);
0633     mscData->fPar3        = 1. + mscData->fPar2;
0634     mscData->fZPathLength = 1. / (mscData->fPar1 * mscData->fPar3);
0635     if (mscData->fTrueStepLength < range) {
0636       mscData->fZPathLength *= (1. - G4HepEmPow(1. - mscData->fTrueStepLength/range, mscData->fPar3));
0637     }
0638   } else {
0639     const double rfin     = G4HepEmMax(range - mscData->fTrueStepLength, 0.01 * range);
0640     const G4HepEmElectronData* elData = iselectron ? hepEmData->fTheElectronData : hepEmData->fThePositronData;
0641     const double t1       = GetInvRange(elData, imc, rfin);
0642     const int    imat     = (hepEmData->fTheMatCutData->fMatCutData[imc]).fHepEmMatIndex;
0643     const double lambda1  = GetTransportMFP(elData, imat, t1, G4HepEmLog(t1));
0644     mscData->fPar1        = (mscData->fLambtr1 - lambda1) / (mscData->fLambtr1 * mscData->fTrueStepLength); // alpha
0645     mscData->fPar2        = 1. / (mscData->fPar1 * mscData->fLambtr1);
0646     mscData->fPar3        = 1. + mscData->fPar2;
0647     mscData->fZPathLength = (1. - G4HepEmPow(lambda1/mscData->fLambtr1, mscData->fPar3))/(mscData->fPar1 * mscData->fPar3);
0648   }
0649   mscData->fZPathLength = G4HepEmMin(mscData->fZPathLength, mscData->fLambtr1);
0650 }
0651 
0652 
0653 void G4HepEmElectronManager::ConvertGeometricToTrueLength(G4HepEmMSCTrackData* mscData, double range, double gStepToConvert) {
0654   // step was not defined by transportation: i.e. physics so we know everything,
0655   // i.e. fTrueStepLength is known because the particle went as far as we expected
0656   // NOTE: this is done directly now in the caller
0657 //  if (!onBoundary) {
0658 //    return;
0659 //  }
0660 //   return;
0661   // else: convert geom -> true by using the mean value
0662   // get the geometrical step length
0663   mscData->fZPathLength = gStepToConvert;
0664   // t = z for very small step
0665   const double kTLimitMinfix2 = 1.0E-6; // 1 [nm]
0666   if (gStepToConvert < kTLimitMinfix2) {
0667     mscData->fTrueStepLength = gStepToConvert;
0668   } else {
0669     // recalculation
0670     const double kTauSmall = 1.0e-16;
0671     double tlength = gStepToConvert;
0672     if (gStepToConvert > mscData->fLambtr1 * kTauSmall) {
0673       if (mscData->fPar1 < 0.) {
0674         tlength = -mscData->fLambtr1 * G4HepEmLog(1. - gStepToConvert / mscData->fLambtr1);
0675       } else {
0676         const double dum = mscData->fPar1 * mscData->fPar3 * gStepToConvert;
0677         if (dum < 1.) {
0678           tlength = (1. - G4HepEmPow(1.-dum, 1./mscData->fPar3)) / mscData->fPar1;
0679         } else {
0680           tlength = range;
0681         }
0682       }
0683       if (tlength < gStepToConvert) {
0684         tlength = gStepToConvert;
0685       }
0686     }
0687     // we make sure in the caller that the results of g->t conversion is not longer
0688     // than the original true step length
0689     mscData->fTrueStepLength = tlength;
0690   }
0691 }