Import Geant4 11.2.0 source tree

This commit is contained in:
Gabriele Cosmo
2023-12-08 10:43:34 +01:00
parent dd1f179cda
commit 860a2b92bf
3962 changed files with 139318 additions and 164259 deletions
@@ -62,6 +62,8 @@
#include "G4EmParameters.hh"
#include "G4ProductionCutsTable.hh"
#include "G4Gamma.hh"
#include "G4Electron.hh"
#include "G4Physics2DVector.hh"
#include "G4Exp.hh"
@@ -73,33 +75,56 @@
#include <fstream>
#include <iomanip>
#include <sstream>
#include <thread>
G4Physics2DVector* G4SeltzerBergerModel::gSBDCSData[] = { nullptr };
G4SBBremTable* G4SeltzerBergerModel::gSBSamplingTable = nullptr;
G4double G4SeltzerBergerModel::gYLimitData[] = { 0.0 };
G4double G4SeltzerBergerModel::gYLimitData[] = { 0.0 };
G4Physics2DVector* G4SeltzerBergerModel::gSBDCSData[] = { nullptr };
G4SBBremTable* G4SeltzerBergerModel::gSBSamplingTable = nullptr;
// constant DCS factor: 16\alpha r_0^2/3
const G4double G4SeltzerBergerModel::gBremFactor
= 16. * CLHEP::fine_structure_const * CLHEP::classic_electr_radius
* CLHEP::classic_electr_radius/3.;
// Migdal's constant: 4\pi r_0*electron_reduced_compton_wavelength^2
const G4double G4SeltzerBergerModel::gMigdalConstant
= 4. * CLHEP::pi * CLHEP::classic_electr_radius
* CLHEP::electron_Compton_length * CLHEP::electron_Compton_length;
static constexpr G4double twoMass = 2* CLHEP::electron_mass_c2;
static constexpr G4double kAlpha = CLHEP::twopi*CLHEP::fine_structure_const;
static std::once_flag applyOnce;
namespace
{
G4Mutex theSBMutex = G4MUTEX_INITIALIZER;
}
static const G4double kMC2 = CLHEP::electron_mass_c2;
static const G4double kAlpha = CLHEP::twopi*CLHEP::fine_structure_const;
// for numerical integration on [0,1]
const G4double gXGL[8] = {
1.98550718e-02, 1.01666761e-01, 2.37233795e-01, 4.08282679e-01,
5.91717321e-01, 7.62766205e-01, 8.98333239e-01, 9.80144928e-01
};
const G4double gWGL[8] = {
5.06142681e-02, 1.11190517e-01, 1.56853323e-01, 1.81341892e-01,
1.81341892e-01, 1.56853323e-01, 1.11190517e-01, 5.06142681e-02
};
}
G4SeltzerBergerModel::G4SeltzerBergerModel(const G4ParticleDefinition* p,
const G4String& nam)
: G4eBremsstrahlungRelModel(p,nam), fIsUseBicubicInterpolation(false),
fIsUseSamplingTables(true), fNumWarnings(0), fIndx(0), fIndy(0)
: G4VEmModel(nam),
fGammaParticle(G4Gamma::Gamma()),
fLowestKinEnergy(1.0*CLHEP::keV)
{
fLowestKinEnergy = 1.0*keV;
SetLowEnergyLimit(fLowestKinEnergy);
SetAngularDistribution(new G4ModifiedTsai());
if (fPrimaryParticle != p) { SetParticle(p); }
}
G4SeltzerBergerModel::~G4SeltzerBergerModel()
{
// delete SB-DCS data per Z
if (isFirstInstance) {
if (isInitializer) {
for (std::size_t iz = 0; iz < gMaxZet; ++iz) {
if (gSBDCSData[iz]) {
delete gSBDCSData[iz];
@@ -116,23 +141,20 @@ G4SeltzerBergerModel::~G4SeltzerBergerModel()
void G4SeltzerBergerModel::Initialise(const G4ParticleDefinition* p,
const G4DataVector& cuts)
{
// parameters in each thread
if (fPrimaryParticle != p) {
SetParticle(p);
}
fIsUseSamplingTables = G4EmParameters::Instance()->EnableSamplingTable();
fCurrentIZ = 0;
// initialise static tables for the Seltzer-Berger model
if (0.0 == gYLimitData[0]) {
std::call_once(applyOnce, [this]() { isInitializer = true; });
if (isInitializer) {
G4AutoLock l(&theSBMutex);
if (0.0 == gYLimitData[0]) {
isFirstInstance = true;
gYLimitData[0] = 1.0;
}
l.unlock();
}
// initialise base class
G4eBremsstrahlungRelModel::Initialise(p, cuts);
// initialisation per element is done only once
if (isFirstInstance) {
// initialisation per element is done only once
auto elemTable = G4Element::GetElementTable();
for (auto const & elm : *elemTable) {
G4int Z = std::max(1,std::min(elm->GetZasInt(), gMaxZet-1));
@@ -148,7 +170,33 @@ void G4SeltzerBergerModel::Initialise(const G4ParticleDefinition* p,
gSBSamplingTable->Initialize(std::max(fLowestKinEnergy, LowEnergyLimit()),
HighEnergyLimit());
}
l.unlock();
}
// element selectors are initialized in the master thread
if (IsMaster()) {
InitialiseElementSelectors(p, cuts);
}
// initialisation in all threads
if (nullptr == fParticleChange) {
fParticleChange = GetParticleChangeForLoss();
}
auto trmodel = GetTripletModel();
if (nullptr != trmodel) {
trmodel->Initialise(p, cuts);
fIsScatOffElectron = true;
}
}
void G4SeltzerBergerModel::InitialiseLocal(const G4ParticleDefinition*,
G4VEmModel* masterModel)
{
SetElementSelectors(masterModel->GetElementSelectors());
}
void G4SeltzerBergerModel::SetParticle(const G4ParticleDefinition* p)
{
fPrimaryParticle = p;
fIsElectron = (p == G4Electron::Electron());
}
void G4SeltzerBergerModel::ReadData(G4int Z) {
@@ -174,7 +222,7 @@ void G4SeltzerBergerModel::ReadData(G4int Z) {
v->SetBicubicInterpolation(fIsUseBicubicInterpolation);
static const G4double emaxlog = 4*G4Log(10.);
gYLimitData[Z] = v->Value(0.97, emaxlog, fIndx, fIndy);
gSBDCSData[Z] = v;
gSBDCSData[Z] = v;
} else {
G4ExceptionDescription ed;
ed << "Bremsstrahlung data file <" << ost.str().c_str()
@@ -186,6 +234,191 @@ void G4SeltzerBergerModel::ReadData(G4int Z) {
}
}
// minimum primary (e-/e+) energy at which discrete interaction is possible
G4double G4SeltzerBergerModel::MinPrimaryEnergy(const G4Material*,
const G4ParticleDefinition*,
G4double cut)
{
return std::max(fLowestKinEnergy, cut);
}
// Sets kinematical variables like E_kin, E_t and some material dependent
// for characteristic photon energy k_p (more exactly
// k_p^2) for the Ter-Mikaelian suppression effect.
void G4SeltzerBergerModel::SetupForMaterial(const G4ParticleDefinition*,
const G4Material* mat,
G4double kinEnergy)
{
fDensityFactor = gMigdalConstant*mat->GetElectronDensity();
// calculate threshold for density effect: k_p = sqrt(fDensityCorr)
fPrimaryKinEnergy = kinEnergy;
fPrimaryTotalEnergy = kinEnergy + CLHEP::electron_mass_c2;
fDensityCorr = fDensityFactor*fPrimaryTotalEnergy*fPrimaryTotalEnergy;
}
// Computes the restricted dE/dx as the appropriate weight of the individual
// element contributions that are computed by numerically integrating the DCS.
G4double
G4SeltzerBergerModel::ComputeDEDXPerVolume(const G4Material* material,
const G4ParticleDefinition* p,
G4double kineticEnergy,
G4double cutEnergy)
{
G4double dedx = 0.0;
if (nullptr == fPrimaryParticle) {
SetParticle(p);
}
if (kineticEnergy <= fLowestKinEnergy) {
return dedx;
}
// maximum value of the dE/dx integral (the minimum is 0 of course)
G4double tmax = std::min(cutEnergy, kineticEnergy);
if (tmax == 0.0) {
return dedx;
}
// sets kinematical and material related variables
SetupForMaterial(fPrimaryParticle, material, kineticEnergy);
// get element compositions of the material
const G4ElementVector* theElemVector = material->GetElementVector();
const G4double* theAtomNumDensVector = material->GetAtomicNumDensityVector();
const std::size_t numberOfElements = theElemVector->size();
// loop over the elements of the material and compute their contributions to
// the restricted dE/dx by numerical integration of the dependent part of DCS
for (std::size_t ie = 0; ie < numberOfElements; ++ie) {
G4VEmModel::SetCurrentElement((*theElemVector)[ie]);
G4int Z = (*theElemVector)[ie]->GetZasInt();
fCurrentIZ = std::min(Z, gMaxZet);
dedx += (Z*Z)*theAtomNumDensVector[ie]*ComputeBremLoss(tmax);
}
// apply the constant factor C/Z = 16\alpha r_0^2/3
dedx *= gBremFactor;
return std::max(dedx, 0.);
}
// Computes the integral part of the restricted dE/dx contribution from a given
// element (Z) by numerically integrating the k dependent DCS between
// k_min=0 and k_max = tmax = min[gamma-cut, electron-kinetic-energy].
// The numerical integration is done by dividing the integration range into 'n'
// subintervals and an 8 pint GL integral (on [0,1]) is performed on each sub-
// inteval by tranforming k to alpha=k/E_t (E_t is the total energy of the e-)
// and each sub-interavl is transformed to [0,1]. So the integrastion is done
// in xi(alpha) = xi(k) = [k/E_t-alpha_i]/delta where alpha_i=(i-1)*delta for
// the i = 1,2,..,n-th sub-interval so xi(k) in [0,1] on each sub-intevals.
// This transformation from 'k' to 'xi(k)' results in a multiplicative factor
// of E_t*delta at each step.
// The restricted dE/dx = N int_{0}^{k_max} k*ds/dk dk. In this case not
// the ds/dk(Z,k) but ds/dk(Z,k)*[F*k/C] is computed since:
// (i) what we need here is ds/dk*k and not k so this multiplication is done
// (ii) the Ter-Mikaelian suppression i.e. F related factor is done here
// (iii) the constant factor C (includes Z^2 as well)is accounted in the caller
G4double G4SeltzerBergerModel::ComputeBremLoss(G4double tmax)
{
// number of intervals and integration step
const G4double alphaMax = tmax/fPrimaryTotalEnergy;
const G4int nSub = (G4int)(20*alphaMax)+3;
const G4double delta = alphaMax/((G4double)nSub);
// set minimum value of the first sub-inteval
G4double alpha_i = 0.0;
G4double dedxInteg = 0.0;
for (G4int l = 0; l < nSub; ++l) {
for (G4int igl = 0; igl < 8; ++igl) {
// compute the emitted photon energy k
const G4double k = (alpha_i+gXGL[igl]*delta)*fPrimaryTotalEnergy;
// compute the DCS value at k (without the constant, the 1/k, 1/F factors)
const G4double dcs = ComputeDXSectionPerAtom(k);
// account Ter-Mikaelian suppression: times 1/F with F = 1+(k_p/k)^2
dedxInteg += gWGL[igl]*dcs/(1.0+fDensityCorr/(k*k));
}
// update sub-interval minimum value
alpha_i += delta;
}
// apply corrections due to variable transformation i.e. E_t*delta
dedxInteg *= delta*fPrimaryTotalEnergy;
return std::max(dedxInteg,0.);
}
// Computes restrected atomic cross section by numerically integrating the
// DCS between the proper kinematical limits accounting the gamma production cut
G4double
G4SeltzerBergerModel::ComputeCrossSectionPerAtom(const G4ParticleDefinition* p,
G4double kineticEnergy,
G4double Z,
G4double,
G4double cut,
G4double maxEnergy)
{
G4double crossSection = 0.0;
if (nullptr == fPrimaryParticle) {
SetParticle(p);
}
if (kineticEnergy <= fLowestKinEnergy) {
return crossSection;
}
// min/max kinetic energy limits of the DCS integration:
const G4double tmin = std::min(cut, kineticEnergy);
const G4double tmax = std::min(maxEnergy, kineticEnergy);
// zero restricted x-section if e- kinetic energy is below gamma cut
if (tmin >= tmax) {
return crossSection;
}
fCurrentIZ = std::min(G4lrint(Z), gMaxZet);
// integrate numerically (dependent part of) the DCS between the kin. limits:
// a. integrate between tmin and kineticEnergy of the e-
crossSection = ComputeXSectionPerAtom(tmin);
// allow partial integration: only if maxEnergy < kineticEnergy
// b. integrate between tmax and kineticEnergy (tmax=maxEnergy in this case)
// (so the result in this case is the integral of DCS between tmin and
// maxEnergy)
if (tmax < kineticEnergy) {
crossSection -= ComputeXSectionPerAtom(tmax);
}
// multiply with the constant factors: 16\alpha r_0^2/3 Z^2
crossSection *= Z*Z*gBremFactor;
return std::max(crossSection, 0.);
}
// Numerical integral of the (k dependent part of) DCS between k_min=tmin and
// k_max = E_k (where E_k is the kinetic energy of the e- and tmin is the
// minimum of energy of the emitted photon). The integration is done in the
// transformed alpha(k) = ln(k/E_t) variable (with E_t being the total energy of
// the primary e-). The integration range is divided into n sub-intervals with
// delta = [ln(k_min/E_t)-ln(k_max/E_t)]/n width each. An 8 point GL integral
// on [0,1] is applied on each sub-inteval so alpha is transformed to
// xi(alpha) = xi(k) = [ln(k/E_t)-alpha_i]/delta where alpha_i = ln(k_min/E_t) +
// (i-1)*delta for the i = 1,2,..,n-th sub-interval and xi(k) in [0,1] on each
// sub-intevals. From the transformed xi, k(xi) = E_t exp[xi*delta+alpha_i].
// Since the integration is done in variable xi instead of k this
// transformation results in a multiplicative factor of k*delta at each step.
// However, DCS differential in k is ~1/k so the multiplicative factor is simple
// becomes delta and the 1/k factor is dropped from the DCS computation.
// Ter-Mikaelian suppression is always accounted
G4double G4SeltzerBergerModel::ComputeXSectionPerAtom(G4double tmin)
{
G4double xSection = 0.0;
const G4double alphaMin = G4Log(tmin/fPrimaryTotalEnergy);
const G4double alphaMax = G4Log(fPrimaryKinEnergy/fPrimaryTotalEnergy);
const G4int nSub = (G4int)(0.45*(alphaMax-alphaMin))+4;
const G4double delta = (alphaMax-alphaMin)/((G4double)nSub);
// set minimum value of the first sub-inteval
G4double alpha_i = alphaMin;
for (G4int l = 0; l < nSub; ++l) {
for (G4int igl = 0; igl < 8; ++igl) {
// compute the emitted photon energy k
const G4double k = G4Exp(alpha_i+gXGL[igl]*delta)*fPrimaryTotalEnergy;
// compute the DCS value at k (without the constant, the 1/k, 1/F factors)
const G4double dcs = ComputeDXSectionPerAtom(k);
// account Ter-Mikaelian suppression: times 1/F with F = 1+(k_p/k)^2
xSection += gWGL[igl]*dcs/(1.0+fDensityCorr/(k*k));
}
// update sub-interval minimum value
alpha_i += delta;
}
// apply corrections due to variable transformation
xSection *= delta;
// final check
return std::max(xSection, 0.);
}
G4double G4SeltzerBergerModel::ComputeDXSectionPerAtom(G4double gammaEnergy)
{
G4double dxsec = 0.0;
@@ -205,17 +438,18 @@ G4double G4SeltzerBergerModel::ComputeDXSectionPerAtom(G4double gammaEnergy)
l.unlock();
}
// NOTE: SetupForMaterial should have been called before!
const G4double pt2 = fPrimaryKinEnergy*(fPrimaryKinEnergy+2.*kMC2);
const G4double pt2 = fPrimaryKinEnergy*(fPrimaryKinEnergy + twoMass);
const G4double invb2 = fPrimaryTotalEnergy*fPrimaryTotalEnergy/pt2;
G4double val = gSBDCSData[fCurrentIZ]->Value(x,y,fIndx,fIndy);
dxsec = val*invb2*CLHEP::millibarn/gBremFactor;
// e+ correction
if (!fIsElectron) {
const G4double invbeta1 = std::sqrt(invb2);
const G4double e2 = fPrimaryKinEnergy-gammaEnergy;
const G4double e2 = fPrimaryKinEnergy - gammaEnergy;
if (e2 > 0.0) {
const G4double invbeta2 = (e2+kMC2)/std::sqrt(e2*(e2+2.0*kMC2));
const G4double dum0 = kAlpha*fCurrentIZ*(invbeta1-invbeta2);
const G4double invbeta2 =
(e2 + CLHEP::electron_mass_c2)/std::sqrt(e2*(e2 + twoMass));
const G4double dum0 = kAlpha*fCurrentIZ*(invbeta1-invbeta2);
if (dum0 < gExpNumLimit) {
dxsec = 0.0;
} else {
@@ -235,7 +469,7 @@ G4SeltzerBergerModel::SampleSecondaries(std::vector<G4DynamicParticle*>* vdp,
G4double cutEnergy,
G4double maxEnergy)
{
const G4double kinEnergy = dp->GetKineticEnergy();
const G4double kinEnergy = dp->GetKineticEnergy();
const G4double logKinEnergy = dp->GetLogKineticEnergy();
const G4double tmin = std::min(cutEnergy, kinEnergy);
const G4double tmax = std::min(maxEnergy, kinEnergy);
@@ -246,9 +480,9 @@ G4SeltzerBergerModel::SampleSecondaries(std::vector<G4DynamicParticle*>* vdp,
SetupForMaterial(fPrimaryParticle, couple->GetMaterial(), kinEnergy);
const G4Element* elm = SelectTargetAtom(couple, fPrimaryParticle, kinEnergy,
logKinEnergy, tmin, tmax);
fCurrentIZ = std::max(std::min(elm->GetZasInt(),gMaxZet-1), 1);
fCurrentIZ = std::max(std::min(elm->GetZasInt(), gMaxZet-1), 1);
//
const G4double totMomentum = std::sqrt(kinEnergy*(fPrimaryTotalEnergy+kMC2));
const G4double totMomentum = std::sqrt(kinEnergy*(kinEnergy + twoMass));
/*
G4cout << "G4SeltzerBergerModel::SampleSecondaries E(MeV)= "
<< kinEnergy/MeV
@@ -268,14 +502,14 @@ G4SeltzerBergerModel::SampleSecondaries(std::vector<G4DynamicParticle*>* vdp,
// angles of the emitted gamma. ( Z - axis along the parent particle) use
// general interface
G4ThreeVector gamDir = GetAngularDistribution()->SampleDirection(dp,
fPrimaryTotalEnergy-gammaEnergy, fCurrentIZ, couple->GetMaterial());
fPrimaryTotalEnergy-gammaEnergy, fCurrentIZ, couple->GetMaterial());
// create G4DynamicParticle object for the emitted Gamma
auto gamma = new G4DynamicParticle(fGammaParticle, gamDir, gammaEnergy);
vdp->push_back(gamma);
//
// compute post-interaction kinematics of the primary e-/e+
G4ThreeVector dir =
(totMomentum*dp->GetMomentumDirection()-gammaEnergy*gamDir).unit();
(totMomentum*dp->GetMomentumDirection() - gammaEnergy*gamDir).unit();
const G4double finalE = kinEnergy - gammaEnergy;
/*
G4cout << "### G4SBModel: v= "
@@ -291,7 +525,7 @@ G4SeltzerBergerModel::SampleSecondaries(std::vector<G4DynamicParticle*>* vdp,
fParticleChange->ProposeTrackStatus(fStopAndKill);
fParticleChange->SetProposedKineticEnergy(0.0);
auto el = new G4DynamicParticle(
const_cast<G4ParticleDefinition*>(fPrimaryParticle), dir, finalE);
const_cast<G4ParticleDefinition*>(fPrimaryParticle), dir, finalE);
vdp->push_back(el);
} else { // continue tracking the primary e-/e+ otherwise
fParticleChange->SetProposedMomentumDirection(dir);
@@ -344,10 +578,12 @@ G4SeltzerBergerModel::SampleEnergyTransfer(const G4double kinEnergy,
v = gSBDCSData[fCurrentIZ]->Value(gammaEnergy/kinEnergy, y, fIndx, fIndy);
// e+ correction
if (!fIsElectron) {
const G4double e1 = kinEnergy - tmin;
const G4double invbeta1 = (e1+kMC2)/std::sqrt(e1*(e1+2.*kMC2));
const G4double e1 = kinEnergy - tmin;
const G4double invbeta1 =
(e1 + CLHEP::electron_mass_c2)/std::sqrt(e1*(e1 + twoMass));
const G4double e2 = kinEnergy-gammaEnergy;
const G4double invbeta2 = (e2+kMC2)/std::sqrt(e2*(e2+2.*kMC2));
const G4double invbeta2 =
(e2 + CLHEP::electron_mass_c2)/std::sqrt(e2*(e2 + twoMass));
const G4double dum0 = kAlpha*fCurrentIZ*(invbeta1-invbeta2);
if (dum0 < gExpNumLimit) {
v = 0.0;
@@ -377,17 +613,3 @@ G4SeltzerBergerModel::SampleEnergyTransfer(const G4double kinEnergy,
}
return gammaEnergy;
}
void G4SeltzerBergerModel::SetupForMaterial(const G4ParticleDefinition*,
const G4Material* mat,
G4double kineticEnergy)
{
fDensityFactor = gMigdalConstant*mat->GetElectronDensity();
// calculate threshold for density effect: gamma*k_p = sqrt(fDensityCorr)
fPrimaryKinEnergy = kineticEnergy;
fPrimaryTotalEnergy = kineticEnergy+CLHEP::electron_mass_c2;
fDensityCorr = fDensityFactor*fPrimaryTotalEnergy*fPrimaryTotalEnergy;
// flag for the base class
fIsLPMActive = false;
}