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