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
@@ -72,6 +72,7 @@
namespace { G4Mutex LivermoreBremsstrahlungModelMutex = G4MUTEX_INITIALIZER; }
using namespace std;
G4Physics2DVector* G4LivermoreBremsstrahlungModel::dataSB[] = {nullptr};
G4double G4LivermoreBremsstrahlungModel::ylimit[] = {0.0};
G4double G4LivermoreBremsstrahlungModel::expnumlim = -12.;
@@ -86,7 +87,6 @@ G4LivermoreBremsstrahlungModel::G4LivermoreBremsstrahlungModel(
: G4eBremsstrahlungRelModel(p,nam),useBicubicInterpolation(false)
{
SetLowEnergyLimit(10.0*eV);
SetLPMFlag(false);
SetAngularDistribution(new G4Generator2BS());
}
@@ -145,7 +145,7 @@ void G4LivermoreBremsstrahlungModel::ReadData(G4int Z, const char* path)
if(dataSB[Z]) { return; }
const char* datadir = path;
if(!datadir) {
if(nullptr == datadir) {
datadir = G4FindDataDir("G4LEDATA");
if(!datadir) {
G4Exception("G4LivermoreBremsstrahlungModel::ReadData()","em0006",
@@ -45,22 +45,26 @@
#include "G4SauterGavrilaAngularDistribution.hh"
#include "G4SystemOfUnits.hh"
#include "G4VAtomDeexcitation.hh"
#include "G4EmParameters.hh"
#include <thread>
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4PhysicsFreeVector* G4LivermorePhotoElectricModel::fCrossSection[] = {nullptr};
G4PhysicsFreeVector* G4LivermorePhotoElectricModel::fCrossSectionLE[] = {nullptr};
G4ElementData* G4LivermorePhotoElectricModel::fCrossSection = nullptr;
G4ElementData* G4LivermorePhotoElectricModel::fCrossSectionLE = nullptr;
std::vector<G4double>* G4LivermorePhotoElectricModel::fParamHigh[] = {nullptr};
std::vector<G4double>* G4LivermorePhotoElectricModel::fParamLow[] = {nullptr};
G4int G4LivermorePhotoElectricModel::fNShells[] = {0};
G4int G4LivermorePhotoElectricModel::fNShellsUsed[] = {0};
G4ElementData* G4LivermorePhotoElectricModel::fShellCrossSection = nullptr;
G4Material* G4LivermorePhotoElectricModel::fWater = nullptr;
G4double G4LivermorePhotoElectricModel::fWaterEnergyLimit = 0.0;
G4String G4LivermorePhotoElectricModel::fDataDirectory = "";
static std::once_flag applyOnce;
namespace
{
G4Mutex livPhotoeffMutex = G4MUTEX_INITIALIZER;
G4Mutex livPhotoeffMutex = G4MUTEX_INITIALIZER;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -97,10 +101,8 @@ G4LivermorePhotoElectricModel::G4LivermorePhotoElectricModel(const G4String& nam
G4LivermorePhotoElectricModel::~G4LivermorePhotoElectricModel()
{
if (IsMaster()) {
delete fShellCrossSection;
fShellCrossSection = nullptr;
for (G4int i = 0; i <= maxZ; ++i) {
if (isInitializer) {
for (G4int i = 0; i < ZMAXPE; ++i) {
if (fParamHigh[i]) {
delete fParamHigh[i];
fParamHigh[i] = nullptr;
@@ -109,57 +111,60 @@ G4LivermorePhotoElectricModel::~G4LivermorePhotoElectricModel()
delete fParamLow[i];
fParamLow[i] = nullptr;
}
if (fCrossSection[i]) {
delete fCrossSection[i];
fCrossSection[i] = nullptr;
}
if (fCrossSectionLE[i]) {
delete fCrossSectionLE[i];
fCrossSectionLE[i] = nullptr;
}
}
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4LivermorePhotoElectricModel::Initialise(const G4ParticleDefinition*, const G4DataVector&)
void G4LivermorePhotoElectricModel::Initialise(const G4ParticleDefinition*,
const G4DataVector&)
{
if (verboseLevel > 2) {
if (verboseLevel > 1) {
G4cout << "Calling G4LivermorePhotoElectricModel::Initialise() " << G4endl;
}
if (IsMaster()) {
// initialise static tables only once
std::call_once(applyOnce, [this]() { isInitializer = true; });
if (isInitializer) {
G4AutoLock l(&livPhotoeffMutex);
FindDirectoryPath();
if (fWater == nullptr) {
fWater = G4Material::GetMaterial("G4_WATER", false);
if (fWater == nullptr) {
fWater = G4Material::GetMaterial("Water", false);
}
if (fWater != nullptr) {
fWaterEnergyLimit = 13.6 * eV;
fWaterEnergyLimit = 13.6 * CLHEP::eV;
}
}
if (fShellCrossSection == nullptr) {
fShellCrossSection = new G4ElementData();
if (fCrossSection == nullptr) {
fCrossSection = new G4ElementData(ZMAXPE);
fCrossSection->SetName("PhotoEffXS");
fCrossSectionLE = new G4ElementData(ZMAXPE);
fCrossSectionLE->SetName("PhotoEffLowXS");
}
const G4ElementTable* elemTable = G4Element::GetElementTable();
std::size_t numElems = (*elemTable).size();
for (std::size_t ie = 0; ie < numElems; ++ie) {
const G4Element* elem = (*elemTable)[ie];
const G4int Z = std::min(maxZ, elem->GetZasInt());
if (fCrossSection[Z] == nullptr) {
ReadData(Z);
G4int Z = elem->GetZasInt();
if (Z < ZMAXPE) {
if (fCrossSection->GetElementData(Z) == nullptr) {
ReadData(Z);
}
}
}
l.unlock();
}
if (verboseLevel > 2) {
if (verboseLevel > 1) {
G4cout << "Loaded cross section files for new LivermorePhotoElectric model" << G4endl;
}
if (!isInitialised) {
isInitialised = true;
if (nullptr == fParticleChange) {
fParticleChange = GetParticleChangeForGamma();
fAtomDeexcitation = G4LossTableManager::Instance()->AtomDeexcitation();
}
@@ -169,16 +174,18 @@ void G4LivermorePhotoElectricModel::Initialise(const G4ParticleDefinition*, cons
fDeexcitationActive = fAtomDeexcitation->IsFluoActive();
}
if (verboseLevel > 0) {
G4cout << "LivermorePhotoElectric model is initialized " << G4endl << G4endl;
if (verboseLevel > 1) {
G4cout << "LivermorePhotoElectric model is initialized " << G4endl;
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4double G4LivermorePhotoElectricModel::CrossSectionPerVolume(const G4Material* material,
const G4ParticleDefinition* p,
G4double energy, G4double, G4double)
G4double
G4LivermorePhotoElectricModel::CrossSectionPerVolume(const G4Material* material,
const G4ParticleDefinition* p,
G4double energy,
G4double, G4double)
{
fCurrSection = 0.0;
if (fWater && (material == fWater || material->GetBaseMaterial() == fWater)) {
@@ -190,8 +197,8 @@ G4double G4LivermorePhotoElectricModel::CrossSectionPerVolume(const G4Material*
G4double energy4 = energy2 * energy2;
fCurrSection = material->GetDensity()
* (fSandiaCof[0] / energy + fSandiaCof[1] / energy2 + fSandiaCof[2] / energy3
+ fSandiaCof[3] / energy4);
* (fSandiaCof[0] / energy + fSandiaCof[1] / energy2 +
fSandiaCof[2] / energy3 + fSandiaCof[3] / energy4);
}
}
if (0.0 == fCurrSection) {
@@ -202,9 +209,11 @@ G4double G4LivermorePhotoElectricModel::CrossSectionPerVolume(const G4Material*
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4double G4LivermorePhotoElectricModel::ComputeCrossSectionPerAtom(const G4ParticleDefinition*,
G4double energy, G4double ZZ,
G4double, G4double, G4double)
G4double
G4LivermorePhotoElectricModel::ComputeCrossSectionPerAtom(const G4ParticleDefinition*,
G4double energy,
G4double ZZ, G4double,
G4double, G4double)
{
if (verboseLevel > 3) {
G4cout << "\n G4LivermorePhotoElectricModel::ComputeCrossSectionPerAtom():"
@@ -212,14 +221,15 @@ G4double G4LivermorePhotoElectricModel::ComputeCrossSectionPerAtom(const G4Parti
}
G4double cs = 0.0;
G4int Z = G4lrint(ZZ);
if (Z > maxZ) {
if (Z >= ZMAXPE || Z <= 0) {
return cs;
}
// if element was not initialised
// do initialisation safely for MT mode
if (fCrossSection[Z] == nullptr) {
InitialiseForElement(theGamma, Z);
if (fCrossSection->GetElementData(Z) == nullptr) {
InitialiseOnFly(Z);
if (fCrossSection->GetElementData(Z) == nullptr) { return cs; }
}
// 7: rows in the parameterization file; 5: number of parameters
@@ -236,27 +246,27 @@ G4double G4LivermorePhotoElectricModel::ComputeCrossSectionPerAtom(const G4Parti
G4double x4 = x2 * x2;
G4double x5 = x4 * x1;
cs = x1
* ((*(fParamHigh[Z]))[idx] + x1 * (*(fParamHigh[Z]))[idx + 1]
+ x2 * (*(fParamHigh[Z]))[idx + 2] + x3 * (*(fParamHigh[Z]))[idx + 3]
+ x4 * (*(fParamHigh[Z]))[idx + 4] + x5 * (*(fParamHigh[Z]))[idx + 5]);
cs = x1 *
((*(fParamHigh[Z]))[idx] + x1 * (*(fParamHigh[Z]))[idx + 1]
+ x2 * (*(fParamHigh[Z]))[idx + 2] + x3 * (*(fParamHigh[Z]))[idx + 3]
+ x4 * (*(fParamHigh[Z]))[idx + 4] + x5 * (*(fParamHigh[Z]))[idx + 5]);
}
// low energy parameterisation
else if (energy >= (*(fParamLow[Z]))[0]) {
G4double x4 = x2 * x2;
G4double x5 = x4 * x1; // this variable usage can probably be optimized
cs = x1
* ((*(fParamLow[Z]))[idx] + x1 * (*(fParamLow[Z]))[idx + 1]
+ x2 * (*(fParamLow[Z]))[idx + 2] + x3 * (*(fParamLow[Z]))[idx + 3]
+ x4 * (*(fParamLow[Z]))[idx + 4] + x5 * (*(fParamLow[Z]))[idx + 5]);
cs = x1 *
((*(fParamLow[Z]))[idx] + x1 * (*(fParamLow[Z]))[idx + 1]
+ x2 * (*(fParamLow[Z]))[idx + 2] + x3 * (*(fParamLow[Z]))[idx + 3]
+ x4 * (*(fParamLow[Z]))[idx + 4] + x5 * (*(fParamLow[Z]))[idx + 5]);
}
// Tabulated values above k-shell ionization energy
else if (energy >= (*(fParamHigh[Z]))[1]) {
cs = x3 * (fCrossSection[Z])->Value(energy);
cs = x3 * (fCrossSection->GetElementData(Z))->Value(energy);
}
// Tabulated values below k-shell ionization energy
else {
cs = x3 * (fCrossSectionLE[Z])->Value(energy);
cs = x3 * (fCrossSectionLE->GetElementData(Z))->Value(energy);
}
if (verboseLevel > 1) {
G4cout << "G4LivermorePhotoElectricModel: E(keV)= " << energy / keV << " Z= " << Z
@@ -296,13 +306,13 @@ void G4LivermorePhotoElectricModel::SampleSecondaries(std::vector<G4DynamicParti
// Select randomly one element in the current material
const G4Element* elm = SelectRandomAtom(material, theGamma, gammaEnergy);
G4int Z = std::min(elm->GetZasInt(), maxZ);
G4int Z = elm->GetZasInt();
// Select the ionised shell in the current atom according to shell
// cross sections
// If element was not initialised gamma should be absorbed
if (fCrossSection[Z] == nullptr) {
if (Z >= ZMAXPE || fCrossSection->GetElementData(Z) == nullptr) {
fParticleChange->ProposeLocalEnergyDeposit(gammaEnergy);
return;
}
@@ -322,17 +332,17 @@ void G4LivermorePhotoElectricModel::SampleSecondaries(std::vector<G4DynamicParti
// so cross section is not real
G4double rand = G4UniformRand();
G4double cs0 = rand
* ((*(fParamHigh[Z]))[idx] + x1 * (*(fParamHigh[Z]))[idx + 1]
+ x2 * (*(fParamHigh[Z]))[idx + 2] + x3 * (*(fParamHigh[Z]))[idx + 3]
+ x4 * (*(fParamHigh[Z]))[idx + 4] + x5 * (*(fParamHigh[Z]))[idx + 5]);
G4double cs0 = rand *
((*(fParamHigh[Z]))[idx] + x1 * (*(fParamHigh[Z]))[idx + 1]
+ x2 * (*(fParamHigh[Z]))[idx + 2] + x3 * (*(fParamHigh[Z]))[idx + 3]
+ x4 * (*(fParamHigh[Z]))[idx + 4] + x5 * (*(fParamHigh[Z]))[idx + 5]);
for (shellIdx = 0; shellIdx < nn; ++shellIdx) {
idx = shellIdx * 7 + 2;
if (gammaEnergy > (*(fParamHigh[Z]))[idx - 1]) {
G4double cs = (*(fParamHigh[Z]))[idx] + x1 * (*(fParamHigh[Z]))[idx + 1]
+ x2 * (*(fParamHigh[Z]))[idx + 2] + x3 * (*(fParamHigh[Z]))[idx + 3]
+ x4 * (*(fParamHigh[Z]))[idx + 4] + x5 * (*(fParamHigh[Z]))[idx + 5];
+ x2 * (*(fParamHigh[Z]))[idx + 2] + x3 * (*(fParamHigh[Z]))[idx + 3]
+ x4 * (*(fParamHigh[Z]))[idx + 4] + x5 * (*(fParamHigh[Z]))[idx + 5];
if (cs >= cs0) {
break;
@@ -352,16 +362,16 @@ void G4LivermorePhotoElectricModel::SampleSecondaries(std::vector<G4DynamicParti
std::size_t idx = nn * 7 - 5;
// when do sampling common factors are not taken into account
// so cross section is not real
G4double cs0 = G4UniformRand()
* ((*(fParamLow[Z]))[idx] + x1 * (*(fParamLow[Z]))[idx + 1]
+ x2 * (*(fParamLow[Z]))[idx + 2] + x3 * (*(fParamLow[Z]))[idx + 3]
+ x4 * (*(fParamLow[Z]))[idx + 4] + x5 * (*(fParamLow[Z]))[idx + 5]);
G4double cs0 = G4UniformRand() *
((*(fParamLow[Z]))[idx] + x1 * (*(fParamLow[Z]))[idx + 1]
+ x2 * (*(fParamLow[Z]))[idx + 2] + x3 * (*(fParamLow[Z]))[idx + 3]
+ x4 * (*(fParamLow[Z]))[idx + 4] + x5 * (*(fParamLow[Z]))[idx + 5]);
for (shellIdx = 0; shellIdx < nn; ++shellIdx) {
idx = shellIdx * 7 + 2;
if (gammaEnergy > (*(fParamLow[Z]))[idx - 1]) {
G4double cs = (*(fParamLow[Z]))[idx] + x1 * (*(fParamLow[Z]))[idx + 1]
+ x2 * (*(fParamLow[Z]))[idx + 2] + x3 * (*(fParamLow[Z]))[idx + 3]
+ x4 * (*(fParamLow[Z]))[idx + 4] + x5 * (*(fParamLow[Z]))[idx + 5];
+ x2 * (*(fParamLow[Z]))[idx + 2] + x3 * (*(fParamLow[Z]))[idx + 3]
+ x4 * (*(fParamLow[Z]))[idx + 4] + x5 * (*(fParamLow[Z]))[idx + 5];
if (cs >= cs0) {
break;
}
@@ -378,17 +388,17 @@ void G4LivermorePhotoElectricModel::SampleSecondaries(std::vector<G4DynamicParti
if (gammaEnergy >= (*(fParamHigh[Z]))[1]) {
// above K-shell binding energy
cs *= (fCrossSection[Z])->Value(gammaEnergy);
cs *= fCrossSection->GetElementData(Z)->Value(gammaEnergy);
}
else {
// below K-shell binding energy
cs *= (fCrossSectionLE[Z])->Value(gammaEnergy);
cs *= fCrossSectionLE->GetElementData(Z)->Value(gammaEnergy);
}
for (G4int j = 0; j < (G4int)nn; ++j) {
shellIdx = (std::size_t)fShellCrossSection->GetComponentID(Z, j);
shellIdx = (std::size_t)fCrossSection->GetComponentID(Z, j);
if (gammaEnergy > (*(fParamLow[Z]))[7 * shellIdx + 1]) {
cs -= fShellCrossSection->GetValueForComponent(Z, j, gammaEnergy);
cs -= fCrossSection->GetValueForComponent(Z, j, gammaEnergy);
}
if (cs <= 0.0 || j + 1 == (G4int)nn) {
break;
@@ -423,11 +433,12 @@ void G4LivermorePhotoElectricModel::SampleSecondaries(std::vector<G4DynamicParti
aDynamicGamma, eKineticEnergy, (G4int)shellIdx, couple->GetMaterial());
// The electron is created
auto electron = new G4DynamicParticle(theElectron, electronDirection, eKineticEnergy);
auto electron =
new G4DynamicParticle(theElectron, electronDirection, eKineticEnergy);
fvect->push_back(electron);
// Sample deexcitation
if (shell) {
if (nullptr != shell) {
G4int index = couple->GetIndex();
if (fAtomDeexcitation->CheckDeexcitationActiveRegion(index)) {
std::size_t nbefore = fvect->size();
@@ -483,26 +494,27 @@ const G4String& G4LivermorePhotoElectricModel::FindDirectoryPath()
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4LivermorePhotoElectricModel::ReadData(G4int ZZ)
void G4LivermorePhotoElectricModel::ReadData(G4int Z)
{
if (verboseLevel > 1) {
G4cout << "Calling ReadData() of G4LivermorePhotoElectricModel for Z=" << ZZ << G4endl;
if (Z <= 0 || Z >= ZMAXPE) {
G4cout << "G4LivermorePhotoElectricModel::ReadData() Warning: Z="
<< Z << " is out of range - request ignored" << G4endl;
return;
}
if (verboseLevel > 1) {
G4cout << "G4LivermorePhotoElectricModel::ReadData() for Z=" << Z << G4endl;
}
G4int Z = std::min(ZZ, maxZ);
if (fCrossSection[Z] != nullptr) {
if (fCrossSection->GetElementData(Z) != nullptr) {
return;
}
// spline for photoeffect total x-section above K-shell when using EPDL97
// but below the parameterized ones
if (G4EmParameters::Instance()->LivermoreDataDir() == "livermore") {
fCrossSection[Z] = new G4PhysicsFreeVector(true);
}
else {
fCrossSection[Z] = new G4PhysicsFreeVector();
}
G4bool spline = (G4EmParameters::Instance()->LivermoreDataDir() == "livermore");
G4int number = G4EmParameters::Instance()->NumberForFreeVector();
auto pv = new G4PhysicsFreeVector(spline);
// fDataDirectory will be defined after these lines
std::ostringstream ost;
@@ -510,8 +522,8 @@ void G4LivermorePhotoElectricModel::ReadData(G4int ZZ)
std::ifstream fin(ost.str().c_str());
if (!fin.is_open()) {
G4ExceptionDescription ed;
ed << "G4LivermorePhotoElectricModel data file <" << ost.str().c_str() << "> is not opened!"
<< G4endl;
ed << "G4LivermorePhotoElectricModel data file <"
<< ost.str().c_str() << "> is not opened!" << G4endl;
G4Exception("G4LivermorePhotoElectricModel::ReadData()", "em0003", FatalException, ed,
"G4LEDATA version should be G4EMLOW8.0 or later.");
return;
@@ -520,9 +532,11 @@ void G4LivermorePhotoElectricModel::ReadData(G4int ZZ)
G4cout << "File " << ost.str().c_str() << " is opened by G4LivermorePhotoElectricModel"
<< G4endl;
}
fCrossSection[Z]->Retrieve(fin, true);
fCrossSection[Z]->ScaleVector(MeV, barn);
fCrossSection[Z]->FillSecondDerivatives();
pv->Retrieve(fin, true);
pv->ScaleVector(MeV, barn);
pv->FillSecondDerivatives();
pv->EnableLogBinSearch(number);
fCrossSection->InitialiseForElement(Z, pv);
fin.close();
// read high-energy fit parameters
@@ -535,8 +549,8 @@ void G4LivermorePhotoElectricModel::ReadData(G4int ZZ)
std::ifstream fin1(ost1.str().c_str());
if (!fin1.is_open()) {
G4ExceptionDescription ed;
ed << "G4LivermorePhotoElectricModel data file <" << ost1.str().c_str() << "> is not opened!"
<< G4endl;
ed << "G4LivermorePhotoElectricModel data file <"
<< ost1.str().c_str() << "> is not opened!" << G4endl;
G4Exception("G4LivermorePhotoElectricModel::ReadData()", "em0003", FatalException, ed,
"G4LEDATA version should be G4EMLOW7.2 or later.");
return;
@@ -645,7 +659,7 @@ void G4LivermorePhotoElectricModel::ReadData(G4int ZZ)
if (nShellLimit < n2) {
n2 = nShellLimit;
}
fShellCrossSection->InitialiseForComponent(Z, n2); // number of shells
fCrossSection->InitialiseForComponent(Z, n2); // number of shells
fNShellsUsed[Z] = n2;
if (1 < n2) {
@@ -672,16 +686,17 @@ void G4LivermorePhotoElectricModel::ReadData(G4int ZZ)
auto v = new G4PhysicsFreeVector(n3, x, y);
for (G4int j = 0; j < n3; ++j) {
fin2 >> x >> y;
v->PutValues(j, x * MeV, y * barn);
v->PutValues(j, x * CLHEP::MeV, y * CLHEP::barn);
}
fShellCrossSection->AddComponent(Z, n4, v);
v->EnableLogBinSearch(number);
fCrossSection->AddComponent(Z, n4, v);
}
fin2.close();
}
// no spline for photoeffect total x-section below K-shell
if (1 < fNShells[Z]) {
fCrossSectionLE[Z] = new G4PhysicsFreeVector();
auto pv1 = new G4PhysicsFreeVector(false);
std::ostringstream ost3;
ost3 << fDataDirectory << "pe-le-cs-" << Z << ".dat";
std::ifstream fin3(ost3.str().c_str());
@@ -697,8 +712,10 @@ void G4LivermorePhotoElectricModel::ReadData(G4int ZZ)
G4cout << "File " << ost3.str().c_str() << " is opened by G4LivermorePhotoElectricModel"
<< G4endl;
}
fCrossSectionLE[Z]->Retrieve(fin3, true);
fCrossSectionLE[Z]->ScaleVector(MeV, barn);
pv1->Retrieve(fin3, true);
pv1->ScaleVector(CLHEP::MeV, CLHEP::barn);
pv1->EnableLogBinSearch(number);
fCrossSectionLE->InitialiseForElement(Z, pv1);
fin3.close();
}
}
@@ -707,30 +724,45 @@ void G4LivermorePhotoElectricModel::ReadData(G4int ZZ)
G4double G4LivermorePhotoElectricModel::GetBindingEnergy(G4int Z, G4int shell)
{
if (Z < 1 || Z > maxZ) {
if (Z < 1 || Z >= ZMAXPE) {
return -1;
} // If Z is out of the supported return 0
// If necessary load data for Z
InitialiseForElement(theGamma, Z);
if (fCrossSection[Z] == nullptr || shell < 0 || shell >= fNShellsUsed[Z]) {
InitialiseOnFly(Z);
if (fCrossSection->GetElementData(Z) == nullptr ||
shell < 0 || shell >= fNShellsUsed[Z]) {
return -1;
}
if (Z > 2) {
return fShellCrossSection->GetComponentDataByIndex(Z, shell)->Energy(0);
return fCrossSection->GetComponentDataByIndex(Z, shell)->Energy(0);
}
else {
return fCrossSection[Z]->Energy(0);
return fCrossSection->GetElementData(Z)->Energy(0);
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4LivermorePhotoElectricModel::InitialiseForElement(const G4ParticleDefinition*, G4int Z)
void
G4LivermorePhotoElectricModel::InitialiseForElement(const G4ParticleDefinition*, G4int Z)
{
if (fCrossSection[Z] == nullptr) {
if (fCrossSection == nullptr) {
fCrossSection = new G4ElementData(ZMAXPE);
fCrossSection->SetName("PhotoEffXS");
fCrossSectionLE = new G4ElementData(ZMAXPE);
fCrossSectionLE->SetName("PhotoEffLowXS");
}
ReadData(Z);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4LivermorePhotoElectricModel::InitialiseOnFly(G4int Z)
{
if (fCrossSection->GetElementData(Z) == nullptr && Z > 0 && Z < ZMAXPE) {
G4AutoLock l(&livPhotoeffMutex);
if (fCrossSection[Z] == nullptr) {
if (fCrossSection->GetElementData(Z) == nullptr) {
ReadData(Z);
}
l.unlock();
@@ -0,0 +1,320 @@
//
// ********************************************************************
// * License and Disclaimer *
// * *
// * The Geant4 software is copyright of the Copyright Holders of *
// * the Geant4 Collaboration. It is provided under the terms and *
// * conditions of the Geant4 Software License, included in the file *
// * LICENSE and available at http://cern.ch/geant4/license . These *
// * include a list of copyright holders. *
// * *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work make any representation or warranty, express or implied, *
// * regarding this software system or assume any liability for its *
// * use. Please see the license in the file LICENSE and URL above *
// * for the full disclaimer and the limitation of liability. *
// * *
// * This code implementation is the result of the scientific and *
// * technical work of the GEANT4 collaboration. *
// * By using, copying, modifying or distributing the software (or *
// * any work based on the software) you agree to acknowledge its *
// * use in resulting scientific publications, and indicate your *
// * acceptance of all terms of the Geant4 Software license. *
// ********************************************************************
//
//
// G4MicroElecCapture.cc,
// 2011/08/29 A.Valentin, M. Raine are with CEA [a]
// 2020/05/20 P. Caron, C. Inguimbert are with ONERA [b]
// Q. Gibaru is with CEA [a], ONERA [b] and CNES [c]
// M. Raine and D. Lambert are with CEA [a]
//
// A part of this work has been funded by the French space agency(CNES[c])
// [a] CEA, DAM, DIF - 91297 ARPAJON, France
// [b] ONERA - DPHY, 2 avenue E.Belin, 31055 Toulouse, France
// [c] CNES, 18 av.E.Belin, 31401 Toulouse CEDEX, France
//
// Based on the following publications
// - A.Valentin, M. Raine,
// Inelastic cross-sections of low energy electrons in silicon
// for the simulation of heavy ion tracks with the Geant4-DNA toolkit,
// NSS Conf. Record 2010, pp. 80-85
// https://doi.org/10.1109/NSSMIC.2010.5873720
//
// - A.Valentin, M. Raine, M.Gaillardin, P.Paillet
// Geant4 physics processes for microdosimetry simulation:
// very low energy electromagnetic models for electrons in Silicon,
// https://doi.org/10.1016/j.nimb.2012.06.007
// NIM B, vol. 288, pp. 66-73, 2012, part A
// heavy ions in Si, NIM B, vol. 287, pp. 124-129, 2012, part B
// https://doi.org/10.1016/j.nimb.2012.07.028
//
// - M. Raine, M. Gaillardin, P. Paillet
// Geant4 physics processes for silicon microdosimetry simulation:
// Improvements and extension of the energy-range validity up to 10 GeV/nucleon
// NIM B, vol. 325, pp. 97-100, 2014
// https://doi.org/10.1016/j.nimb.2014.01.014
//
// - J. Pierron, C. Inguimbert, M. Belhaj, T. Gineste, J. Puech, M. Raine
// Electron emission yield for low energy electrons:
// Monte Carlo simulation and experimental comparison for Al, Ag, and Si
// Journal of Applied Physics 121 (2017) 215107.
// https://doi.org/10.1063/1.4984761
//
// - P. Caron,
// Study of Electron-Induced Single-Event Upset in Integrated Memory Devices
// PHD, 16th October 2019
//
// - Q.Gibaru, C.Inguimbert, P.Caron, M.Raine, D.Lambert, J.Puech,
// Geant4 physics processes for microdosimetry and secondary electron emission simulation :
// Extension of MicroElec to very low energies and new materials
// NIM B, 2020, in review.
//
//----------------------------------------------------------------------------
//
// ClassName: G4MicroElecCapture derivated from G4ElectronCapture (V Ivanchenko)
//
// Description: The process to kill particles to save CPU
//
// Author: C. Inguimbert 31 january 2022 derivated from G4ElectronCapture (V.Ivanchenko 31 August 2010)
//
//----------------------------------------------------------------------------
//
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
#include "G4MicroElecCapture.hh"
#include "G4SystemOfUnits.hh"
#include "G4ParticleDefinition.hh"
#include "G4Step.hh"
#include "G4PhysicalConstants.hh"
#include "G4Track.hh"
#include "G4Region.hh"
#include "G4RegionStore.hh"
#include "G4Electron.hh"
#include "G4Pow.hh"
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4MicroElecCapture::G4MicroElecCapture(const G4String& regName, G4double ekinlim)
: G4VDiscreteProcess("MicroElecCapture", fElectromagnetic), kinEnergyThreshold(ekinlim),
regionName(regName), region(0)
{
if(regName == "" || regName == "world")
{
regionName = "DefaultRegionForTheWorld";
}
isInitialised = false;
pParticleChange = &fParticleChange;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4MicroElecCapture::~G4MicroElecCapture()
{
for (auto pos = tableWF.cbegin(); pos != tableWF.cend(); ++pos)
{
G4MicroElecMaterialStructure* table = pos->second;
delete table;
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4MicroElecCapture::SetKinEnergyLimit(G4double val)
{
kinEnergyThreshold = val;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4MicroElecCapture::BuildPhysicsTable(const G4ParticleDefinition&)
{
region = (G4RegionStore::GetInstance())->GetRegion(regionName);
// if(region && verboseLevel > 0) {
G4cout << "### G4MicroElecCapture: Tracking cut E(MeV) = "
<< kinEnergyThreshold/MeV << " is assigned to " << regionName
<< G4endl;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4bool G4MicroElecCapture::IsApplicable(const G4ParticleDefinition&)
{
return true;
}
void G4MicroElecCapture::Initialise()
{
if (isInitialised) { return; }
G4ProductionCutsTable* theCoupleTable = G4ProductionCutsTable::GetProductionCutsTable();
G4int numOfCouples = (G4int)theCoupleTable->GetTableSize();
G4cout << numOfCouples << G4endl;
for (G4int i = 0; i < numOfCouples; ++i)
{
const G4Material* material = theCoupleTable->GetMaterialCutsCouple(i)->GetMaterial();
G4cout << "G4Capture, Material " << i + 1 << " / "
<< numOfCouples << " : " << material->GetName() << G4endl;
if (material->GetName() == "Vacuum")
{
tableWF[material->GetName()] = 0;
continue;
}
G4String mat = material->GetName();
G4MicroElecMaterialStructure* str = new G4MicroElecMaterialStructure(mat);
tableWF[mat] = str;
}
isInitialised = true;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4VParticleChange* G4MicroElecCapture::PostStepDoIt(const G4Track& aTrack,
const G4Step&)
{
if (!isInitialised) { Initialise(); }
G4String mat = aTrack.GetMaterial()->GetName();
G4int Ztarget = ((*(aTrack.GetMaterial()->GetElementVector()))[0])->GetZasInt();
G4int Atarget = ((*(aTrack.GetMaterial()->GetElementVector()))[0])->GetAtomicMassAmu();
G4double Nbelements = aTrack.GetMaterial()->GetNumberOfElements();
G4double moleculeMass = aTrack.GetMaterial()->GetMassOfMolecule() / amu;
auto FractionMass = aTrack.GetMaterial()->GetFractionVector();
G4int Zinc = aTrack.GetParticleDefinition()->GetAtomicNumber();
G4int Ainc = aTrack.GetParticleDefinition()->GetAtomicMass();
G4String IncPartName = aTrack.GetParticleDefinition()->GetParticleName();
G4double NIEdep = 0.0;
for (G4int i = 0; i < Nbelements; ++i)
{
Ztarget = ((*(aTrack.GetMaterial()->GetElementVector()))[i])->GetZasInt();
Atarget = ((*(aTrack.GetMaterial()->GetElementVector()))[i])->GetAtomicMassAmu();
NIEdep = NIEdep + moleculeMass*FractionMass[i] / Atarget*G_Lindhard_Rob(aTrack.GetKineticEnergy(), Zinc, Ainc, Ztarget, Atarget);
}
WorkFunctionTable::iterator matWF;
matWF = tableWF.find(mat);
if (matWF == tableWF.end())
{
G4String str = "Material ";
str += mat + " not found!";
G4Exception("G4MicroElecCapture::PostStepGPIL", "em0002",
FatalException, str);
return nullptr;
}
else
{
G4MicroElecMaterialStructure* str = matWF->second;
pParticleChange->Initialize(aTrack);
pParticleChange->ProposeTrackStatus(fStopAndKill);
G4double InitE = str->GetEnergyGap() + str->GetInitialEnergy();
if (IncPartName == "e-")
{
// metals = Non ionizing deposited energy = 0.0
if (((str->GetEnergyGap()) / eV)<(0.001))
{
pParticleChange->ProposeNonIonizingEnergyDeposit(0.0);
pParticleChange->ProposeLocalEnergyDeposit(aTrack.GetKineticEnergy());
}
else // MicroElec materials Non ionizing deposited energy different from zero
{
G4int c = (G4int)((aTrack.GetKineticEnergy()) / (InitE));
pParticleChange->ProposeNonIonizingEnergyDeposit(aTrack.GetKineticEnergy() - InitE*c);
pParticleChange->ProposeLocalEnergyDeposit(aTrack.GetKineticEnergy());
}
}
else
{
if ((IncPartName == "Genericion") || (IncPartName == "alpha")
|| (IncPartName == "He3") || (IncPartName == "deuteron")
|| (IncPartName == "triton") || (IncPartName == "proton"))
{
pParticleChange->ProposeNonIonizingEnergyDeposit(NIEdep);
pParticleChange->ProposeLocalEnergyDeposit(aTrack.GetKineticEnergy());
}
else
{
pParticleChange->ProposeNonIonizingEnergyDeposit(0.0);
pParticleChange->ProposeLocalEnergyDeposit(aTrack.GetKineticEnergy());
}
}
} // matWF == tableWF.end())
fParticleChange.SetProposedKineticEnergy(0.0);
return pParticleChange;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4MicroElecCapture::GetMeanFreePath(const G4Track& aTrack, G4double,
G4ForceCondition*)
{
G4String material = aTrack.GetMaterial()->GetName();
// test particle type in order to applied the capture to both electrons, protons and heavy ions
if ((aTrack.GetParticleDefinition()->GetParticleName()) == "e-")
{
if (material != "G4_ALUMINUM_OXIDE" && material != "G4_SILICON_DIOXIDE"
&& material != "G4_BORON_NITRIDE")
{
return DBL_MAX;
}
G4double S = 0;
G4double y = 0;
if (material == "G4_ALUMINUM_OXIDE")
{
S = 1 * (1 / nm);
y = 0.25 * (1 / eV);
}
if (material == "G4_SILICON_DIOXIDE")
{
S = 0.3 * (1 / nm);
y = 0.2 * (1 / eV);
}
if (material == "G4_BORON_NITRIDE")
{
S = 0 * (1 / nm);
y = 1 * (1 / eV);
}
G4double P = S * G4Exp(-y * aTrack.GetKineticEnergy());
if (P <= 0) { return DBL_MAX; }
else { return 1 / P; }
}
else return DBL_MAX;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4MicroElecCapture::G_Lindhard_Rob(G4double Trecoil, G4int Zrecoil, G4int Arecoil, G4int Zcible, G4int Acible)
{
G4double Lind =0.0;
if (Arecoil <= 0 || Zrecoil == 0)
{
Lind = 0.0;
}
else
{
G4double El = 30.724 * Zcible * Zrecoil
* std::pow((G4Pow::GetInstance()->Z23(Zcible) + G4Pow::GetInstance()->Z23(Zrecoil)), 0.5)
* (Arecoil + Acible) / Acible;
// multiplication by 1e6 to change El from eV to MeV
G4double e = Trecoil / (El * CLHEP::eV);
G4double Fl = (0.0793 * G4Pow::GetInstance()->Z23(Zrecoil) * std::pow(Zcible, 0.5) * std::pow((Arecoil + Acible), 1.5))
/ (std::pow((G4Pow::GetInstance()->Z23(Zcible) + G4Pow::GetInstance()->Z23(Zrecoil)), 3. / 4.) * std::pow(Arecoil, 3. / 2.) * std::pow(Acible, 1. / 2.));
Lind = 1. / (1 + Fl * (3.4008 * std::pow(e, 1. / 6.) + 0.40244 * std::pow(e, 3. / 4.) + e));
// to get the energie that go into displacement
Lind = Lind * Trecoil;
}
return Lind;
}
@@ -78,6 +78,7 @@
#include "G4Exp.hh"
#include "G4Material.hh"
#include "G4String.hh"
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -348,6 +349,18 @@ G4double G4MicroElecElasticModel_new::CrossSectionPerVolume(const G4Material* ma
return AcousticCrossSectionPerVolume(ekin, kbz, rho, cs, Aac, Eac, prefactor);
}
else if (currentMaterialName == "ALUMINUM_OXIDE" && ekin < 20 * eV) {
acousticModelEnabled = true;
//Values for Al2O3
G4double kbz = 8871930614.247564,
rho = 3.97 * 1000, // [g/cm3] * 1000
cs = 233329.07733059773, //Sound speed
Aac = 2.9912494342262614e-19, //A screening parameter
Eac = 2.1622471654789847e-18, //C deformation potential
prefactor = 1;
return AcousticCrossSectionPerVolume(ekin, kbz, rho, cs, Aac, Eac, prefactor);
}
//Elastic
else {
acousticModelEnabled = false;
@@ -395,6 +408,11 @@ G4double G4MicroElecElasticModel_new::CrossSectionPerVolume(const G4Material* ma
G4cout << " - Cross section per Si atom (cm^2)=" << sigma / cm / cm << G4endl;
G4cout << " - Cross section per Si atom (cm^-1)=" << sigma*density / (1. / cm) << G4endl;
}
// Hsing-YinChangaAndrewAlvaradoaTreyWeberaJaimeMarianab Monte Carlo modeling of low-energy electron-induced secondary electron emission yields in micro-architected boron nitride surfaces - ScienceDirect, (n.d.). https://www.sciencedirect.com/science/article/pii/S0168583X19304069 (accessed April 1, 2022).
if (currentMaterialName == "BORON_NITRIDE") {
sigma = sigma * tanh(0.5 * pow(ekin / 5.2e-6, 2));
}
return sigma*density;
}
}
@@ -75,7 +75,7 @@
#include "globals.hh"
#include "G4MicroElecInelasticModel_new.hh"
#include "G4MicroElecInelasticModel_new.hh"
#include "G4PhysicalConstants.hh"
#include "G4SystemOfUnits.hh"
#include "G4ios.hh"
@@ -143,37 +143,22 @@ G4MicroElecInelasticModel_new::~G4MicroElecInelasticModel_new()
}
tableTCS.clear();
dataDiffCSMap::iterator iterator_proba;
// (1)
for (iterator_proba = eNrjTransStorage.begin(); iterator_proba != eNrjTransStorage.end(); ++iterator_proba) {
vector<TriDimensionMap>* eNrjTransfData = iterator_proba->second;
eNrjTransfData->clear();
delete eNrjTransfData;
for (auto const & obj : eNrjTransStorage) {
delete obj.second;
}
eNrjTransStorage.clear();
for (iterator_proba = pNrjTransStorage.begin(); iterator_proba != pNrjTransStorage.end(); ++iterator_proba) {
vector<TriDimensionMap>* pNrjTransfData = iterator_proba->second;
pNrjTransfData->clear();
delete pNrjTransfData;
for (auto const & obj : pNrjTransStorage) {
delete obj.second;
}
pNrjTransStorage.clear();
// (2)
for (iterator_proba = eDiffDatatable.begin(); iterator_proba != eDiffDatatable.end(); ++iterator_proba) {
vector<TriDimensionMap>* eDiffCrossSectionData = iterator_proba->second;
eDiffCrossSectionData->clear();
delete eDiffCrossSectionData;
}
eDiffDatatable.clear();
for (iterator_proba = pDiffDatatable.begin(); iterator_proba != pDiffDatatable.end(); ++iterator_proba) {
vector<TriDimensionMap>* pDiffCrossSectionData = iterator_proba->second;
pDiffCrossSectionData->clear();
delete pDiffCrossSectionData;
// (2)
for (auto const & obj : eDiffDatatable) {
delete obj.second;
}
pDiffDatatable.clear();
for (auto const & obj : pDiffDatatable) {
delete obj.second;
}
// (3)
dataProbaShellMap::iterator iterator_probaShell;
@@ -309,7 +294,6 @@ void G4MicroElecInelasticModel_new::Initialise(const G4ParticleDefinition* parti
if (fasterCode) G4Exception("G4MicroElecInelasticModel_new::Initialise", "em0003",
FatalException, sortieString.c_str());
else {
G4Exception("G4MicroElecInelasticModel_new::Initialise", "em0003",
FatalException, "Missing data file:/microelec/sigmadiff_inelastic_e_Si.dat");
@@ -478,13 +462,13 @@ void G4MicroElecInelasticModel_new::Initialise(const G4ParticleDefinition* parti
pIncidentEnergyStorage[mat] = pTdummyVec;
//Cleanup support vectors
// delete pNrjTransfData;
// delete eVecm;
// delete pDiffCrossSectionData;
// delete pProbaShellMap;
//delete pNrjTransfData;
//delete eVecm;
//delete pDiffCrossSectionData;
//delete pProbaShellMap;
}
tableTCS[mat] = tableData;
}
}
if (particle==electronDef)
{
SetLowEnergyLimit(lowEnergyLimit[electron]);
@@ -669,6 +653,8 @@ void G4MicroElecInelasticModel_new::SampleSecondaries(std::vector<G4DynamicParti
G4double bindingEnergy = currentMaterialStructure->Energy(Shell);
G4double limitEnergy = currentMaterialStructure->GetLimitEnergy(Shell);
G4bool weaklyBound = currentMaterialStructure->IsShellWeaklyBound(Shell);
if (verboseLevel > 3)
{
@@ -676,14 +662,18 @@ void G4MicroElecInelasticModel_new::SampleSecondaries(std::vector<G4DynamicParti
G4cout << "Shell: " << Shell << ", energy: " << bindingEnergy/eV << G4endl;
}
if (k<limitEnergy) {
if (weaklyBound && k > currentMaterialStructure->GetEnergyGap()) {
limitEnergy = currentMaterialStructure->GetEnergyGap();
}
else return; }
// sample deexcitation
std::size_t secNumberInit = 0; // need to know at a certain point the energy of secondaries
std::size_t secNumberFinal = 0; // So I'll make the difference and then sum the energies
//SI: additional protection if tcs interpolation method is modified
//if (k<bindingEnergy) return;
if (k<limitEnergy) return;
// G4cout << currentMaterial << G4endl;
G4int Z = currentMaterialStructure->GetZ(Shell);
G4int shellEnum = currentMaterialStructure->GetEADL_Enumerator(Shell);
@@ -745,10 +735,20 @@ void G4MicroElecInelasticModel_new::SampleSecondaries(std::vector<G4DynamicParti
for (std::size_t j=secNumberInit; j < secNumberFinal; ++j) {
deexSecEnergy = deexSecEnergy + (*fvect)[j]->GetKineticEnergy();
}
if (SEFromFermiLevel) limitEnergy = currentMaterialStructure->GetEnergyGap();
fParticleChangeForGamma->SetProposedKineticEnergy(ekin - secondaryKinetic - limitEnergy); //Ef = Ei-(Q-El)-El = Ei-Q
fParticleChangeForGamma->ProposeLocalEnergyDeposit(limitEnergy - deexSecEnergy);
// correction CI 12/01/2023 limit energy = gap
//if (SEFromFermiLevel) limitEnergy = currentMaterialStructure->GetEnergyGap();
//fParticleChangeForGamma->SetProposedKineticEnergy(ekin - secondaryKinetic - limitEnergy); //Ef = Ei-(Q-El)-El = Ei-Q
//fParticleChangeForGamma->ProposeLocalEnergyDeposit(limitEnergy - deexSecEnergy);
// correction CI 09/03/2022 limit energy = gap
//if (!SEFromFermiLevel && weaklyBound) limitEnergy += currentMaterialStructure->GetInitialEnergy();
//if (!SEFromFermiLevel && weaklyBound) limitEnergy += currentMaterialStructure->GetEnergyGap();
fParticleChangeForGamma->SetProposedKineticEnergy(ekin - secondaryKinetic-limitEnergy); //Ef = Ei-(Q-El)-El = Ei-Q
fParticleChangeForGamma->ProposeLocalEnergyDeposit(limitEnergy-deexSecEnergy);
if (secondaryKinetic>0)
{
G4DynamicParticle* dp = new G4DynamicParticle(G4Electron::Electron(), deltaDirection, secondaryKinetic); //Esec = Q-El
@@ -791,7 +791,9 @@ G4double G4MicroElecInelasticModel_new::RandomizeEjectedElectronEnergy(
} while(G4UniformRand()*crossSectionMaximum >
DifferentialCrossSection(particleDefinition, k,
(secondaryElectronKineticEnergy+currentMaterialStructure->GetLimitEnergy(shell)),shell));
}
// added 12/01/2023
return secondaryElectronKineticEnergy;
}
else if (particleDefinition == G4Proton::ProtonDefinition())
{
G4double maximumEnergyTransfer =
@@ -839,17 +841,24 @@ G4double G4MicroElecInelasticModel_new::RandomizeEjectedElectronEnergyFromCumula
{
G4double secondaryElectronKineticEnergy = 0.;
G4double random = G4UniformRand();
secondaryElectronKineticEnergy = TransferedEnergy(particleDefinition, k, shell, random)
- currentMaterialStructure->GetLimitEnergy(shell) ;
if (isnan(secondaryElectronKineticEnergy)) { secondaryElectronKineticEnergy = k - currentMaterialStructure->GetLimitEnergy(shell); }
if (secondaryElectronKineticEnergy < 0.) {
secondaryElectronKineticEnergy = k - currentMaterialStructure->GetEnergyGap();
SEFromFermiLevel = true;
}
return secondaryElectronKineticEnergy;
G4bool weaklyBound = currentMaterialStructure->IsShellWeaklyBound(shell);
G4double transf = TransferedEnergy(particleDefinition, k, shell, random);
if (!weaklyBound) {
secondaryElectronKineticEnergy = transf - currentMaterialStructure->GetLimitEnergy(shell); //shell energy for core electrons,
if(secondaryElectronKineticEnergy <= 0.) {
secondaryElectronKineticEnergy = 0.0;
}
}
else {
secondaryElectronKineticEnergy = transf - currentMaterialStructure->GetLimitEnergy(shell);
// for weaklybound electrons = gap + average energy in the energy band
if (secondaryElectronKineticEnergy <= 0.) {
secondaryElectronKineticEnergy = 0.0;
SEFromFermiLevel = true;
}
}
//corrections CI 07/02/2022 - added
return secondaryElectronKineticEnergy;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -901,7 +910,7 @@ G4double G4MicroElecInelasticModel_new::TransferedEnergy(
vector<VecMap>* eProbaShellMap = iterator_Proba->second; //Storage of probabilities for energy transfer
vector<G4double>* eTdummyVec = iterator_Tdummy->second; //Incident energies for interpolation
// k should be in eV
// k should be in eV auto, :std::vector<double>::iterator
auto k2 = std::upper_bound(eTdummyVec->begin(),
eTdummyVec->end(),
k);
@@ -1001,14 +1010,14 @@ G4double G4MicroElecInelasticModel_new::TransferedEnergy(
incidentEnergyMap::iterator iterator_Tdummy;
iterator_Tdummy = pIncidentEnergyStorage.find(currentMaterial);
if (iterator_Nrj == pNrjTransStorage.end() || iterator_Proba == pProbaShellStorage.end() ||
iterator_Tdummy == pIncidentEnergyStorage.end())
{
G4String str = "Material ";
str += currentMaterial + " not found!";
G4Exception("G4MicroElecInelasticModel_new::TransferedEnergy", "em0002",
FatalException, str);
}
if (iterator_Nrj == pNrjTransStorage.end() || iterator_Proba == pProbaShellStorage.end() ||
iterator_Tdummy == pIncidentEnergyStorage.end())
{
G4String str = "Material ";
str += currentMaterial + " not found!";
G4Exception("G4MicroElecInelasticModel_new::TransferedEnergy", "em0002",
FatalException, str);
}
else
{
vector<TriDimensionMap>* pNrjTransfData = iterator_Nrj->second; //Storage of possible transfer energies
@@ -1085,7 +1094,7 @@ G4double G4MicroElecInelasticModel_new::TransferedEnergy(
nrjTransf22);
// zeros are explicitly set
G4double value = Interpolate(valueK1, valueK2, k, 0., interpolatedvalue2);
G4double value = Interpolate(valueK1, valueK2, k, 0., interpolatedvalue2);
return value;
}
}
@@ -1262,9 +1271,11 @@ G4double G4MicroElecInelasticModel_new::Interpolate(G4double e1,
G4double xs2)
{
G4double value = 0.;
if (e == e1 || e1 == e2 ) { return xs1; }
if (e == e2) { return xs2; }
// Log-log interpolation by default
if (e1 != 0 && e2 != 0 && (e2-e1) != 0 && !fasterCode)
if (e1 > 0. && e2 > 0. && xs1 > 0. && xs2 > 0. && !fasterCode)
{
G4double a = std::log(xs2/xs1)/ std::log(e2/e1);
G4double b = std::log(xs2) - a * std::log(e2);
@@ -1273,7 +1284,7 @@ G4double G4MicroElecInelasticModel_new::Interpolate(G4double e1,
}
// Switch to log-lin interpolation for faster code
if ((e2 - e1) != 0 && xs1 != 0 && xs2 != 0 && fasterCode)
else if (xs1 > 0. && xs2 > 0. && fasterCode)
{
G4double d1 = std::log(xs1);
G4double d2 = std::log(xs2);
@@ -1282,7 +1293,7 @@ G4double G4MicroElecInelasticModel_new::Interpolate(G4double e1,
// Switch to lin-lin interpolation for faster code
// in case one of xs1 or xs2 (=cum proba) value is zero
if ((e2 - e1) != 0 && (xs1 == 0 || xs2 == 0) && fasterCode)
else
{
G4double d1 = xs1;
G4double d2 = xs2;
@@ -25,9 +25,9 @@
//
//
// G4MicroElecLOPhononModel.cc,
// 2020/05/20 P. Caron, C. Inguimbert are with ONERA [b]
// Q. Gibaru is with CEA [a], ONERA [b] and CNES [c]
// M. Raine and D. Lambert are with CEA [a]
// 2020/05/20 P. Caron, C. Inguimbert are with ONERA [b]
// Q. Gibaru is with CEA [a], ONERA [b] and CNES [c]
// M. Raine and D. Lambert are with CEA [a]
//
// A part of this work has been funded by the French space agency(CNES[c])
// [a] CEA, DAM, DIF - 91297 ARPAJON, France
@@ -36,45 +36,41 @@
//
// Based on the following publications
//
// - J. Pierron, C. Inguimbert, M. Belhaj, T. Gineste, J. Puech, M. Raine
// Electron emission yield for low energy electrons:
// Monte Carlo simulation and experimental comparison for Al, Ag, and Si
// Journal of Applied Physics 121 (2017) 215107.
// https://doi.org/10.1063/1.4984761
// - J. Pierron, C. Inguimbert, M. Belhaj, T. Gineste, J. Puech, M. Raine
// Electron emission yield for low energy electrons:
// Monte Carlo simulation and experimental comparison for Al, Ag, and Si
// Journal of Applied Physics 121 (2017) 215107.
// https://doi.org/10.1063/1.4984761
//
// - P. Caron,
// Study of Electron-Induced Single-Event Upset in Integrated Memory Devices
// PHD, 16th October 2019
// - P. Caron,
// Study of Electron-Induced Single-Event Upset in Integrated Memory Devices
// PHD, 16th October 2019
//
// - Q.Gibaru, C.Inguimbert, P.Caron, M.Raine, D.Lambert, J.Puech,
// Geant4 physics processes for microdosimetry and secondary electron emission simulation :
// Extension of MicroElec to very low energies and new materials
// NIM B, 2020, in review.
// - Q.Gibaru, C.Inguimbert, P.Caron, M.Raine, D.Lambert, J.Puech,
// Geant4 physics processes for microdosimetry and secondary electron emission simulation :
// Extension of MicroElec to very low energies and new materials
// NIM B, 2020, in review.
//
//
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
////////////////////////////////////////////////////////////////////////
#include "G4MicroElecLOPhononModel.hh"
#include "G4SystemOfUnits.hh"
#include "G4PhysicalConstants.hh"
G4MicroElecLOPhononModel::G4MicroElecLOPhononModel(const G4ParticleDefinition*,
const G4String& nam)
: G4VEmModel(nam)
const G4String& nam)
: G4VEmModel(nam),isInitialised(false)
{
fParticleChangeForGamma = GetParticleChangeForGamma();
G4cout << "Phonon model is constructed " << G4endl
<< "Phonon Energy = " << phononEnergy / eV << " eV "<< G4endl;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4MicroElecLOPhononModel::~G4MicroElecLOPhononModel()
{}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4MicroElecLOPhononModel::Initialise(const G4ParticleDefinition*,
const G4DataVector& /*cuts*/)
{
{
if (isInitialised) { return; }
fParticleChangeForGamma = GetParticleChangeForGamma();
isInitialised = true;
@@ -82,58 +78,102 @@ void G4MicroElecLOPhononModel::Initialise(const G4ParticleDefinition*,
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
G4double G4MicroElecLOPhononModel::CrossSectionPerVolume(const G4Material* material,
const G4ParticleDefinition*,
G4double ekin,
G4double, G4double)
G4double G4MicroElecLOPhononModel::
CrossSectionPerVolume(const G4Material* material,
const G4ParticleDefinition* p,
G4double ekin,
G4double, G4double)
{
if (material->GetName()!="G4_SILICON_DIOXIDE") return 0.0;
const G4double e = CLHEP::eplus / CLHEP::coulomb;
const G4double m0 = CLHEP::electron_mass_c2 / (CLHEP::c_squared*CLHEP::kg);
const G4double h = CLHEP::hbar_Planck * CLHEP::s/ (CLHEP::m2*CLHEP::kg);
const G4double eps0 = CLHEP::epsilon0 * CLHEP::m/ (CLHEP::farad);
const G4double kb = CLHEP::k_Boltzmann * CLHEP::kelvin/ CLHEP::joule;
const G4double T = 300;
G4double eps = 9;
G4double einf = 3;
// Parameters SiO2
phononEnergy = (0.75*0.153+0.25*0.063 )* CLHEP::eV;
const G4double eps = 3.84;
const G4double einf = 2.25;
const G4double T = 300; // should be taken from material property
G4double E =(ekin/CLHEP::eV)*e;
G4double hw = (phononEnergy / CLHEP::eV) * e;
const G4DataVector cuts;
Initialise(p, cuts);
if (material->GetName() != "G4_SILICON_DIOXIDE"
&& material->GetName() != "G4_ALUMINUM_OXIDE"
&& material->GetName() != "G4_BORON_NITRIDE")
{
return 1 / DBL_MAX;
}
G4double E =(ekin/eV)*e;
if (material->GetName() == "G4_ALUMINUM_OXIDE")
{
eps = 9;
einf = 3;
phononEnergy = 0.1*eV;
}
if (material->GetName() == "G4_SILICON_DIOXIDE")
{
eps = 3.84;
einf = 2.25;
phononEnergy = (0.75*0.153+0.25*0.063 )* eV;
}
// Nuclear Instruments and Methods in Physics Research Section B:
// Beam Interactions with Materials and Atoms
// Volume 454, 1 September 2019, Pages 14 - 22
// Nuclear Instruments and Methods in Physics Research Section B:
// Beam Interactions with Materials and Atoms
// Monte Carlo modeling of low - energy electron - induced secondary
// electron emission yields in micro - architected boron nitride surfaces
if (material->GetName() == "G4_BORON_NITRIDE")
{
eps = 7.1;
einf = 4.5;
phononEnergy = 0.17 * eV;
}
G4double hw = (phononEnergy / eV) * e;
G4double n = 1.0 / (std::exp(hw / (kb*T)) - 1); //Phonon distribution
G4double signe = (absor) ? -1. : 1.;
G4double racine = std::sqrt(1. + ((-signe*hw) / E));
G4double P = (std::pow(e, 2) / (4 * pi*eps0*h*h)) * (n + 0.5 + signe*0.5) * ((1 / einf) - (1 / eps))
* std::sqrt(m0 / (2 * E)) *hw* std::log((1 + racine) / (signe * 1 + ((-signe)*racine)));
G4double MFP = (std::sqrt(2. * E / m0) / P)*m;
return 2. / MFP;
if (absor) // Absorption
{
Eprim = E + hw;
signe = -1;
}
else // Emission
{
Eprim = E - hw;
signe = +1;
}
G4double racine = std::sqrt(1 + ((-signe*hw) / E));
G4double P = (std::pow(e, 2) / (4 * pi*eps0*h*h)) * (n + 0.5 + signe*0.5) * ((1 / einf) - (1 / eps)) * std::sqrt(m0 / (2 * E)) *hw* std::log((1 + racine) / (signe * 1 + ((-signe)*racine)));
G4double MFP = (std::sqrt(2 * E / m0) / P)*m;
if (material->GetName() == "G4_SILICON_DIOXIDE") { return 2 / MFP; }
return 1/(MFP);
// correction CI 12/1/2023 add
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
void G4MicroElecLOPhononModel::SampleSecondaries(
std::vector<G4DynamicParticle*>*,
const G4MaterialCutsCouple*,
const G4DynamicParticle* aDynamicElectron,
G4double, G4double)
void G4MicroElecLOPhononModel::
SampleSecondaries(std::vector<G4DynamicParticle*>*,
const G4MaterialCutsCouple*,
const G4DynamicParticle* aDynamicElectron,
G4double, G4double)
{
G4double E = aDynamicElectron->GetKineticEnergy();
G4double Eprim = (absor) ? E + phononEnergy : E - phononEnergy;
Eprim = (absor) ? E + phononEnergy : E - phononEnergy;
G4double rand = G4UniformRand();
G4double B = (E + Eprim + 2 * std::sqrt(E*Eprim)) / (E + Eprim - 2 * std::sqrt(E*Eprim));
G4double cosTheta = ((E + Eprim) / (2 * std::sqrt(E*Eprim)))*(1 - std::pow(B, rand)) + std::pow(B, rand);
if(Interband){
G4double B = (E + Eprim + 2 * std::sqrt(E*Eprim))
/ (E + Eprim - 2 * std::sqrt(E*Eprim));
G4double cosTheta = ((E + Eprim) / (2 * std::sqrt(E*Eprim)))
* (1 - std::pow(B, rand)) + std::pow(B, rand);
if(Interband)
{
cosTheta = 1 - 2 * G4UniformRand(); //Isotrope
}
G4double phi = twopi * G4UniformRand();
@@ -25,9 +25,9 @@
//
//
// G4MicroElecSurface.cc,
// 2020/05/20 P. Caron, C. Inguimbert are with ONERA [b]
// Q. Gibaru is with CEA [a], ONERA [b] and CNES [c]
// M. Raine and D. Lambert are with CEA [a]
// 2020/05/20 P. Caron, C. Inguimbert are with ONERA [b]
// Q. Gibaru is with CEA [a], ONERA [b] and CNES [c]
// M. Raine and D. Lambert are with CEA [a]
//
// A part of this work has been funded by the French space agency(CNES[c])
// [a] CEA, DAM, DIF - 91297 ARPAJON, France
@@ -36,28 +36,24 @@
//
// Based on the following publications
//
// - Q.Gibaru, C.Inguimbert, P.Caron, M.Raine, D.Lambert, J.Puech,
// Geant4 physics processes for microdosimetry and secondary electron emission simulation :
// Extension of MicroElec to very low energies and new materials
// NIM B, 2020, in review.
// - Q.Gibaru, C.Inguimbert, P.Caron, M.Raine, D.Lambert, J.Puech,
// Geant4 physics processes for microdosimetry and secondary electron emission simulation:
// Extension of MicroElec to very low energies and new materials
// NIM B, 2020, in review.
//
//
// - Modèle de transport d'électrons à basse énergie (10 eV- 2 keV) pour
// applications spatiales (OSMOSEE, GEANT4), PhD dissertation, 2017.
//
// - Modele de transport d'electrons a basse energie (10 eV- 2 keV) pour
// applications spatiales (OSMOSEE, GEANT4), PhD dissertation, 2017.
//
////////////////////////////////////////////////////////////////////////
#include "G4MicroElecSurface.hh"
#include "G4ios.hh"
#include "G4PhysicalConstants.hh"
#include "G4EmProcessSubType.hh"
#include "G4GeometryTolerance.hh"
#include "G4EmProcessSubType.hh"
#include "G4SystemOfUnits.hh"
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4MicroElecSurface::G4MicroElecSurface(const G4String& processName,G4ProcessType type)
@@ -66,9 +62,9 @@ G4MicroElecSurface::G4MicroElecSurface(const G4String& processName,G4ProcessType
theGlobalNormal(0.,0.,0.), theFacetNormal(0.,0.,0.)
{
if ( verboseLevel > 0)
{
G4cout << GetProcessName() << " is created " << G4endl;
}
{
G4cout << GetProcessName() << " is created " << G4endl;
}
isInitialised=false;
SetProcessSubType(fSurfaceReflection);
@@ -103,6 +99,36 @@ G4MicroElecSurface::IsApplicable(const G4ParticleDefinition& aParticleType)
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4MicroElecSurface::Initialise()
{
if (isInitialised) { return; }
G4ProductionCutsTable* theCoupleTable =
G4ProductionCutsTable::GetProductionCutsTable();
G4int numOfCouples = (G4int)theCoupleTable->GetTableSize();
G4cout << numOfCouples << G4endl;
for (G4int i = 0; i < numOfCouples; ++i)
{
const G4Material* material =
theCoupleTable->GetMaterialCutsCouple(i)->GetMaterial();
G4cout << this->GetProcessName() << ", Material " << i + 1
<< " / " << numOfCouples << " : " << material->GetName() << G4endl;
if (material->GetName() == "Vacuum")
{
tableWF[material->GetName()] = 0; continue;
}
G4String mat = material->GetName();
G4MicroElecMaterialStructure str = G4MicroElecMaterialStructure(mat);
tableWF[mat] = str.GetWorkFunction();
}
isInitialised = true;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4MicroElecSurface::BuildPhysicsTable(const G4ParticleDefinition&)
{
if (isInitialised) { return; }
@@ -113,12 +139,18 @@ void G4MicroElecSurface::BuildPhysicsTable(const G4ParticleDefinition&)
G4cout << "G4MicroElecSurface::Initialise: Ncouples= "
<< numOfCouples << G4endl;
for (G4int i = 0; i < numOfCouples; ++i) {
for (G4int i = 0; i < numOfCouples; ++i)
{
const G4Material* material =
theCoupleTable->GetMaterialCutsCouple(i)->GetMaterial();
G4cout << "G4Surface, Material " << i + 1 << " / " << numOfCouples << " : " << material->GetName() << G4endl;
if (material->GetName() == "Vacuum") { tableWF[material->GetName()] = 0; continue; }
G4cout << "G4Surface, Material " << i + 1 << " / "
<< numOfCouples << " : " << material->GetName() << G4endl;
if (material->GetName() == "Vacuum")
{
tableWF[material->GetName()] = 0;
continue;
}
G4String mat = material->GetName();
G4MicroElecMaterialStructure str = G4MicroElecMaterialStructure(mat);
tableWF[mat] = str.GetWorkFunction();
@@ -130,263 +162,287 @@ void G4MicroElecSurface::BuildPhysicsTable(const G4ParticleDefinition&)
G4VParticleChange* G4MicroElecSurface::PostStepDoIt(const G4Track& aTrack, const G4Step& aStep)
{
if (!isInitialised) { Initialise(); }
theStatus = UndefinedSurf;
//Definition of the parameters for the particle
// Definition of the parameters for the particle
aParticleChange.Initialize(aTrack);
aParticleChange.ProposeVelocity(aTrack.GetVelocity());
G4StepPoint* pPreStepPoint = aStep.GetPreStepPoint();
G4StepPoint* pPostStepPoint = aStep.GetPostStepPoint();
material1 = pPreStepPoint -> GetMaterial();
material2 = pPostStepPoint -> GetMaterial();
theStatus = UndefinedSurf;
const G4DynamicParticle* aParticle = aTrack.GetDynamicParticle();
theParticleMomentum = aParticle->GetTotalMomentum();
previousMomentum = oldMomentum;
oldMomentum = aParticle->GetMomentumDirection();
//First case: not a boundary
if (pPostStepPoint->GetStepStatus() != fGeomBoundary ||
pPostStepPoint->GetPhysicalVolume() == pPreStepPoint->GetPhysicalVolume())
{
theStatus = NotAtBoundarySurf;
flag_franchissement_surface = false;
// Fisrt case: not a boundary
if (pPostStepPoint->GetStepStatus() != fGeomBoundary
|| pPostStepPoint->GetPhysicalVolume()->GetName() == pPreStepPoint->GetPhysicalVolume()->GetName())
{
theStatus = NotAtBoundarySurf;
flag_franchissement_surface = false;
flag_reflexion = false;
return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
theStatus = UndefinedSurf;
// Third case: same material
if (material1 == material2)
{
theStatus = SameMaterialSurf;
return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
if (verboseLevel > 3)
{
G4cout << G4endl << " Electron at Boundary! " << G4endl;
G4VPhysicalVolume* thePrePV = pPreStepPoint->GetPhysicalVolume();
G4VPhysicalVolume* thePostPV = pPostStepPoint->GetPhysicalVolume();
if (thePrePV) G4cout << " thePrePV: " << thePrePV->GetName() << G4endl;
if (thePostPV) G4cout << " thePostPV: " << thePostPV->GetName() << G4endl;
G4cout << " Old Momentum Direction: " << oldMomentum << G4endl;
}
// Definition of the parameters for the surface
G4ThreeVector theGlobalPoint = pPostStepPoint->GetPosition();
G4Navigator* theNavigator = G4TransportationManager::
GetTransportationManager()->GetNavigatorForTracking();
G4bool valid;
theGlobalNormal = theNavigator->GetGlobalExitNormal(theGlobalPoint, &valid);
if (valid)
{
theGlobalNormal = -theGlobalNormal;
}
else
{
G4ExceptionDescription ed;
ed << " G4MicroElecSurface/PostStepDoIt(): "
<< " The Navigator reports that it returned an invalid normal"
<< G4endl;
G4Exception("G4MuElecSurf::PostStepDoIt", "OpBoun01",
EventMustBeAborted, ed,
"Invalid Surface Normal - Geometry must return valid surface normal");
}
// Exception: the particle is not in the right direction
if (oldMomentum * theGlobalNormal > 0.0)
{
theGlobalNormal = -theGlobalNormal;
}
if (aTrack.GetStepLength()<=kCarTolerance/2 * 0.0000000001)
{
if (flag_reflexion == true)
{
flag_reflexion = false;
return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
theStatus = UndefinedSurf;
//Third case: same material
if (material1 == material2)
{
theStatus = SameMaterialSurf;
return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
if (verboseLevel > 0)
{
G4cout << G4endl << " Electron at Boundary! " << G4endl;
G4VPhysicalVolume* thePrePV = pPreStepPoint->GetPhysicalVolume();
G4VPhysicalVolume* thePostPV = pPostStepPoint->GetPhysicalVolume();
if (thePrePV) G4cout << " thePrePV: " << thePrePV->GetName() << G4endl;
if (thePostPV) G4cout << " thePostPV: " << thePostPV->GetName() << G4endl;
G4cout << " Old Momentum Direction: " << oldMomentum << G4endl;
}
//Definition of the parameters for the surface
G4ThreeVector theGlobalPoint = pPostStepPoint->GetPosition();
G4Navigator* theNavigator =
G4TransportationManager::GetTransportationManager()->
GetNavigatorForTracking();
G4bool valid;
theGlobalNormal = theNavigator->GetGlobalExitNormal(theGlobalPoint, &valid);
// G4cout << "Global exit normal = " << theGlobalNormal << " valid = " << valid << G4endl;
if (valid)
{
theGlobalNormal = -theGlobalNormal;
}
else
{
G4ExceptionDescription ed;
ed << " G4MicroElecSurface/PostStepDoIt(): "
<< " The Navigator reports that it returned an invalid normal.\n"
<< "PV: " << pPreStepPoint->GetPhysicalVolume()->GetName()
<< " TrackID= " << aTrack.GetTrackID()
<< " Ekin(MeV)= " << aTrack.GetKineticEnergy()
<< " position: " << theGlobalPoint
<< " direction: " << oldMomentum
<< G4endl;
G4Exception("G4MuElecSurf::PostStepDoIt", "OpBoun01",
FatalException, ed,
"Invalid Surface Normal - Geometry must return valid surface normal");
return 0;
}
//Exception: the particle is not in the right direction
if (oldMomentum * theGlobalNormal > 0.0)
{
theGlobalNormal = -theGlobalNormal;
}
//Second case: step too small
//Corrections bug rotation + réflexion
if (aTrack.GetStepLength()<=kCarTolerance)
{
theStatus = StepTooSmallSurf;
WorkFunctionTable::iterator postStepWF;
postStepWF = tableWF.find(pPostStepPoint->GetMaterial()->GetName());
WorkFunctionTable::iterator preStepWF;
preStepWF = tableWF.find(pPreStepPoint->GetMaterial()->GetName());
if (postStepWF == tableWF.end()) {
G4String str = "Material ";
str += pPostStepPoint->GetMaterial()->GetName() + " not found!";
G4Exception("G4Surface::G4Surface", "em0002", FatalException, str);
return 0;
}
else if (preStepWF == tableWF.end()) {
G4String str = "Material ";
str += pPreStepPoint->GetMaterial()->GetName() + " not found!";
G4Exception("G4Surface::G4Surface", "em0002", FatalException, str);
return 0;
}
if (pPreStepPoint->GetMaterial() != pPostStepPoint->GetMaterial()) {
flag_franchissement_surface = false;
if (flag_reflexion == true && flag_normal == true) {
aParticleChange.ProposeMomentumDirection(-Reflexion(aStep.GetPostStepPoint()));
flag_reflexion = false;
flag_normal = false;
}
}
return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
flag_normal = (theGlobalNormal.x() == 0.0 && theGlobalNormal.y() == 0.0);
theStatus = StepTooSmallSurf;
G4double energyThreshold_surface = 0.0*eV;
WorkFunctionTable::iterator postStepWF;
postStepWF = tableWF.find(pPostStepPoint->GetMaterial()->GetName());
WorkFunctionTable::iterator preStepWF;
preStepWF = tableWF.find(pPreStepPoint->GetMaterial()->GetName());
if (postStepWF == tableWF.end())
{
G4String str = "Material ";
str += pPostStepPoint->GetMaterial()->GetName() + " not found!";
G4Exception("G4Surface::G4Surface", "em0002", FatalException, str);
return nullptr;
}
else if (preStepWF == tableWF.end())
{
G4String str = "Material ";
str += pPreStepPoint->GetMaterial()->GetName() + " not found!";
G4Exception("G4Surface::G4Surface", "em0002", FatalException, str);
return nullptr;
}
else
{
G4double thresholdNew_surface = postStepWF->second;
G4double thresholdOld_surface = preStepWF->second;
energyThreshold_surface = thresholdNew_surface - thresholdOld_surface;
}
if (flag_franchissement_surface == true)
{
aParticleChange.ProposeEnergy(aStep.GetPostStepPoint()->GetKineticEnergy() + energyThreshold_surface);
flag_franchissement_surface = false;
}
if (flag_reflexion == true && flag_normal == true)
{
aParticleChange.ProposeMomentumDirection(-Reflexion(aStep.GetPostStepPoint()));
flag_reflexion = false;
flag_normal = false;
}
return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
flag_normal = (theGlobalNormal == G4ThreeVector(0, 0, 1)
|| theGlobalNormal == G4ThreeVector(0, 0, -1));
G4LogicalSurface* Surface = nullptr;
Surface = G4LogicalBorderSurface::GetSurface
(pPreStepPoint ->GetPhysicalVolume(),
pPostStepPoint->GetPhysicalVolume());
(pPreStepPoint ->GetPhysicalVolume(),
pPostStepPoint->GetPhysicalVolume());
if (Surface == nullptr)
{
G4bool enteredDaughter=(pPostStepPoint->GetPhysicalVolume()->GetMotherLogical()
== pPreStepPoint->GetPhysicalVolume()->GetLogicalVolume());
if(enteredDaughter)
{
G4bool enteredDaughter=(pPostStepPoint->GetPhysicalVolume()
->GetMotherLogical() ==
pPreStepPoint->GetPhysicalVolume()
->GetLogicalVolume());
if(enteredDaughter)
{
Surface = G4LogicalSkinSurface::GetSurface
(pPostStepPoint->GetPhysicalVolume()->
GetLogicalVolume());
if(Surface == nullptr)
Surface = G4LogicalSkinSurface::GetSurface
(pPreStepPoint->GetPhysicalVolume()->
GetLogicalVolume());
}
else
{
Surface = G4LogicalSkinSurface::GetSurface
(pPreStepPoint->GetPhysicalVolume()->
GetLogicalVolume());
if(Surface == nullptr)
Surface = G4LogicalSkinSurface::GetSurface
(pPostStepPoint->GetPhysicalVolume()->
GetLogicalVolume());
}
Surface = G4LogicalSkinSurface::GetSurface
(pPostStepPoint->GetPhysicalVolume()->GetLogicalVolume());
if(Surface == nullptr)
Surface = G4LogicalSkinSurface::GetSurface
(pPreStepPoint->GetPhysicalVolume()->GetLogicalVolume());
}
else
{
Surface = G4LogicalSkinSurface::GetSurface
(pPreStepPoint->GetPhysicalVolume()->GetLogicalVolume());
if(Surface == nullptr)
Surface = G4LogicalSkinSurface::GetSurface
(pPostStepPoint->GetPhysicalVolume()->GetLogicalVolume());
}
}
// Definition of the parameters for the surface crossing
G4VPhysicalVolume* thePrePV = pPreStepPoint->GetPhysicalVolume();
G4VPhysicalVolume* thePostPV = pPostStepPoint->GetPhysicalVolume();
if (thePostPV)
energyThreshold = 0.0*eV;
G4double energyDelta = 0;
if ((thePrePV)&&(thePostPV))
{
WorkFunctionTable::iterator postStepWF;
postStepWF = tableWF.find(thePostPV->GetLogicalVolume()->GetMaterial()->GetName());
WorkFunctionTable::iterator preStepWF;
preStepWF = tableWF.find(thePrePV->GetLogicalVolume()->GetMaterial()->GetName());
if (postStepWF == tableWF.end())
{
WorkFunctionTable::iterator postStepWF;
postStepWF = tableWF.find(thePostPV->GetLogicalVolume()->GetMaterial()->GetName());
WorkFunctionTable::iterator preStepWF;
preStepWF = tableWF.find(thePrePV->GetLogicalVolume()->GetMaterial()->GetName());
if (postStepWF == tableWF.end()) {
G4String str = "Material ";
str += thePostPV->GetLogicalVolume()->GetMaterial()->GetName() + " not found!";
G4Exception("G4Surface::G4Surface", "em0002", FatalException, str);
return 0;
}
else if (preStepWF == tableWF.end()) {
G4String str = "Material ";
str += thePrePV->GetLogicalVolume()->GetMaterial()->GetName() + " not found!";
G4Exception("G4Surface::G4Surface", "em0002", FatalException, str);
return 0;
}
else
{
G4double thresholdNew = postStepWF->second;
G4double thresholdOld = preStepWF->second;
energyThreshold = thresholdNew - thresholdOld;
}
G4String str = "Material ";
str += thePostPV->GetLogicalVolume()->GetMaterial()->GetName() + " not found!";
G4Exception("G4Surface::G4Surface", "em0002", FatalException, str);
return nullptr;
}
ekint = pPreStepPoint->GetKineticEnergy();
else if (preStepWF == tableWF.end())
{
G4String str = "Material ";
str += thePrePV->GetLogicalVolume()->GetMaterial()->GetName() + " not found!";
G4Exception("G4Surface::G4Surface", "em0002", FatalException, str);
return nullptr;
}
else
{
G4double thresholdNew = postStepWF->second;
G4double thresholdOld = preStepWF->second;
energyThreshold = thresholdNew - thresholdOld;
energyDelta = thresholdOld- thresholdNew;
}
}
ekint=aStep.GetPostStepPoint()->GetKineticEnergy();
thetat= GetIncidentAngle(); //angle d'incidence
G4double ekinNormalt=ekint*std::cos(thetat)*std::cos(thetat);
G4double atet = std::sqrt(ekint/(ekint+energyThreshold))*std::sin(thetat);
thetaft = (atet > 1.0) ? pi*0.5 : std::asin(atet);//Angle de réfraction
thetaft=std::asin(std::sqrt(ekint/(ekint+energyThreshold))*std::sin(thetat));//Angle de refraction
if(std::sqrt(ekint/(ekint+energyThreshold))*std::sin(thetat)>1.0)
{
thetaft=std::asin(1.0);
}
G4double aleat=G4UniformRand();
const G4double waveVectort=std::sqrt(2*9.1093826E-31*1.602176487E-19)/(6.6260755E-34/(2.0*pi));
//Parameter for an exponential barrier of potential (Thèse P68)
const G4double at=0.5E-10;
//G4double modif already declared in .hh
G4double waveVectort=std::sqrt(2*9.1093826E-31*1.602176487E-19)/(6.6260755E-34/(2.0*pi));
// Parameter for an exponential barrier of potential (Thesis P68)
G4double at=0.5E-10;
crossingProbability=0;
G4double kft=waveVectort*std::sqrt(ekint+energyThreshold)*std::cos(thetaft);
G4double kit=waveVectort*std::sqrt(ekinNormalt);
G4double yy = std::sinh(pi*at*(kit-kft))/std::sinh(pi*at*(kit+kft));
crossingProbability = 1 - yy*yy;
//First case: the electron crosses the surface
if((aleat<=crossingProbability)&&(ekint>std::abs(energyThreshold)))
{
if (pPreStepPoint->GetMaterial() != pPostStepPoint->GetMaterial()) {
flag_franchissement_surface = true;
}
thetaft=std::abs(thetaft-thetat);
G4ThreeVector zVerst = aStep.GetPostStepPoint()->GetMomentumDirection();
G4ThreeVector xVerst = zVerst.orthogonal();
G4ThreeVector yVerst = zVerst.cross(xVerst);
G4double cost = std::cos(thetaft);
G4double xDirt = std::sqrt(1. - cost*cost);
G4double yDirt = xDirt;
G4ThreeVector zPrimeVerst = xDirt*xVerst + yDirt*yVerst + cost*zVerst;
aParticleChange.ProposeMomentumDirection(zPrimeVerst.unit());
}
else if ((aleat > crossingProbability) && (ekint>std::abs(energyThreshold)))
crossingProbability=1-(std::pow(std::sinh(pi*at*(kit-kft)), 2.0)/std::pow(std::sinh(pi*at*(kit+kft)), 2.0));
// First case: the electron crosses the surface
if((aleat<=crossingProbability)&&(ekint> energyDelta))
{
if (aStep.GetPreStepPoint()->GetMaterial()->GetName()
!= aStep.GetPostStepPoint()->GetMaterial()->GetName())
{
flag_reflexion = true;
if (flag_normal) { aParticleChange.ProposeMomentumDirection(-oldMomentum.unit()); }
else { aParticleChange.ProposeMomentumDirection(Reflexion(aStep.GetPostStepPoint())); }
aParticleChange.ProposeEnergy(ekint - energyDelta);
flag_franchissement_surface = true;
}
thetaft=std::abs(thetaft-thetat);
G4ThreeVector zVerst = aStep.GetPostStepPoint()->GetMomentumDirection();
G4ThreeVector xVerst = zVerst.orthogonal();
G4ThreeVector yVerst = zVerst.cross(xVerst);
G4double xDirt = std::sqrt(1. - std::cos(thetaft)*std::cos(thetaft));
G4double yDirt = xDirt;
G4ThreeVector zPrimeVerst=((xDirt*xVerst + yDirt*yVerst + std::cos(thetaft)*zVerst));
aParticleChange.ProposeMomentumDirection(zPrimeVerst.unit());
}
else if ((aleat > crossingProbability) && (ekint> energyDelta))
{
flag_reflexion = true;
if (flag_normal)
{
aParticleChange.ProposeMomentumDirection(-oldMomentum.unit());
}
else
{
aParticleChange.ProposeMomentumDirection(Reflexion(aStep.GetPostStepPoint()));
}
}
else
{
if (flag_normal)
{
aParticleChange.ProposeMomentumDirection(-oldMomentum.unit());
}
else
{
aParticleChange.ProposeMomentumDirection(Reflexion(aStep.GetPostStepPoint()));
}
else {
if (flag_normal) { aParticleChange.ProposeMomentumDirection(-oldMomentum.unit()); }
else { aParticleChange.ProposeMomentumDirection(Reflexion(aStep.GetPostStepPoint())); }
flag_reflexion = true;
}
return G4VDiscreteProcess::PostStepDoIt(aTrack, aStep);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4double G4MicroElecSurface::GetMeanFreePath(const G4Track&, G4double,
G4ForceCondition* condition)
G4ForceCondition* condition)
{
*condition = Forced;
return DBL_MAX;
@@ -411,17 +467,17 @@ G4double G4MicroElecSurface::GetIncidentAngle()
G4ThreeVector G4MicroElecSurface::Reflexion(const G4StepPoint* PostStepPoint)
{
//Normale
// Normale
G4double Nx = theGlobalNormal.x();
G4double Ny = theGlobalNormal.y();
G4double Nz = theGlobalNormal.z();
//PostStepPoint
// PostStepPoint
G4double PSx = PostStepPoint->GetPosition().x();
G4double PSy = PostStepPoint->GetPosition().y();
G4double PSz = PostStepPoint->GetPosition().z();
//P(alpha,beta,gamma) - PostStep avec translation momentum
// P(alpha,beta,gamma) - PostStep avec translation momentum
G4double alpha = PSx + oldMomentum.x();
G4double beta = PSy + oldMomentum.y();
G4double gamma = PSz + oldMomentum.z();
@@ -429,23 +485,24 @@ G4ThreeVector G4MicroElecSurface::Reflexion(const G4StepPoint* PostStepPoint)
G4double x, y, z, d, A, B, PM2x, PM2y, PM2z;
d = -(Nx*PSx + Ny*PSy + Nz*PSz);
if (Ny == 0 && Nx == 0) {
if (Ny == 0 && Nx == 0)
{
gamma = -gamma;
}
else {
if (Ny == 0) {
else
{
if (Ny == 0)
{
A = (Nz*Nz*alpha) + (Nx*Nx*PSx) + (Nx*Nz*(PSz - gamma));
B = r*r;
//M(x,y,z) - Projection de P sur la surface
// M(x,y,z) - Projection de P sur la surface
x = A / B;
y = beta;
z = (x - alpha)*(Nz / Nx) + gamma;
}
else {
else
{
A = (r*r) / Ny;
B = (beta / Ny)*(Nx*Nx + Nz*Nz) - (Nx*alpha + Nz*gamma + d);
@@ -455,10 +512,10 @@ G4ThreeVector G4MicroElecSurface::Reflexion(const G4StepPoint* PostStepPoint)
z = (y - beta)*(Nz / Ny) + gamma;
}
//Vecteur 2*PM
// Vecteur 2*PM
PM2x = 2 * (x - alpha); PM2y = 2 * (y - beta); PM2z = 2 * (z - gamma);
//Nouveau point P
// Nouveau point P
alpha += PM2x; beta += PM2y; gamma += PM2z;
}
@@ -38,6 +38,7 @@
// 24 May 2011 L Pandola Renamed (make v2008 as default Penelope)
// 10 Jun 2011 L Pandola Migrate atomic deexcitation interface
// 09 Oct 2013 L Pandola Migration to MT
// 25 Jul 2023 D Iuso Fix for possible infinite loops due to FP
//
#include "G4PenelopeComptonModel.hh"
#include "G4PhysicalConstants.hh"
@@ -320,6 +321,13 @@ void G4PenelopeComptonModel::SampleSecondaries(std::vector<G4DynamicParticle*>*
G4double ek1 = eks-ek2-1.0;
G4double taumin = 1.0/ek2;
//This is meant to fix a possible (rare) floating point exception in the sampling of tau below,
//causing an infinite loop. The maximum of tau is not 1., but the closest double which can
//be represented (i.e. ~ 1. - 1e-16). Fix by Domenico Iuso
static G4double taumax = std::nexttoward(1.0,0.0);
if (fVerboseLevel > 3)
G4cout << "G4PenelopeComptonModel: maximum value of tau: 1 - " << 1.-taumax << G4endl;
//To here.
G4double a1 = G4Log(ek2);
G4double a2 = a1+2.0*ek*(1.0+ek)/(ek2*ek2);
@@ -339,6 +347,7 @@ void G4PenelopeComptonModel::SampleSecondaries(std::vector<G4DynamicParticle*>*
//rejection function
TST = (1.0+tau*(ek1+tau*(ek2+tau*eks)))/(eks*tau*(1.0+tau*tau));
}while (G4UniformRand()> TST);
if (tau > taumax) tau = taumax; //prevent FP exception causing infinite loop
epsilon=tau;
cosTheta = 1.0 - (1.0-tau)/(ek*tau);
@@ -394,6 +403,7 @@ void G4PenelopeComptonModel::SampleSecondaries(std::vector<G4DynamicParticle*>*
tau = std::pow(taumin,G4UniformRand());
else
tau = std::sqrt(1.0+G4UniformRand()*(taumin*taumin-1.0));
if (tau > taumax) tau = taumax; //prevent FP exception causing infinite loop
cdt1 = (1.0-tau)/(ek*tau);
//Incoherent scattering function
S = 0.;