ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
G4eBremsstrahlungRelModel.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file G4eBremsstrahlungRelModel.cc
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: G4eBremsstrahlungRelModel
33 //
34 // Author: Andreas Schaelicke
35 //
36 // Creation date: 12.08.2008
37 //
38 // Modifications:
39 //
40 // 13.11.08 add SetLPMflag and SetLPMconstant methods
41 // 13.11.08 change default LPMconstant value
42 // 13.10.10 add angular distributon interface (VI)
43 // 31.05.16 change LPMconstant such that it gives suppression variable 's'
44 // that consistent to Migdal's one; fix a small bug in 'logTS1'
45 // computation; better agreement with exp.(M.Novak)
46 // 15.07.18 improved LPM suppression function approximation (no artificial
47 // steps), code cleanup and optimizations,more implementation and
48 // model related comments, consistent variable naming (M.Novak)
49 //
50 // Main References:
51 // Y.-S.Tsai, Rev. Mod. Phys. 46 (1974) 815; Rev. Mod. Phys. 49 (1977) 421.
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 //
59 
61 #include "G4PhysicalConstants.hh"
62 #include "G4SystemOfUnits.hh"
63 #include "G4Electron.hh"
64 #include "G4Gamma.hh"
65 #include "Randomize.hh"
66 #include "G4Material.hh"
67 #include "G4Element.hh"
68 #include "G4ElementVector.hh"
70 #include "G4ModifiedTsai.hh"
71 //#include "G4DipBustGenerator.hh"
72 
74 
75 // constant DCS factor: 16\alpha r_0^2/3
79 
80 // Migdal's constant: 4\pi r_0*electron_reduced_compton_wavelength^2
84 
85 // LPM constant: \alpha(mc^2)^2/(4\pi*\hbar c)
89 
90 // abscissas and weights of an 8 point Gauss-Legendre quadrature
91 // for numerical integration on [0,1]
93  1.98550718e-02, 1.01666761e-01, 2.37233795e-01, 4.08282679e-01,
94  5.91717321e-01, 7.62766205e-01, 8.98333239e-01, 9.80144928e-01
95 };
97  5.06142681e-02, 1.11190517e-01, 1.56853323e-01, 1.81341892e-01,
98  1.81341892e-01, 1.56853323e-01, 1.11190517e-01, 5.06142681e-02
99 };
100 
101 // elastic and inelatic radiation logarithms for light elements (where the
102 // Thomas-Fermi model doesn't work): computed by using Dirac-Fock model of atom.
104  0.0, 5.3104, 4.7935, 4.7402, 4.7112, 4.6694, 4.6134, 4.5520
105 };
107  0.0, 5.9173, 5.6125, 5.5377, 5.4728, 5.4174, 5.3688, 5.3236
108 };
109 
110 // LPM supression functions evaluated at initialisation time
112 
113 // special data structure per element i.e. per Z
114 std::vector<G4eBremsstrahlungRelModel::ElementData*> G4eBremsstrahlungRelModel::gElementData;
115 
117  const G4String& nam)
118 : G4VEmModel(nam), fIsElectron(true), fIsScatOffElectron(false),
119  fIsLPMActive(false), fPrimaryParticle(nullptr), fIsUseCompleteScreening(false)
120 {
121  fCurrentIZ = 0;
122  //
124  fPrimaryKinEnergy = 0.;
125  fPrimaryTotalEnergy = 0.;
126  fDensityFactor = 0.;
127  fDensityCorr = 0.;
128  fNucTerm = 0.;
129  fSumTerm = 0.;
130  //
131  fPrimaryParticle = nullptr;
133  fParticleChange = nullptr;
134  //
135  fLowestKinEnergy = 1.0*MeV;
137  //
138  fLPMEnergyThreshold = 1.e+39;
139  fLPMEnergy = 0.;
140 
141  SetLPMFlag(true);
142  //
144  //SetAngularDistribution(new G4DipBustGenerator());
145  //
146  if (p) {
147  SetParticle(p);
148  }
149 }
150 
152 {
153  if (IsMaster()) {
154  // clear ElementData container
155  for (size_t iz = 0; iz < gElementData.size(); ++iz) {
156  if (gElementData[iz]) {
157  delete gElementData[iz];
158  }
159  }
160  gElementData.clear();
161  // clear LPMFunctions (if any)
162  if (LPMFlag()) {
163  gLPMFuncs.fLPMFuncG.clear();
164  gLPMFuncs.fLPMFuncPhi.clear();
165  gLPMFuncs.fIsInitialized = false;
166  }
167  }
168 }
169 
171  const G4DataVector& cuts)
172 {
173  if (p) {
174  SetParticle(p);
175  }
176  fCurrentIZ = 0;
177  // init element data and precompute LPM functions (only if lpmflag is true)
178  if (IsMaster()) {
180  if (LPMFlag()) { InitLPMFunctions(); }
181  if (LowEnergyLimit() < HighEnergyLimit()) {
183  }
184  }
186  if (GetTripletModel()) {
187  GetTripletModel()->Initialise(p, cuts);
188  fIsScatOffElectron = true;
189  }
190 }
191 
193  G4VEmModel* masterModel)
194 {
195  if (LowEnergyLimit() < HighEnergyLimit()) {
197  }
198 }
199 
201 {
205 }
206 
207 // Sets kinematical variables like E_kin, E_t and some material dependent
208 // variables like LPM energy and characteristic photon energy k_p (more exactly
209 // k_p^2) for the Ter-Mikaelian suppression effect.
211  const G4Material* mat,
212  G4double kineticEnergy)
213 {
216  // threshold for LPM effect (i.e. below which LPM hidden by density effect)
217  if (LPMFlag()) {
219  } else {
220  fLPMEnergyThreshold = 1.e+39; // i.e. do not use LPM effect
221  }
222  // calculate threshold for density effect: k_p = sqrt(fDensityCorr)
223  fPrimaryKinEnergy = kineticEnergy;
226  // set activation flag for LPM effects in the DCS
227  fIsLPMActive = (fPrimaryTotalEnergy>fLPMEnergyThreshold);
228 }
229 
230 // minimum primary (e-/e+) energy at which discrete interaction is possible
232  const G4ParticleDefinition*,
233  G4double cut)
234 {
235  return std::max(fLowestKinEnergy, cut);
236 }
237 
238 // Computes the restricted dE/dx as the appropriate weight of the individual
239 // element contributions that are computed by numerically integrating the DCS.
240 G4double
242  const G4ParticleDefinition* p,
243  G4double kineticEnergy,
244  G4double cutEnergy)
245 {
246  G4double dedx = 0.0;
247  if (!fPrimaryParticle) {
248  SetParticle(p);
249  }
250  if (kineticEnergy < LowEnergyLimit()) {
251  return dedx;
252  }
253  // maximum value of the dE/dx integral (the minimum is 0 of course)
254  G4double tmax = std::min(cutEnergy, kineticEnergy);
255  if (tmax == 0.0) {
256  return dedx;
257  }
258  // sets kinematical and material related variables
259  SetupForMaterial(fPrimaryParticle, material,kineticEnergy);
260  // get element compositions of the material
261  const G4ElementVector* theElemVector = material->GetElementVector();
262  const G4double* theAtomNumDensVector = material->GetAtomicNumDensityVector();
263  const size_t numberOfElements = theElemVector->size();
264  // loop over the elements of the material and compute their contributions to
265  // the restricted dE/dx by numerical integration of the dependent part of DCS
266  for (size_t ie = 0; ie < numberOfElements; ++ie) {
267  G4VEmModel::SetCurrentElement((*theElemVector)[ie]);
268  //SetCurrentElement((*theElementVector)[i]->GetZasInt());
269  const G4double zet = (*theElemVector)[ie]->GetZ();
271  dedx += theAtomNumDensVector[ie]*zet*zet*ComputeBremLoss(tmax);
272  }
273  // apply the constant factor C/Z = 16\alpha r_0^2/3
274  dedx *= gBremFactor;
275  return std::max(dedx,0.);
276 }
277 
278 // Computes the integral part of the restricted dE/dx contribution from a given
279 // element (Z) by numerically integrating the k dependent part of the DCS between
280 // k_min=0 and k_max = tmax = min[gamma-cut, electron-kinetic-eenrgy].
281 // The numerical integration is done by dividing the integration range into 'n'
282 // subintervals and an 8 pint GL integral (on [0,1]) is performed on each sub-
283 // inteval by tranforming k to alpha=k/E_t (E_t is the total energy of the e-)
284 // and each sub-interavl is transformed to [0,1]. So the integrastion is done
285 // in xi(alpha) = xi(k) = [k/E_t-alpha_i]/delta where alpha_i=(i-1)*delta for
286 // the i = 1,2,..,n-th sub-interval so xi(k) in [0,1] on each sub-intevals.
287 // This transformation from 'k' to 'xi(k)' results in a multiplicative factor
288 // of E_t*delta at each step.
289 // The restricted dE/dx = N int_{0}^{k_max} k*ds/dk dk. There are 2 DCS model
290 // one with LPM and one without LPM effects (see them below). In both case not
291 // the ds/dk(Z,k) but ds/dk(Z,k)*[F*k/C] is computed since:
292 // (i) what we need here is ds/dk*k and not k so this multiplication is done
293 // (ii) the Ter-Mikaelian suppression i.e. F related factor is done here
294 // (iii) the constant factor C (includes Z^2 as well)is accounted in the caller
296 {
297  // number of intervals and integration step
298  const G4double alphaMax = tmax/fPrimaryTotalEnergy;
299  const G4int nSub = (G4int)(20*alphaMax)+3;
300  const G4double delta = alphaMax/((G4double)nSub);
301  // set minimum value of the first sub-inteval
302  G4double alpha_i = 0.0;
303  G4double dedxInteg = 0.0;
304  for (G4int l = 0; l < nSub; ++l) {
305  for (G4int igl = 0; igl < 8; ++igl) {
306  // compute the emitted photon energy k
307  const G4double k = (alpha_i+gXGL[igl]*delta)*fPrimaryTotalEnergy;
308  // compute the DCS value at k (without the constant, the 1/k, 1/F factors)
309  const G4double dcs = fIsLPMActive
310  ? ComputeRelDXSectionPerAtom(k) // DCS WITHOUT LPM
311  : ComputeDXSectionPerAtom(k); // DCS WITH LPM
312  // account Ter-Mikaelian suppression: times 1/F with F = 1+(k_p/k)^2
313  dedxInteg += gWGL[igl]*dcs/(1.0+fDensityCorr/(k*k));
314  }
315  // update sub-interval minimum value
316  alpha_i += delta;
317  }
318  // apply corrections due to variable transformation i.e. E_t*delta
319  dedxInteg *= delta*fPrimaryTotalEnergy;
320  return std::max(dedxInteg,0.);
321 }
322 
323 // Computes restrected atomic cross section by numerically integrating the
324 // DCS between the proper kinematical limits accounting the gamma production cut
326  const G4ParticleDefinition* p,
327  G4double kineticEnergy,
328  G4double Z,
329  G4double,
330  G4double cut,
331  G4double maxEnergy)
332 {
333  G4double crossSection = 0.0;
334  if (!fPrimaryParticle) {
335  SetParticle(p);
336  }
337  if (kineticEnergy < LowEnergyLimit()) {
338  return crossSection;
339  }
340  // min/max kinetic energy limits of the DCS integration:
341  const G4double tmin = std::min(cut, kineticEnergy);
342  const G4double tmax = std::min(maxEnergy, kineticEnergy);
343  // zero restricted x-section if e- kinetic energy is below gamma cut
344  if (tmin >= tmax) {
345  return crossSection;
346  }
348  // integrate numerically (dependent part of) the DCS between the kin. limits:
349  // a. integrate between tmin and kineticEnergy of the e-
350  crossSection = ComputeXSectionPerAtom(tmin);
351  // allow partial integration: only if maxEnergy < kineticEnergy
352  // b. integrate between tmax and kineticEnergy (tmax=maxEnergy in this case)
353  // (so the result in this case is the integral of DCS between tmin and
354  // maxEnergy)
355  if (tmax < kineticEnergy) {
356  crossSection -= ComputeXSectionPerAtom(tmax);
357  }
358  // multiply with the constant factors: 16\alpha r_0^2/3 Z^2
359  crossSection *= Z*Z*gBremFactor;
360  return std::max(crossSection, 0.);
361 }
362 
363 // Numerical integral of the (k dependent part of) DCS between k_min=tmin and
364 // k_max = E_k (where E_k is the kinetic energy of the e- and tmin is the
365 // minimum of energy of the emitted photon). The integration is done in the
366 // transformed alpha(k) = ln(k/E_t) variable (with E_t being the total energy of
367 // the primary e-). The integration range is divided into n sub-intervals with
368 // delta = [ln(k_min/E_t)-ln(k_max/E_t)]/n width each. An 8 point GL integral
369 // on [0,1] is applied on each sub-inteval so alpha is transformed to
370 // xi(alpha) = xi(k) = [ln(k/E_t)-alpha_i]/delta where alpha_i = ln(k_min/E_t) +
371 // (i-1)*delta for the i = 1,2,..,n-th sub-interval and xi(k) in [0,1] on each
372 // sub-intevals. From the transformed xi, k(xi) = E_t exp[xi*delta+alpha_i].
373 // Since the integration is done in variable xi instead of k this
374 // transformation results in a multiplicative factor of k*delta at each step.
375 // However, DCS differential in k is ~1/k so the multiplicative factor is simple
376 // becomes delta and the 1/k factor is dropped from the DCS computation.
377 // NOTE:
378 // - LPM suppression is accounted above threshold e- energy (corresponidng
379 // flag is set in SetUpForMaterial() => 2 DCS with/without LPM
380 // - Ter-Mikaelian suppression is always accounted
382 {
383  G4double xSection = 0.0;
384  const G4double alphaMin = G4Log(tmin/fPrimaryTotalEnergy);
386  const G4int nSub = (G4int)(0.45*(alphaMax-alphaMin))+4;
387  const G4double delta = (alphaMax-alphaMin)/((G4double)nSub);
388  // set minimum value of the first sub-inteval
389  G4double alpha_i = alphaMin;
390  for (G4int l = 0; l < nSub; ++l) {
391  for (G4int igl = 0; igl < 8; ++igl) {
392  // compute the emitted photon energy k
393  const G4double k = G4Exp(alpha_i+gXGL[igl]*delta)*fPrimaryTotalEnergy;
394  // compute the DCS value at k (without the constant, the 1/k, 1/F factors)
395  const G4double dcs = fIsLPMActive
396  ? ComputeRelDXSectionPerAtom(k) // DCS WITHOUT LPM
397  : ComputeDXSectionPerAtom(k); // DCS WITH LPM
398  // account Ter-Mikaelian suppression: times 1/F with F = 1+(k_p/k)^2
399  xSection += gWGL[igl]*dcs/(1.0+fDensityCorr/(k*k));
400  }
401  // update sub-interval minimum value
402  alpha_i += delta;
403  }
404  // apply corrections due to variable transformation
405  xSection *= delta;
406  // final check
407  return std::max(xSection, 0.);
408 }
409 
410 // DCS WITH LPM EFFECT: complete screening aprx. and includes LPM suppression
411 // ds/dk(Z,k) = C/[F*k]*{ Xi(s*F)*[y^2*G/4 +(1-y+y^2/3)Phi]*[L_el-f_c+L_inel/Z]
412 // +(1-y)*[1+1/Z]/12} with C = 16\alpha r_0^2/3 Z^2 and
413 // Xi(s),G(s), Phi(s) are LPM suppression functions:
414 //
415 // LPM SUPPRESSION: The 's' is the suppression variable and F = F(k,k_p) =
416 // 1+(k_p/k)^2 with k_p = hbar*w_p*E/(m*c^2) is a material (e- density)
417 // dependent constant. F accounts the Ter-Mikaelian suppression with a smooth
418 // transition in the emitted photon energy. Also, the LPM suppression functions
419 // goes to 0 when s goes to 0 and goes to 1 when s is increasing (=1 at s=~2)
420 // So evaluating the LPM suppression functions at 'sF' instead of 's' ensures a
421 // smooth transition depending on the emitted photon energy 'k': LPM effect is
422 // smoothly turned off i.e. Xi(sF)=G(sF)=Phi(sF)=1 when k << k_p because F >> 1
423 // and sF ~ s when k >> k_p since F ~ 1 in that case.
424 // HERE, ds/dk(Z,k)*[F*k/C] is computed since:
425 // (i) DCS ~ 1/k factor will disappear due to the variable transformation
426 // v(k)=ln(k/E_t) -> dk/dv=E_t*e^v=k -> ds/dv= ds/dk*dk/dv=ds/dk*k so it
427 // would cnacell out the 1/k factor => 1/k don't included here
428 // (ii) the constant factor C and Z don't depend on 'k' => not included here
429 // (iii) the 1/F(k) factor is accounted in the callers: explicitely (cross sec-
430 // tion computation) or implicitly through further variable transformaton
431 // (in the final state sampling algorithm)
432 // COMPLETE SCREENING: see more at the DCS without LPM effect below.
433 G4double
435 {
436  G4double dxsec = 0.0;
437  if (gammaEnergy < 0.) {
438  return dxsec;
439  }
440  const G4double y = gammaEnergy/fPrimaryTotalEnergy;
441  const G4double onemy = 1.-y;
442  const G4double dum0 = 0.25*y*y;
443  // evaluate LPM functions (combined with the Ter-Mikaelian effect)
444  G4double funcGS, funcPhiS, funcXiS;
445  ComputeLPMfunctions(funcXiS, funcGS, funcPhiS, gammaEnergy);
446  const ElementData* elDat = gElementData[fCurrentIZ];
447  const G4double term1 = funcXiS*(dum0*funcGS+(onemy+2.0*dum0)*funcPhiS);
448  dxsec = term1*elDat->fZFactor1+onemy*elDat->fZFactor2;
449  //
450  if (fIsScatOffElectron) {
451  fSumTerm = dxsec;
452  fNucTerm = term1*elDat->fZFactor11 + onemy/12.;
453  }
454  return std::max(dxsec,0.0);
455 }
456 
457 // DCS WITHOUT LPM EFFECT: DCS with sceening (Z>5) and Coulomb cor. no LPM
458 // ds/dk(Z,k)=C/[F*k]*{(1-y+3*y^2/4)*[(0.25*phi1(g)-ln(Z)/3-f_c)+(0.25*psi1(e)
459 // -2*ln(Z)/3)/Z]+ (1-y)*[(phi1(g)-phi2(g))+(psi1(e)-psi2(e))/Z]/8}
460 // where f_c(Z) is the Coulomb correction factor and phi1(g),phi2(g) and psi1(e),
461 // psi2(e) are coherent and incoherent screening functions. In the Thomas-Fermi
462 // model of the atom, the screening functions will have a form that do not
463 // depend on Z (not explicitely). These numerical screening functions can be
464 // approximated as Tsai Eqs. [3.38-3.41] with the variables g=gamma and
465 // e=epsilon given by Tsai Eqs. [3.30 and 3.31] (see more details at the method
466 // ComputeScreeningFunctions()). Note, that in case of complete screening i.e.
467 // g = e = 0 => 0.25*phi1(0)-ln(Z)/3 = ln(184.149/Z^(1/3)) = L_el and
468 // 0.25*psi1(0)-2*ln(Z)/3=ln(1193.923/Z^(2/3))=L_inel and phi1(0)-phi2(0) =
469 // psi1(0)-psi2(0) = 2/3 so the DCS in complete screening =>
470 // COMPLETE SCREENING:
471 // ds/dk(Z,k)=C/k*{(1-y+3*y^2/4)*[L_el-f_c+L_inel/Z] + (1-y)*[1+1/Z]/12} that is
472 // used in case of DCS with LPM above (if all the suprression functions are
473 // absent i.e. their value = 1).
474 // Since the Thomas-Fermi model of the atom is not accurate at low Z, the DCS in
475 // complete screening is used here at low Z(<5) with L_el(Z), L_inel(Z) values
476 // computed by using the Dirac-Fock model of the atom.
477 // NOTE: that the Ter-Mikaelian suppression is accounted in the DCS through the
478 // 1/F factor but it is included in the caller and not considered here.
479 // HERE, ds/dk(Z,k)*[F*k/C] is computed exactly like in the DCS with LPM case.
480 G4double
482 {
483  G4double dxsec = 0.0;
484  if (gammaEnergy < 0.) {
485  return dxsec;
486  }
487  const G4double y = gammaEnergy/fPrimaryTotalEnergy;
488  const G4double onemy = 1.-y;
489  const G4double dum0 = onemy+0.75*y*y;
490  const ElementData* elDat = gElementData[fCurrentIZ];
491  // use complete screening and L_el, L_inel from Dirac-Fock model instead of TF
492  if (fCurrentIZ < 5 || fIsUseCompleteScreening) {
493  dxsec = dum0*elDat->fZFactor1;
494  dxsec += onemy*elDat->fZFactor2;
495  if (fIsScatOffElectron) {
496  fSumTerm = dxsec;
497  fNucTerm = dum0*elDat->fZFactor11+onemy/12.;
498  }
499  } else {
500  // use Tsai's analytical approx. (Tsai Eqs. [3.38-3.41]) to the 'universal'
501  // numerical screening functions computed by using the TF model of the atom
502  const G4double invZ = 1./(G4double)fCurrentIZ;
503  const G4double Fz = elDat->fFz;
504  const G4double logZ = elDat->fLogZ;
505  const G4double dum1 = y/(fPrimaryTotalEnergy-gammaEnergy);
506  const G4double gamma = dum1*elDat->fGammaFactor;
507  const G4double epsilon = dum1*elDat->fEpsilonFactor;
508  // evaluate the screening functions
509  G4double phi1, phi1m2, psi1, psi1m2;
510  ComputeScreeningFunctions(phi1, phi1m2, psi1, psi1m2, gamma, epsilon);
511  dxsec = dum0*((0.25*phi1-Fz) + (0.25*psi1-2.*logZ/3.)*invZ);
512  dxsec += 0.125*onemy*(phi1m2 + psi1m2*invZ);
513  if (fIsScatOffElectron) {
514  fSumTerm = dxsec;
515  fNucTerm = dum0*(0.25*phi1-Fz) + 0.125*onemy*phi1m2;
516  }
517  }
518  return std::max(dxsec,0.0);
519 }
520 
521 // Coherent and incoherent screening function approximations (see Tsai
522 // Eqs.[3.38-3.41]). Tsai's analytical approximations to the numerical screening
523 // functions computed by using the Thomas-Fermi model of atom (Moliere's appro-
524 // ximation to the numerical TF screening function). In the TF-model, these
525 // screening functions can be expressed in a 'universal' i.e. Z (directly) inde-
526 // pendent variable (see Tsai Eqs. Eqs. [3.30 and 3.31]).
528  G4double& phi1m2,
529  G4double& psi1,
530  G4double& psi1m2,
531  const G4double gam,
532  const G4double eps)
533 {
534  const G4double gam2 = gam*gam;
535  phi1 = 16.863-2.0*G4Log(1.0+0.311877*gam2)+2.4*G4Exp(-0.9*gam)
536  +1.6*G4Exp(-1.5*gam);
537  phi1m2 = 2.0/(3.0+19.5*gam+18.0*gam2); // phi1-phi2
538  const G4double eps2 = eps*eps;
539  psi1 = 24.34-2.0*G4Log(1.0+13.111641*eps2)+2.8*G4Exp(-8.0*eps)
540  +1.2*G4Exp(-29.2*eps);
541  psi1m2 = 2.0/(3.0+120.0*eps+1200.0*eps2); //psi1-psi2
542 }
543 
544 void
545 G4eBremsstrahlungRelModel::SampleSecondaries(std::vector<G4DynamicParticle*>* vdp,
546  const G4MaterialCutsCouple* couple,
547  const G4DynamicParticle* dp,
548  G4double cutEnergy,
549  G4double maxEnergy)
550 {
551  const G4double kineticEnergy = dp->GetKineticEnergy();
552 // const G4double logKineticEnergy = dp->GetLogKineticEnergy();
553  if (kineticEnergy < LowEnergyLimit()) {
554  return;
555  }
556  // min, max kinetic energy limits
557  const G4double tmin = std::min(cutEnergy, kineticEnergy);
558  const G4double tmax = std::min(maxEnergy, kineticEnergy);
559  if (tmin >= tmax) {
560  return;
561  }
562  //
563  SetupForMaterial(fPrimaryParticle, couple->GetMaterial(), kineticEnergy);
564  const G4Element* elm = SelectTargetAtom(couple,fPrimaryParticle,kineticEnergy,
565  dp->GetLogKineticEnergy(),tmin,tmax);
566  //
567  fCurrentIZ = elm->GetZasInt();
568  const ElementData* elDat = gElementData[fCurrentIZ];
569  const G4double funcMax = elDat->fZFactor1+elDat->fZFactor2;
570  // get the random engine
571  G4double rndm[2];
572  CLHEP::HepRandomEngine* rndmEngine = G4Random::getTheEngine();
573  // min max of the transformed variable: x(k) = ln(k^2+k_p^2) that is in [ln(k_c^2+k_p^2), ln(E_k^2+k_p^2)]
574  const G4double xmin = G4Log(tmin*tmin+fDensityCorr);
575  const G4double xrange = G4Log(tmax*tmax+fDensityCorr)-xmin;
576  G4double gammaEnergy, funcVal;
577  do {
578  rndmEngine->flatArray(2, rndm);
579  gammaEnergy = std::sqrt(std::max(G4Exp(xmin+rndm[0]*xrange)-fDensityCorr, 0.0));
580  funcVal = fIsLPMActive
581  ? ComputeRelDXSectionPerAtom(gammaEnergy)
582  : ComputeDXSectionPerAtom(gammaEnergy);
583  // cross-check of proper function maximum in the rejection
584 // if (funcVal > funcMax) {
585 // G4cout << "### G4eBremsstrahlungRelModel Warning: Majoranta exceeded! "
586 // << funcVal << " > " << funcMax
587 // << " Egamma(MeV)= " << gammaEnergy
588 // << " Ee(MeV)= " << kineticEnergy
589 // << " " << GetName()
590 // << G4endl;
591 // }
592  // Loop checking, 03-Aug-2015, Vladimir Ivanchenko
593  } while (funcVal < funcMax*rndm[1]);
594  //
595  // scattering off nucleus or off e- by triplet model
596  if (fIsScatOffElectron && rndmEngine->flat()*fSumTerm>fNucTerm) {
597  GetTripletModel()->SampleSecondaries(vdp, couple, dp, cutEnergy, maxEnergy);
598  return;
599  }
600  //
601  // angles of the emitted gamma. ( Z - axis along the parent particle)
602  // use general interface
603  G4ThreeVector gamDir =
605  fCurrentIZ, couple->GetMaterial());
606  // create G4DynamicParticle object for the Gamma
608  gammaEnergy);
609  vdp->push_back(gamma);
610  // compute post-interaction kinematics of primary e-/e+ based on
611  // energy-momentum conservation
612  const G4double totMomentum = std::sqrt(kineticEnergy*(
615  (totMomentum*dp->GetMomentumDirection()-gammaEnergy*gamDir).unit();
616  const G4double finalE = kineticEnergy-gammaEnergy;
617  // if secondary gamma energy is higher than threshold(very high by default)
618  // then stop tracking the primary particle and create new secondary e-/e+
619  // instead of the primary one
620  if (gammaEnergy > SecondaryThreshold()) {
624  const_cast<G4ParticleDefinition*>(fPrimaryParticle), dir, finalE);
625  vdp->push_back(el);
626  } else { // continue tracking the primary e-/e+ otherwise
629  }
630 }
631 
633 {
634  const G4int size = gElementData.size();
635  if (size < gMaxZet+1) {
636  gElementData.resize(gMaxZet+1, nullptr);
637  }
638  // create for all elements that are in the detector
639  const G4ElementTable* elemTable = G4Element::GetElementTable();
640  size_t numElems = (*elemTable).size();
641  for (size_t ielem=0; ielem<numElems; ++ielem) {
642  const G4Element* elem = (*elemTable)[ielem];
643  const G4double zet = elem->GetZ();
644  const G4int izet = std::min(G4lrint(zet),gMaxZet);
645  if (!gElementData[izet]) {
646  ElementData *elemData = new ElementData();
647  const G4double fc = elem->GetfCoulomb();
648  G4double Fel = 1.;
649  G4double Finel = 1.;
650  elemData->fLogZ = G4Log(zet);
651  elemData->fFz = elemData->fLogZ/3.+fc;
652  if (izet < 5) {
653  Fel = gFelLowZet[izet];
654  Finel = gFinelLowZet[izet];
655  } else {
656  Fel = G4Log(184.15) - elemData->fLogZ/3.;
657  Finel = G4Log(1194) - 2.*elemData->fLogZ/3.;
658  }
659  const G4double z23 = std::pow(zet,2./3.);
660  const G4double z13 = std::pow(zet,1./3.);
661  elemData->fZFactor1 = (Fel-fc)+Finel/zet;
662  elemData->fZFactor11 = (Fel-fc); // used only for the triplet
663  elemData->fZFactor2 = (1.+1./zet)/12.;
664  elemData->fVarS1 = z23/(184.15*184.15);
665  elemData->fILVarS1Cond = 1./(G4Log(std::sqrt(2.0)*elemData->fVarS1));
666  elemData->fILVarS1 = 1./G4Log(elemData->fVarS1);
667  elemData->fGammaFactor = 100.0*electron_mass_c2/z13;
668  elemData->fEpsilonFactor = 100.0*electron_mass_c2/z23;
669  gElementData[izet] = elemData;
670  }
671  }
672 }
673 
675  G4double& funcGS,
676  G4double& funcPhiS,
677  const G4double egamma)
678 {
679  static const G4double sqrt2 = std::sqrt(2.);
680  const G4double redegamma = egamma/fPrimaryTotalEnergy;
681  const G4double varSprime = std::sqrt(0.125*redegamma*fLPMEnergy/
682  ((1.0-redegamma)*fPrimaryTotalEnergy));
683  const ElementData* elDat = gElementData[fCurrentIZ];
684  const G4double varS1 = elDat->fVarS1;
685  const G4double condition = sqrt2*varS1;
686  G4double funcXiSprime = 2.0;
687  if (varSprime > 1.0) {
688  funcXiSprime = 1.0;
689  } else if (varSprime > condition) {
690  const G4double ilVarS1Cond = elDat->fILVarS1Cond;
691  const G4double funcHSprime = G4Log(varSprime)*ilVarS1Cond;
692  funcXiSprime = 1.0 + funcHSprime - 0.08*(1.0-funcHSprime)*funcHSprime
693  *(2.0-funcHSprime)*ilVarS1Cond;
694  }
695  const G4double varS = varSprime/std::sqrt(funcXiSprime);
696  // - include dielectric suppression effect into s according to Migdal
697  const G4double varShat = varS*(1.0+fDensityCorr/(egamma*egamma));
698  funcXiS = 2.0;
699  if (varShat > 1.0) {
700  funcXiS = 1.0;
701  } else if (varShat > varS1) {
702  funcXiS = 1.0+G4Log(varShat)*elDat->fILVarS1;
703  }
704  GetLPMFunctions(funcGS, funcPhiS, varShat);
705  //ComputeLPMGsPhis(funcGS, funcPhiS, varShat);
706  //
707  //MAKE SURE SUPPRESSION IS SMALLER THAN 1: due to Migdal's approximation on xi
708  if (funcXiS*funcPhiS > 1. || varShat > 0.57) {
709  funcXiS=1./funcPhiS;
710  }
711 }
712 
714  G4double& funcPhiS,
715  const G4double varShat)
716 {
717  if (varShat < 0.01) {
718  funcPhiS = 6.0*varShat*(1.0-CLHEP::pi*varShat);
719  funcGS = 12.0*varShat-2.0*funcPhiS;
720  } else {
721  const G4double varShat2 = varShat*varShat;
722  const G4double varShat3 = varShat*varShat2;
723  const G4double varShat4 = varShat2*varShat2;
724  // use Stanev approximation: for \psi(s) and compute G(s)
725  if (varShat < 0.415827) {
726  funcPhiS = 1.0-G4Exp(-6.0*varShat*(1.0+varShat*(3.0-CLHEP::pi))
727  + varShat3/(0.623+0.796*varShat+0.658*varShat2));
728  // 1-\exp \left\{-4s-\frac{8s^2}{1+3.936s+4.97s^2-0.05s^3+7.5s^4} \right\}
729  const G4double funcPsiS = 1.0 - G4Exp(-4.0*varShat
730  - 8.0*varShat2/(1.0+3.936*varShat+4.97*varShat2
731  - 0.05*varShat3 + 7.5*varShat4));
732  // G(s) = 3 \psi(s) - 2 \phi(s)
733  funcGS = 3.0*funcPsiS - 2.0*funcPhiS;
734  } else if (varShat<1.55) {
735  funcPhiS = 1.0-G4Exp(-6.0*varShat*(1.0+varShat*(3.0-CLHEP::pi))
736  + varShat3/(0.623+0.796*varShat+0.658*varShat2));
737  const G4double dum0 = -0.160723 + 3.755030*varShat
738  -1.798138*varShat2 + 0.672827*varShat3
739  -0.120772*varShat4;
740  funcGS = std::tanh(dum0);
741  } else {
742  funcPhiS = 1.0-0.011905/varShat4;
743  if (varShat<1.9156) {
744  const G4double dum0 = -0.160723 + 3.755030*varShat
745  -1.798138*varShat2 + 0.672827*varShat3
746  -0.120772*varShat4;
747  funcGS = std::tanh(dum0);
748  } else {
749  funcGS = 1.0-0.023065/varShat4;
750  }
751  }
752  }
753 }
754 
755 // s goes up to 2 with ds = 0.01 to be the default bining
757 {
758  if (!gLPMFuncs.fIsInitialized) {
760  gLPMFuncs.fLPMFuncG.resize(num);
761  gLPMFuncs.fLPMFuncPhi.resize(num);
762  for (G4int i = 0; i < num; ++i) {
763  const G4double sval=i/gLPMFuncs.fISDelta;
765  }
766  gLPMFuncs.fIsInitialized = true;
767  }
768 }
769 
771  G4double& lpmPhis,
772  const G4double sval)
773 {
774  if (sval < gLPMFuncs.fSLimit) {
775  G4double val = sval*gLPMFuncs.fISDelta;
776  const G4int ilow = (G4int)val;
777  val -= ilow;
778  lpmGs = (gLPMFuncs.fLPMFuncG[ilow+1]-gLPMFuncs.fLPMFuncG[ilow])*val
779  + gLPMFuncs.fLPMFuncG[ilow];
780  lpmPhis = (gLPMFuncs.fLPMFuncPhi[ilow+1]-gLPMFuncs.fLPMFuncPhi[ilow])*val
781  + gLPMFuncs.fLPMFuncPhi[ilow];
782  } else {
783  G4double ss = sval*sval;
784  ss *= ss;
785  lpmPhis = 1.0-0.01190476/ss;
786  lpmGs = 1.0-0.0230655/ss;
787  }
788 }
789