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