Geant4 11.1.1
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
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