Geant4 11.1.1
Toolkit for the simulation of the passage of particles through matter
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
G4PairProductionRelModel.cc
Go to the documentation of this file.
1//
2// ********************************************************************
3// * License and Disclaimer *
4// * *
5// * The Geant4 software is copyright of the Copyright Holders of *
6// * the Geant4 Collaboration. It is provided under the terms and *
7// * conditions of the Geant4 Software License, included in the file *
8// * LICENSE and available at http://cern.ch/geant4/license . These *
9// * include a list of copyright holders. *
10// * *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work make any representation or warranty, express or implied, *
14// * regarding this software system or assume any liability for its *
15// * use. Please see the license in the file LICENSE and URL above *
16// * for the full disclaimer and the limitation of liability. *
17// * *
18// * This code implementation is the result of the scientific and *
19// * technical work of the GEANT4 collaboration. *
20// * By using, copying, modifying or distributing the software (or *
21// * any work based on the software) you agree to acknowledge its *
22// * use in resulting scientific publications, and indicate your *
23// * acceptance of all terms of the Geant4 Software license. *
24// ********************************************************************
25//
26//
27// -------------------------------------------------------------------
28//
29// GEANT4 Class file
30//
31//
32// File name: G4PairProductionRelModel
33//
34// Author: Andreas Schaelicke
35//
36// Creation date: 02.04.2009
37//
38// Modifications:
39// 20.03.17 Change LPMconstant such that it gives suppression variable 's'
40// that consistent to Migdal's one; fix a small bug in 'logTS1'
41// computation; suppression is consistent now with the one in the
42// brem. model (F.Hariri)
43// 28-05-18 New version with improved screening function approximation, improved
44// LPM function approximation, efficiency, documentation and cleanup.
45// Corrected call to selecting target atom in the final state sampling.
46// (M. Novak)
47//
48// Class Description:
49//
50// Main References:
51// J.W.Motz et.al., Rev. Mod. Phys. 41 (1969) 581.
52// S.Klein, Rev. Mod. Phys. 71 (1999) 1501.
53// T.Stanev et.al., Phys. Rev. D25 (1982) 1291.
54// M.L.Ter-Mikaelian, High-energy Electromagnetic Processes in Condensed Media,
55// Wiley, 1972.
56//
57// -------------------------------------------------------------------
58
61#include "G4SystemOfUnits.hh"
62#include "G4Gamma.hh"
63#include "G4Electron.hh"
64#include "G4Positron.hh"
66#include "G4LossTableManager.hh"
67#include "G4ModifiedTsai.hh"
68#include "G4Exp.hh"
69#include "G4Pow.hh"
70
72
73// LPM constant: \alpha(mc^2)^2/(4\pi*\hbar c)
75 CLHEP::fine_structure_const*CLHEP::electron_mass_c2*CLHEP::electron_mass_c2
76 /(4.*CLHEP::pi*CLHEP::hbarc);
77
78// abscissas and weights of an 8 point Gauss-Legendre quadrature
79// for numerical integration on [0,1]
81 1.98550718e-02, 1.01666761e-01, 2.37233795e-01, 4.08282679e-01,
82 5.91717321e-01, 7.62766205e-01, 8.98333239e-01, 9.80144928e-01
83};
85 5.06142681e-02, 1.11190517e-01, 1.56853323e-01, 1.81341892e-01,
86 1.81341892e-01, 1.56853323e-01, 1.11190517e-01, 5.06142681e-02
87};
88
89// elastic and inelatic radiation logarithms for light elements (where the
90// Thomas-Fermi model doesn't work): computed by using Dirac-Fock model of atom.
92 0.0, 5.3104, 4.7935, 4.7402, 4.7112, 4.6694, 4.6134, 4.5520
93};
95 0.0, 5.9173, 5.6125, 5.5377, 5.4728, 5.4174, 5.3688, 5.3236
96};
97
98// constant cross section factor
100 4.*CLHEP::fine_structure_const*CLHEP::classic_electr_radius
101 *CLHEP::classic_electr_radius;
102
103// gamma energy limit above which LPM suppression will be applied (if the
104// fIsUseLPMCorrection flag is true)
106
107// special data structure per element i.e. per Z
108std::vector<G4PairProductionRelModel::ElementData*> G4PairProductionRelModel::gElementData;
109
110// LPM supression functions evaluated at initialisation time
111G4PairProductionRelModel::LPMFuncs G4PairProductionRelModel::gLPMFuncs;
112
113// CTR
115 const G4String& nam)
116 : G4VEmModel(nam), fIsUseLPMCorrection(true), fIsUseCompleteScreening(false),
117 fLPMEnergy(0.), fG4Calc(G4Pow::GetInstance()), fTheGamma(G4Gamma::Gamma()),
118 fTheElectron(G4Electron::Electron()), fThePositron(G4Positron::Positron()),
119 fParticleChange(nullptr)
120{
121 // gamma energy below which the parametrized atomic x-section is used (80 GeV)
122 fParametrizedXSectionThreshold = 30.0*CLHEP::GeV;
123 // gamma energy below the Coulomb correction is turned off (50 MeV)
124 fCoulombCorrectionThreshold = 50.0*CLHEP::MeV;
125 // set angular generator used in the final state kinematics computation
127}
128
129// DTR
131{
132 if (IsMaster()) {
133 // clear ElementData container
134 for (std::size_t iz = 0; iz < gElementData.size(); ++iz) {
135 if (gElementData[iz]) delete gElementData[iz];
136 }
137 gElementData.clear();
138 // clear LPMFunctions (if any)
140 gLPMFuncs.fLPMFuncG.clear();
141 gLPMFuncs.fLPMFuncPhi.clear();
142 gLPMFuncs.fIsInitialized = false;
143 }
144 }
145}
146
148 const G4DataVector& cuts)
149{
150 if (IsMaster()) {
151 // init element data and LPM funcs
152 if (IsMaster()) {
153 InitialiseElementData();
155 InitLPMFunctions();
156 }
157 }
158 }
162 }
163}
164
166 G4VEmModel* masterModel)
167{
170 }
171}
172
174 G4double Z)
175{
176 G4double xSection = 0.0;
177 // check if LPM suppression needs to be used
178 const G4bool isLPM = (fIsUseLPMCorrection && gammaEnergy>gEgLPMActivation);
179 // determine the kinematical limits (taken into account the correction due to
180 // the way in which the Coulomb correction is applied i.e. avoid negative DCS)
181 const G4int iz = std::min(gMaxZet, G4lrint(Z));
182 const G4double eps0 = CLHEP::electron_mass_c2/gammaEnergy;
183 // Coulomb correction is always included in the DCS even below 50 MeV (note:
184 // that this DCS is only used to get the integrated x-section)
185 const G4double dmax = gElementData[iz]->fDeltaMaxHigh;
186 const G4double dmin = 4.*eps0*gElementData[iz]->fDeltaFactor;
187 const G4double eps1 = 0.5 - 0.5*std::sqrt(1.-dmin/dmax);
188 const G4double epsMin = std::max(eps0, eps1);
189 const G4double epsMax = 0.5; // DCS is symmetric around eps=0.5
190 // let Et be the total energy transferred to the e- or to the e+
191 // the [Et-min, Et-max] interval will be divided into i=1,2,..,n subintervals
192 // with width of dInterv = (Et-max - Et-min)/n and numerical integration will
193 // be done in each sub-inteval using the xi = (Et - Et_i-min)/dInterv variable
194 // that is in [0,1]. The 8-point GL q. is used for the integration on [0,1].
195 const G4int numSub = 2;
196 const G4double dInterv= (epsMax - epsMin)*gammaEnergy/G4double(numSub);
197 G4double minEti = epsMin*gammaEnergy; // Et-min i.e. Et_0-min
198 for (G4int i = 0; i < numSub; ++i) {
199 for (G4int ngl = 0; ngl < 8; ++ngl) {
200 const G4double Et = (minEti + gXGL[ngl]*dInterv);
201 const G4double xs = isLPM ? ComputeRelDXSectionPerAtom(Et, gammaEnergy, Z)
202 : ComputeDXSectionPerAtom(Et, gammaEnergy, Z);
203 xSection += gWGL[ngl]*xs;
204 }
205 // update minimum Et of the sub-inteval
206 minEti += dInterv;
207 }
208 // apply corrections of variable transformation and half interval integration
209 xSection = std::max(2.*xSection*dInterv, 0.);
210 return xSection;
211}
212
213// DCS WITHOUT LPM SUPPRESSION
214// Computes DCS value for a given target element (Z), initial gamma energy (Eg),
215// total energy transferred to one of the e-/e+ pair(Et) WITHOUT LPM suppression
216// The constant factor 4 \alpha r_0^2 Z (Z +\eta(Z)) is not included here and
217// the returned value will be differential in total energy transfer instead of
218// the eps=Et/Eg. The computed part of the DCS
219// NORMAL CASE: DEFAULT STTING (i.e. fIsUseCompleteScreening = FALSE)
220// ds/deps(Et,Eg,Z) = ds/deps(eps,Z) = (eps^2+(1-eps)^2)*[phi1(d)/4-ln(Z)/3-fc]
221// + 2*eps(1-eps)*[phi2(d)/4-ln(Z)/3-fc]/3 where the universal (in the TF model)
222// screening variable d=d(eps)=136Z^(-1/3)eps0/[eps*(1-eps)] with eps0=mc^2/Eg.
223// COMPLETE SCREENING (when d(eps) approx-equal-to 0) : NEED TO BE SET BY USER
224// ds/deps(Et,Eg,Z) = ds/deps(eps,Z) = (eps^2+(1-eps)^2+eps*(1-eps)/3)*[Lel-fc]
225// -eps(1-eps)/9 where Lel=phi1(0)/4-ln(Z)/3 is the elastic(coherent) radiation
226// logarithm, fc is the Coulomb correction and the relation phi2(0)/4-ln(Z)/3 =
227// phi1(0)/4-1/6-ln(Z)/3 = Lel-1/6 (due to phi2(0)=phi1(0)-2/3) was used.
229 G4double gammaEnergy,
230 G4double Z)
231{
232 G4double xSection = 0.;
233 const G4int iz = std::min(gMaxZet, G4lrint(Z));
234 const G4double eps = pEnergy/gammaEnergy;
235 const G4double epsm = 1.-eps;
236 const G4double dum = eps*epsm;
238 // complete screening:
239 const G4double Lel = gElementData[iz]->fLradEl;
240 const G4double fc = gElementData[iz]->fCoulomb;
241 xSection = (eps*eps + epsm*epsm + 2.*dum/3.)*(Lel-fc) - dum/9.;
242 } else {
243 // normal case:
244 const G4double eps0 = CLHEP::electron_mass_c2/gammaEnergy;
245 const G4double fc = gElementData[iz]->fCoulomb;
246 const G4double lnZ13 = gElementData[iz]->fLogZ13;
247 const G4double delta = gElementData[iz]->fDeltaFactor*eps0/dum;
248 G4double phi1, phi2;
249 ComputePhi12(delta, phi1, phi2);
250 xSection = (eps*eps + epsm*epsm)*(0.25*phi1-lnZ13-fc)
251 + 2.*dum*(0.25*phi2-lnZ13-fc)/3.;
252 }
253 // non-const. part of the DCS differential in total energy transfer not in eps
254 // ds/dEt=ds/deps deps/dEt with deps/dEt=1/Eg
255 return std::max(xSection, 0.0)/gammaEnergy;
256}
257
258// DCS WITH POSSIBLE LPM SUPPRESSION
259// Computes DCS value for a given target element (Z), initial gamma energy (Eg),
260// total energy transferred to one of the e-/e+ pair(Et) WITH LPM suppression.
261// For a given Z, the LPM suppression will depend on the material through the
262// LMP-Energy. This will determine the suppression variable s and the LPM sup-
263// pression functions xi(s), fi(s) and G(s).
264// The constant factor 4 \alpha r_0^2 Z (Z +\eta(Z)) is not included here and
265// the returned value will be differential in total energy transfer instead of
266// the eps=Et/Eg. The computed part of the DCS
267// NORMAL CASE: DEFAULT STTING (i.e. fIsUseCompleteScreening = FALSE)
268// ds/deps(Et,Eg,Z)=ds/deps(eps,Z) = xi(s)*{ (eps^2+(1-eps)^2)*[2fi(s)/3+G(s)/3]
269// *[phi1(d)/4-ln(Z)/3-fc] + 2*eps(1-eps)*G(s)*[phi2(d)/4-ln(Z)/3-fc]/3 } where
270// the universal (in the TF model) screening variable d=d(eps)=136Z^(-1/3)eps0
271// /[eps*(1-eps)] with eps0=mc^2/Eg.
272// COMPLETE SCREENING (when d(eps) approx-equal-to 0) : NEED TO BE SET BY USER
273// ds/deps(Et,Eg,Z) = ds/deps(eps,Z) = xi(s)*{ [Lel-fc]*[ (eps^2+(1-eps)^2+eps
274// *(1-eps)/3)*2fi(s)/3 + G(s)/3] - eps(1-eps)*G(s)/9 }
275// Note, that when the LPM suppression is absent i.e. xi(s)=fi(s)=G(s)=1, both
276// the normal and the complete screening DCS give back the NO-LMP case above.
278 G4double gammaEnergy,
279 G4double Z)
280{
281 G4double xSection = 0.;
282 const G4int iz = std::min(gMaxZet, G4lrint(Z));
283 const G4double eps = pEnergy/gammaEnergy;
284 const G4double epsm = 1.-eps;
285 const G4double dum = eps*epsm;
286 // evaluate LPM suppression functions
287 G4double fXiS, fGS, fPhiS;
288 ComputeLPMfunctions(fXiS, fGS, fPhiS, eps, gammaEnergy, iz);
290 // complete screening:
291 const G4double Lel = gElementData[iz]->fLradEl;
292 const G4double fc = gElementData[iz]->fCoulomb;
293 xSection = (Lel-fc)*((eps*eps+epsm*epsm)*2.*fPhiS + fGS)/3. - dum*fGS/9.;
294 } else {
295 // normal case:
296 const G4double eps0 = CLHEP::electron_mass_c2/gammaEnergy;
297 const G4double fc = gElementData[iz]->fCoulomb;
298 const G4double lnZ13 = gElementData[iz]->fLogZ13;
299 const G4double delta = gElementData[iz]->fDeltaFactor*eps0/dum;
300 G4double phi1, phi2;
301 ComputePhi12(delta, phi1, phi2);
302 xSection = (eps*eps + epsm*epsm)*(2.*fPhiS+fGS)*(0.25*phi1-lnZ13-fc)/3.
303 + 2.*dum*fGS*(0.25*phi2-lnZ13-fc)/3.;
304 }
305 // non-const. part of the DCS differential in total energy transfer not in eps
306 // ds/dEt=ds/deps deps/dEt with deps/dEt=1/Eg
307 return std::max(fXiS*xSection, 0.0)/gammaEnergy;
308}
309
313{
314 G4double crossSection = 0.0 ;
315 // check kinematical limit
316 if ( gammaEnergy <= 2.0*electron_mass_c2 ) { return crossSection; }
317 // compute the atomic cross section either by using x-section parametrization
318 // or by numerically integrationg the DCS (with or without LPM)
319 if ( gammaEnergy < fParametrizedXSectionThreshold) {
320 // using the parametrized cross sections (max up to 80 GeV)
321 crossSection = ComputeParametrizedXSectionPerAtom(gammaEnergy, Z);
322 } else {
323 // by numerical integration of the DCS:
324 // Computes the cross section with or without LPM suppression depending on
325 // settings (by default with if the gamma energy is above a given threshold)
326 // and using or not using complete sreening approximation (by default not).
327 // Only the dependent part is computed in the numerical integration of the DCS
328 // i.e. the result must be multiplied here with 4 \alpha r_0^2 Z(Z+\eta(Z))
329 crossSection = ComputeXSectionPerAtom(gammaEnergy, Z);
330 // apply the constant factors:
331 // - eta(Z) is a correction to account interaction in the field of e-
332 // - gXSecFactor = 4 \alpha r_0^2
333 const G4int iz = std::min(gMaxZet, G4lrint(Z));
334 const G4double eta = gElementData[iz]->fEtaValue;
335 crossSection *= gXSecFactor*Z*(Z+eta);
336 }
337 // final protection
338 return std::max(crossSection, 0.);
339}
340
342 const G4Material* mat, G4double)
343{
345}
346
347void
348G4PairProductionRelModel::SampleSecondaries(std::vector<G4DynamicParticle*>* fvect,
349 const G4MaterialCutsCouple* couple,
350 const G4DynamicParticle* aDynamicGamma,
351 G4double,
352 G4double)
353// The secondaries e+e- energies are sampled using the Bethe - Heitler
354// cross sections with Coulomb correction.
355// A modified version of the random number techniques of Butcher & Messel
356// is used (Nuc Phys 20(1960),15).
357//
358// GEANT4 internal units.
359//
360// Note 1 : Effects due to the breakdown of the Born approximation at
361// low energy are ignored.
362// Note 2 : The differential cross section implicitly takes account of
363// pair creation in both nuclear and atomic electron fields.
364// However triplet prodution is not generated.
365{
366 const G4Material* mat = couple->GetMaterial();
367 const G4double gammaEnergy = aDynamicGamma->GetKineticEnergy();
368 const G4double eps0 = CLHEP::electron_mass_c2/gammaEnergy ;
369 //
370 // check kinematical limit: gamma energy(Eg) must be at least 2 e- rest mass
371 // (but the model should be used at higher energies above 100 MeV)
372 if (eps0 > 0.5) { return; }
373 //
374 // select target atom of the material
375 const G4Element* anElement = SelectTargetAtom(couple, fTheGamma, gammaEnergy,
376 aDynamicGamma->GetLogKineticEnergy());
377 CLHEP::HepRandomEngine* rndmEngine = G4Random::getTheEngine();
378 //
379 // 'eps' is the total energy transferred to one of the e-/e+ pair in initial
380 // gamma energy units Eg. Since the corresponding DCS is symmetric on eps=0.5,
381 // the kinematical limits for eps0=mc^2/Eg <= eps <= 0.5
382 // 1. 'eps' is sampled uniformly on the [eps0, 0.5] inteval if Eg<Egsmall
383 // 2. otherwise, on the [eps_min, 0.5] interval according to the DCS (case 2.)
384 G4double eps;
385 // case 1.
386 static const G4double Egsmall = 2.*CLHEP::MeV;
387 if (gammaEnergy < Egsmall) {
388 eps = eps0 + (0.5-eps0)*rndmEngine->flat();
389 } else {
390 // case 2.
391 // get the Coulomb factor for the target element (Z) and gamma energy (Eg)
392 // F(Z) = 8*ln(Z)/3 if Eg <= 50 [MeV] => no Coulomb correction
393 // F(Z) = 8*ln(Z)/3 + 8*fc(Z) if Eg > 50 [MeV] => fc(Z) is the Coulomb cor.
394 //
395 // The screening variable 'delta(eps)' = 136*Z^{-1/3}*eps0/[eps(1-eps)]
396 // Due to the Coulomb correction, the DCS can go below zero even at
397 // kinematicaly allowed eps > eps0 values. In order to exclude this eps
398 // range with negative DCS, the minimum eps value will be set to eps_min =
399 // max[eps0, epsp] with epsp is the solution of SF(delta(epsp)) - F(Z)/2 = 0
400 // with SF being the screening function (SF1=SF2 at high value of delta).
401 // The solution is epsp = 0.5 - 0.5*sqrt[ 1 - 4*136*Z^{-1/3}eps0/deltap]
402 // with deltap = Exp[(42.038-F(Z))/8.29]-0.958. So the limits are:
403 // - when eps=eps_max = 0.5 => delta_min = 136*Z^{-1/3}*eps0/4
404 // - epsp = 0.5 - 0.5*sqrt[ 1 - delta_min/deltap]
405 // - and eps_min = max[eps0, epsp]
406 const G4int iZet = std::min(gMaxZet, anElement->GetZasInt());
407 const G4double deltaFactor = gElementData[iZet]->fDeltaFactor*eps0;
408 const G4double deltaMin = 4.*deltaFactor;
409 G4double deltaMax = gElementData[iZet]->fDeltaMaxLow;
410 G4double FZ = 8.*gElementData[iZet]->fLogZ13;
411 if ( gammaEnergy > fCoulombCorrectionThreshold ) { // Eg > 50 MeV ?
412 FZ += 8.*gElementData[iZet]->fCoulomb;
413 deltaMax = gElementData[iZet]->fDeltaMaxHigh;
414 }
415 // compute the limits of eps
416 const G4double epsp = 0.5 - 0.5*std::sqrt(1. - deltaMin/deltaMax) ;
417 const G4double epsMin = std::max(eps0,epsp);
418 const G4double epsRange = 0.5 - epsMin;
419 //
420 // sample the energy rate (eps) of the created electron (or positron)
422 ScreenFunction12(deltaMin, F10, F20);
423 F10 -= FZ;
424 F20 -= FZ;
425 const G4double NormF1 = std::max(F10 * epsRange * epsRange, 0.);
426 const G4double NormF2 = std::max(1.5 * F20 , 0.);
427 const G4double NormCond = NormF1/(NormF1 + NormF2);
428 // check if LPM correction is active
429 const G4bool isLPM = (fIsUseLPMCorrection && gammaEnergy>gEgLPMActivation);
431 // we will need 3 uniform random number for each trial of sampling
432 G4double rndmv[3];
433 G4double greject = 0.;
434 do {
435 rndmEngine->flatArray(3, rndmv);
436 if (NormCond > rndmv[0]) {
437 eps = 0.5 - epsRange * fG4Calc->A13(rndmv[1]);
438 const G4double delta = deltaFactor/(eps*(1.-eps));
439 if (isLPM) {
440 G4double lpmXiS, lpmGS, lpmPhiS, phi1, phi2;
441 ComputePhi12(delta, phi1, phi2);
442 ComputeLPMfunctions(lpmXiS, lpmGS, lpmPhiS, eps, gammaEnergy, iZet);
443 greject = lpmXiS*((2.*lpmPhiS+lpmGS)*phi1-lpmGS*phi2-lpmPhiS*FZ)/F10;
444 } else {
445 greject = (ScreenFunction1(delta)-FZ)/F10;
446 }
447 } else {
448 eps = epsMin + epsRange*rndmv[1];
449 const G4double delta = deltaFactor/(eps*(1.-eps));
450 if (isLPM) {
451 G4double lpmXiS, lpmGS, lpmPhiS, phi1, phi2;
452 ComputePhi12(delta, phi1, phi2);
453 ComputeLPMfunctions(lpmXiS, lpmGS, lpmPhiS, eps, gammaEnergy, iZet);
454 greject = lpmXiS*( (lpmPhiS+0.5*lpmGS)*phi1 + 0.5*lpmGS*phi2
455 -0.5*(lpmGS+lpmPhiS)*FZ )/F20;
456 } else {
457 greject = (ScreenFunction2(delta)-FZ)/F20;
458 }
459 }
460 // Loop checking, 03-Aug-2015, Vladimir Ivanchenko
461 } while (greject < rndmv[2]);
462 // end of eps sampling
463 }
464 //
465 // select charges randomly
466 G4double eTotEnergy, pTotEnergy;
467 if (rndmEngine->flat() > 0.5) {
468 eTotEnergy = (1.-eps)*gammaEnergy;
469 pTotEnergy = eps*gammaEnergy;
470 } else {
471 pTotEnergy = (1.-eps)*gammaEnergy;
472 eTotEnergy = eps*gammaEnergy;
473 }
474 //
475 // sample pair kinematics
476 //
477 const G4double eKinEnergy = std::max(0.,eTotEnergy - CLHEP::electron_mass_c2);
478 const G4double pKinEnergy = std::max(0.,pTotEnergy - CLHEP::electron_mass_c2);
479 //
480 G4ThreeVector eDirection, pDirection;
481 //
483 eKinEnergy, pKinEnergy, eDirection, pDirection);
484 // create G4DynamicParticle object for the particle1
485 auto aParticle1 = new G4DynamicParticle(fTheElectron,eDirection,eKinEnergy);
486
487 // create G4DynamicParticle object for the particle2
488 auto aParticle2 = new G4DynamicParticle(fThePositron,pDirection,pKinEnergy);
489 // Fill output vector
490 fvect->push_back(aParticle1);
491 fvect->push_back(aParticle2);
492 // kill incident photon
495}
496
497// should be called only by the master and at initialisation
498void G4PairProductionRelModel::InitialiseElementData()
499{
500 G4int size = (G4int)gElementData.size();
501 if (size < gMaxZet+1) {
502 gElementData.resize(gMaxZet+1, nullptr);
503 }
504 // create for all elements that are in the detector
505 const G4ElementTable* elemTable = G4Element::GetElementTable();
506 std::size_t numElems = (*elemTable).size();
507 for (std::size_t ie = 0; ie < numElems; ++ie) {
508 const G4Element* elem = (*elemTable)[ie];
509 const G4int iz = std::min(gMaxZet, elem->GetZasInt());
510 if (!gElementData[iz]) { // create it if doesn't exist yet
511 const G4double logZ13 = elem->GetIonisation()->GetlogZ3();
512 const G4double Z13 = elem->GetIonisation()->GetZ3();
513 const G4double fc = elem->GetfCoulomb();
514 const G4double FZLow = 8.*logZ13;
515 const G4double FZHigh = 8.*(logZ13 + fc);
516 G4double Fel;
517 G4double Finel;
518 if (iz<5) { // use data from Dirac-Fock atomic model
519 Fel = gFelLowZet[iz];
520 Finel = gFinelLowZet[iz];
521 } else { // use the results of the Thomas-Fermi-Moliere model
522 Fel = G4Log(184.) - logZ13;
523 Finel = G4Log(1194.) - 2.*logZ13;
524 }
525 auto elD = new ElementData();
526 elD->fLogZ13 = logZ13;
527 elD->fCoulomb = fc;
528 elD->fLradEl = Fel;
529 elD->fDeltaFactor = 136./Z13;
530 elD->fDeltaMaxLow = G4Exp((42.038 - FZLow)/8.29) - 0.958;
531 elD->fDeltaMaxHigh = G4Exp((42.038 - FZHigh)/8.29) - 0.958;
532 elD->fEtaValue = Finel/(Fel-fc);
533 elD->fLPMVarS1Cond = std::sqrt(2.)*Z13*Z13/(184.*184.);
534 elD->fLPMILVarS1Cond = 1./G4Log(elD->fLPMVarS1Cond);
535 gElementData[iz] = elD;
536 }
537 }
538}
539
540// s goes up to 2 with ds = 0.01 be default
541void G4PairProductionRelModel::InitLPMFunctions() {
542 if (!gLPMFuncs.fIsInitialized) {
543 const G4int num = gLPMFuncs.fSLimit*gLPMFuncs.fISDelta+1;
544 gLPMFuncs.fLPMFuncG.resize(num);
545 gLPMFuncs.fLPMFuncPhi.resize(num);
546 for (G4int i=0; i<num; ++i) {
547 const G4double sval = i/gLPMFuncs.fISDelta;
548 ComputeLPMGsPhis(gLPMFuncs.fLPMFuncG[i],gLPMFuncs.fLPMFuncPhi[i],sval);
549 }
550 gLPMFuncs.fIsInitialized = true;
551 }
552}
553
554// used only at initialisation time
555void G4PairProductionRelModel::ComputeLPMGsPhis(G4double &funcGS, G4double &funcPhiS, const G4double varShat) {
556 if (varShat < 0.01) {
557 funcPhiS = 6.0*varShat*(1.0-CLHEP::pi*varShat);
558 funcGS = 12.0*varShat-2.0*funcPhiS;
559 } else {
560 const G4double varShat2 = varShat*varShat;
561 const G4double varShat3 = varShat*varShat2;
562 const G4double varShat4 = varShat2*varShat2;
563 if (varShat < 0.415827397755) { // Stanev ap.: for \psi(s) and compute G(s)
564 funcPhiS = 1.0-G4Exp( -6.0*varShat*(1.0+varShat*(3.0-CLHEP::pi))
565 + varShat3/(0.623+0.796*varShat+0.658*varShat2));
566 // 1-\exp \left\{-4s-\frac{8s^2}{1+3.936s+4.97s^2-0.05s^3+7.5s^4} \right\}
567 const G4double funcPsiS = 1.0-G4Exp( -4.0*varShat - 8.0*varShat2/(1.0
568 + 3.936*varShat+4.97*varShat2-0.05*varShat3+7.5*varShat4));
569 // G(s) = 3 \psi(s) - 2 \phi(s)
570 funcGS = 3.0*funcPsiS - 2.0*funcPhiS;
571 } else if (varShat < 1.55) {
572 funcPhiS = 1.0-G4Exp( -6.0*varShat*(1.0+varShat*(3.0-CLHEP::pi))
573 + varShat3/(0.623+0.796*varShat+0.658*varShat2));
574 const G4double dum0 = -0.16072300849123999+3.7550300067531581*varShat
575 -1.7981383069010097 *varShat2
576 +0.67282686077812381*varShat3
577 -0.1207722909879257 *varShat4;
578 funcGS = std::tanh(dum0);
579 } else {
580 funcPhiS = 1.0-0.01190476/varShat4;
581 if (varShat < 1.9156) {
582 const G4double dum0 = -0.16072300849123999+3.7550300067531581*varShat
583 -1.7981383069010097 *varShat2
584 +0.67282686077812381*varShat3
585 -0.1207722909879257 *varShat4;
586 funcGS = std::tanh(dum0);
587 } else {
588 funcGS = 1.0-0.0230655/varShat4;
589 }
590 }
591 }
592}
593
594// used at run-time to get some pre-computed LPM function values
595void G4PairProductionRelModel::GetLPMFunctions(G4double &lpmGs,
596 G4double &lpmPhis,
597 const G4double sval) {
598 if (sval < gLPMFuncs.fSLimit) {
599 G4double val = sval*gLPMFuncs.fISDelta;
600 const G4int ilow = (G4int)val;
601 val -= ilow;
602 lpmGs = (gLPMFuncs.fLPMFuncG[ilow+1]-gLPMFuncs.fLPMFuncG[ilow])*val
603 + gLPMFuncs.fLPMFuncG[ilow];
604 lpmPhis = (gLPMFuncs.fLPMFuncPhi[ilow+1]-gLPMFuncs.fLPMFuncPhi[ilow])*val
605 + gLPMFuncs.fLPMFuncPhi[ilow];
606 } else {
607 G4double ss = sval*sval;
608 ss *= ss;
609 lpmPhis = 1.0-0.01190476/ss;
610 lpmGs = 1.0-0.0230655/ss;
611 }
612}
613
614void G4PairProductionRelModel::ComputeLPMfunctions(G4double &funcXiS,
615 G4double &funcGS, G4double &funcPhiS, const G4double eps,
616 const G4double egamma, const G4int izet)
617{
618 // 1. y = E_+/E_{\gamma} with E_+ being the total energy transfered
619 // to one of the e-/e+ pair
620 // s' = \sqrt{ \frac{1}{8} \frac{1}{y(1-y)} \frac{E^{KL}_{LPM}}{E_{\gamma}} }
621 const G4double varSprime = std::sqrt(0.125*fLPMEnergy/(eps*egamma*(1.0-eps)));
622 const G4double condition = gElementData[izet]->fLPMVarS1Cond;
623 funcXiS = 2.0;
624 if (varSprime > 1.0) {
625 funcXiS = 1.0;
626 } else if (varSprime > condition) {
627 const G4double dum = gElementData[izet]->fLPMILVarS1Cond;
628 const G4double funcHSprime = G4Log(varSprime)*dum;
629 funcXiS = 1.0 + funcHSprime
630 - 0.08*(1.0-funcHSprime)*funcHSprime*(2.0-funcHSprime)*dum;
631 }
632 // 2. s=\frac{s'}{\sqrt{\xi(s')}}
633 const G4double varShat = varSprime / std::sqrt(funcXiS);
634 GetLPMFunctions(funcGS, funcPhiS, varShat);
635 // MAKE SURE SUPPRESSION IS SMALLER THAN 1: due to Migdal's approximation on xi
636 if (funcXiS * funcPhiS > 1. || varShat > 0.57) {
637 funcXiS = 1. / funcPhiS;
638 }
639}
640
641// Calculates the microscopic cross section in GEANT4 internal units. Same as in
642// G4BetheHeitlerModel and should be used below 80 GeV since it start to deverge
643// from the cross section data above 80-90 GeV:
644// Parametrized formula (L. Urban) is used to estimate the atomic cross sections
645// given numerically in the table of [Hubbell, J. H., Heinz Albert Gimm, and I.
646// Overbo: "Pair, Triplet, and Total Atomic Cross Sections (and Mass Attenuation
647// Coefficients) for 1 MeV‐100 GeV Photons in Elements Z= 1 to 100." Journal of
648// physical and chemical reference data 9.4 (1980): 1023-1148.]
649//
650// The formula gives a good approximation of the data from 1.5 MeV to 100 GeV.
651// below 1.5 MeV: sigma=sigma(1.5MeV)*(GammaEnergy-2electronmass)
652// *(GammaEnergy-2electronmass)
655 G4double Z)
656{
657 G4double xSection = 0.0 ;
658 // short versions
659 static const G4double kMC2 = CLHEP::electron_mass_c2;
660 // zero cross section below the kinematical limit: Eg<2mc^2
661 if (Z < 0.9 || gammaE <= 2.0*kMC2) { return xSection; }
662 //
663 static const G4double gammaEnergyLimit = 1.5*CLHEP::MeV;
664 // set coefficients a, b c
665 static const G4double a0 = 8.7842e+2*CLHEP::microbarn;
666 static const G4double a1 = -1.9625e+3*CLHEP::microbarn;
667 static const G4double a2 = 1.2949e+3*CLHEP::microbarn;
668 static const G4double a3 = -2.0028e+2*CLHEP::microbarn;
669 static const G4double a4 = 1.2575e+1*CLHEP::microbarn;
670 static const G4double a5 = -2.8333e-1*CLHEP::microbarn;
671
672 static const G4double b0 = -1.0342e+1*CLHEP::microbarn;
673 static const G4double b1 = 1.7692e+1*CLHEP::microbarn;
674 static const G4double b2 = -8.2381 *CLHEP::microbarn;
675 static const G4double b3 = 1.3063 *CLHEP::microbarn;
676 static const G4double b4 = -9.0815e-2*CLHEP::microbarn;
677 static const G4double b5 = 2.3586e-3*CLHEP::microbarn;
678
679 static const G4double c0 = -4.5263e+2*CLHEP::microbarn;
680 static const G4double c1 = 1.1161e+3*CLHEP::microbarn;
681 static const G4double c2 = -8.6749e+2*CLHEP::microbarn;
682 static const G4double c3 = 2.1773e+2*CLHEP::microbarn;
683 static const G4double c4 = -2.0467e+1*CLHEP::microbarn;
684 static const G4double c5 = 6.5372e-1*CLHEP::microbarn;
685 // check low energy limit of the approximation (1.5 MeV)
686 G4double gammaEnergyOrg = gammaE;
687 if (gammaE < gammaEnergyLimit) { gammaE = gammaEnergyLimit; }
688 // compute gamma energy variables
689 const G4double x = G4Log(gammaE/kMC2);
690 const G4double x2 = x *x;
691 const G4double x3 = x2*x;
692 const G4double x4 = x3*x;
693 const G4double x5 = x4*x;
694 //
695 const G4double F1 = a0 + a1*x + a2*x2 + a3*x3 + a4*x4 + a5*x5;
696 const G4double F2 = b0 + b1*x + b2*x2 + b3*x3 + b4*x4 + b5*x5;
697 const G4double F3 = c0 + c1*x + c2*x2 + c3*x3 + c4*x4 + c5*x5;
698 // compute the approximated cross section
699 xSection = (Z + 1.)*(F1*Z + F2*Z*Z + F3);
700 // check if we are below the limit of the approximation and apply correction
701 if (gammaEnergyOrg < gammaEnergyLimit) {
702 const G4double dum = (gammaEnergyOrg-2.*kMC2)/(gammaEnergyLimit-2.*kMC2);
703 xSection *= dum*dum;
704 }
705 return xSection;
706}
707
708
std::vector< G4Element * > G4ElementTable
#define F10
#define F20
G4double condition(const G4ErrorSymMatrix &m)
G4double G4Exp(G4double initial_x)
Exponential Function double precision.
Definition: G4Exp.hh:180
const G4double a0
G4double G4Log(G4double x)
Definition: G4Log.hh:227
#define elem(i, j)
@ fStopAndKill
double G4double
Definition: G4Types.hh:83
bool G4bool
Definition: G4Types.hh:86
int G4int
Definition: G4Types.hh:85
const G4int Z[17]
virtual double flat()=0
virtual void flatArray(const int size, double *vect)=0
G4double GetLogKineticEnergy() const
G4double GetKineticEnergy() const
static G4ElementTable * GetElementTable()
Definition: G4Element.cc:403
G4int GetZasInt() const
Definition: G4Element.hh:132
const G4Material * GetMaterial() const
G4double GetRadlen() const
Definition: G4Material.hh:215
G4double ComputeRelDXSectionPerAtom(G4double eplusEnergy, G4double gammaEnergy, G4double Z)
static const G4double gFelLowZet[8]
void SampleSecondaries(std::vector< G4DynamicParticle * > *, const G4MaterialCutsCouple *, const G4DynamicParticle *, G4double tmin, G4double maxEnergy) override
G4double ComputeParametrizedXSectionPerAtom(G4double gammaEnergy, G4double Z)
static const G4double gEgLPMActivation
void ScreenFunction12(const G4double delta, G4double &f1, G4double &f2)
static const G4double gFinelLowZet[8]
static std::vector< ElementData * > gElementData
G4PairProductionRelModel(const G4ParticleDefinition *p=nullptr, const G4String &nam="BetheHeitlerLPM")
static const G4double gXGL[8]
G4double ScreenFunction1(const G4double delta)
void SetupForMaterial(const G4ParticleDefinition *, const G4Material *, G4double) override
G4double ComputeCrossSectionPerAtom(const G4ParticleDefinition *, G4double kinEnergy, G4double Z, G4double A=0., G4double cut=0., G4double emax=DBL_MAX) override
G4ParticleDefinition * fThePositron
static const G4double gLPMconstant
void ComputePhi12(const G4double delta, G4double &phi1, G4double &phi2)
G4double ComputeXSectionPerAtom(G4double gammaEnergy, G4double Z)
G4double ScreenFunction2(const G4double delta)
void Initialise(const G4ParticleDefinition *, const G4DataVector &) override
G4ParticleDefinition * fTheGamma
G4double ComputeDXSectionPerAtom(G4double eplusEnergy, G4double gammaEnergy, G4double Z)
void InitialiseLocal(const G4ParticleDefinition *, G4VEmModel *masterModel) override
static const G4double gWGL[8]
G4ParticleChangeForGamma * fParticleChange
G4ParticleDefinition * fTheElectron
void SetProposedKineticEnergy(G4double proposedKinEnergy)
Definition: G4Pow.hh:49
G4double A13(G4double A) const
Definition: G4Pow.cc:116
virtual void SamplePairDirections(const G4DynamicParticle *dp, G4double elecKinEnergy, G4double posiKinEnergy, G4ThreeVector &dirElectron, G4ThreeVector &dirPositron, G4int Z=0, const G4Material *mat=nullptr)
void SetElementSelectors(std::vector< G4EmElementSelector * > *)
Definition: G4VEmModel.hh:831
G4VEmAngularDistribution * GetAngularDistribution()
Definition: G4VEmModel.hh:600
G4ParticleChangeForGamma * GetParticleChangeForGamma()
Definition: G4VEmModel.cc:124
G4double LowEnergyLimit() const
Definition: G4VEmModel.hh:641
std::vector< G4EmElementSelector * > * GetElementSelectors()
Definition: G4VEmModel.hh:823
G4bool IsMaster() const
Definition: G4VEmModel.hh:725
G4double HighEnergyLimit() const
Definition: G4VEmModel.hh:634
void SetAngularDistribution(G4VEmAngularDistribution *)
Definition: G4VEmModel.hh:607
const G4Element * SelectTargetAtom(const G4MaterialCutsCouple *, const G4ParticleDefinition *, G4double kineticEnergy, G4double logKineticEnergy, G4double cutEnergy=0.0, G4double maxEnergy=DBL_MAX)
Definition: G4VEmModel.hh:577
void InitialiseElementSelectors(const G4ParticleDefinition *, const G4DataVector &)
Definition: G4VEmModel.cc:139
void ProposeTrackStatus(G4TrackStatus status)
int G4lrint(double ad)
Definition: templates.hh:134