Import Geant4 11.3.0.beta source tree

This commit is contained in:
Gabriele Cosmo
2024-06-28 13:08:51 +02:00
parent f7b23877ed
commit e58e650b32
5232 changed files with 239416 additions and 244360 deletions
@@ -0,0 +1,143 @@
//
// ********************************************************************
// * 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. *
// ********************************************************************
//
//
// GEANT4 Class file
//
//
// File name: G4AllisonPositronAtRestModel
//
// Author: Vladimir Ivanchenko
//
// Creation date: 14 May 2024
//
// -------------------------------------------------------------------
//
#include "G4AllisonPositronAtRestModel.hh"
#include "G4DynamicParticle.hh"
#include "G4Material.hh"
#include "Randomize.hh"
#include "G4Gamma.hh"
#include "G4RandomDirection.hh"
#include "G4ThreeVector.hh"
#include "G4PhysicalConstants.hh"
#include "G4SystemOfUnits.hh"
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4AllisonPositronAtRestModel::G4AllisonPositronAtRestModel()
: G4VPositronAtRestModel("Allison")
{}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4AllisonPositronAtRestModel::SampleSecondaries(
std::vector<G4DynamicParticle*>& secParticles,
G4double&, const G4Material* material) const
{
const G4double eGamma = CLHEP::electron_mass_c2;
// In rest frame of positronium gammas are back to back
const G4ThreeVector& dir1 = G4RandomDirection();
const G4ThreeVector& dir2 = -dir1;
auto aGamma1 = new G4DynamicParticle(G4Gamma::Gamma(),dir1,eGamma);
auto aGamma2 = new G4DynamicParticle(G4Gamma::Gamma(),dir2,eGamma);
// In rest frame the gammas are polarised perpendicular to each other - see
// Pryce and Ward, Nature No 4065 (1947) p.435.
// Snyder et al, Physical Review 73 (1948) p.440.
G4ThreeVector pol1 = (G4RandomDirection().cross(dir1)).unit();
G4ThreeVector pol2 = (pol1.cross(dir2)).unit();
// A positron in matter slows down and combines with an atomic electron to
// make a neutral atom called positronium, about half the size of a normal
// atom. I expect that when the energy of the positron is small enough,
// less than the binding energy of positronium (6.8 eV), it is
// energetically favourable for an electron from the outer orbitals of a
// nearby atom or molecule to transfer and bind to the positron, as in an
// ionic bond, leaving behind a mildly ionised nearby atom/molecule. I
// would expect the positronium to come away with a kinetic energy of a
// few eV on average. In its para (spin 0) state it annihilates into two
// photons, which in the rest frame of the positronium are collinear
// (back-to-back) due to momentum conservation. Because of the motion of the
// positronium, photons will be not quite back-to-back in the laboratory.
// The positroniuim acquires an energy of order its binding energy and
// doesn't have time to thermalise. Nevertheless, here we approximate its
// energy distribution by a Maxwell-Boltzman with mean energy <KE>. In terms
// of a more familiar concept of temperature, and the law of equipartition
// of energy of translational motion, <KE>=3kT/2. Each component of velocity
// has a distribution exp(-mv^2/2kT), which is a Gaussian of mean zero
// and variance kT/m=2<KE>/3m, where m is the positronium mass.
const G4double meanEnergyPerIonPair = material->GetIonisation()->GetMeanEnergyPerIonPair();
const G4double& meanKE = meanEnergyPerIonPair; // Just an alias
if (meanKE > 0.) { // Positronium has motion
// Mass of positronium
const G4double mass = 2.*CLHEP::electron_mass_c2;
// Mean <KE>=3kT/2, as described above
// const G4double T = 2.*meanKE/(3.*k_Boltzmann);
// Component velocities: Gaussian, variance kT/m=2<KE>/3m.
const G4double sigmav = std::sqrt(2.*meanKE/(3.*mass));
// This is in units where c=1
const G4double vx = G4RandGauss::shoot(0.,sigmav);
const G4double vy = G4RandGauss::shoot(0.,sigmav);
const G4double vz = G4RandGauss::shoot(0.,sigmav);
const G4ThreeVector v(vx,vy,vz); // In unit where c=1
const G4ThreeVector& beta = v; // so beta=v/c=v
aGamma1->Set4Momentum(aGamma1->Get4Momentum().boost(beta));
aGamma2->Set4Momentum(aGamma2->Get4Momentum().boost(beta));
// Rotate polarisation vectors
const G4ThreeVector& newDir1 = aGamma1->GetMomentumDirection();
const G4ThreeVector& newDir2 = aGamma2->GetMomentumDirection();
const G4ThreeVector& axis1 = dir1.cross(newDir1); // No need to be unit
const G4ThreeVector& axis2 = dir2.cross(newDir2); // No need to be unit
const G4double& angle1 = std::acos(dir1*newDir1);
const G4double& angle2 = std::acos(dir2*newDir2);
pol1.rotate(axis1, angle1);
pol2.rotate(axis2, angle2);
}
// use constructors optimal for massless particle
aGamma1->SetPolarization(pol1);
aGamma2->SetPolarization(pol2);
secParticles.push_back(aGamma1);
secParticles.push_back(aGamma2);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4AllisonPositronAtRestModel::PrintGeneratorInformation() const
{
G4cout << "\n" << G4endl;
G4cout << "Allison AtRest positron 2-gamma annihilation model." << G4endl;
G4cout << "Takes into account positronium motion in the media." << G4endl;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -0,0 +1,153 @@
//
// ********************************************************************
// * 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. *
// ********************************************************************
//
//
// GEANT4 Class file
//
//
// File name: G4OrePowellAtRestModel
//
// Author: I.Semeniouk & D.Bernard
//
// Creation date: 04 Juin 2024
//
// -------------------------------------------------------------------
//
#include "G4OrePowellAtRestModel.hh"
#include "G4DynamicParticle.hh"
#include "G4Material.hh"
#include "Randomize.hh"
#include "G4Gamma.hh"
#include "G4RandomDirection.hh"
#include "G4ThreeVector.hh"
#include "G4PhysicalConstants.hh"
#include "G4SystemOfUnits.hh"
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4OrePowellAtRestModel::G4OrePowellAtRestModel() : G4VPositronAtRestModel("OrePawell") {}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4OrePowellAtRestModel::SampleSecondaries(
std::vector<G4DynamicParticle*>& secParticles,
G4double&, const G4Material*) const
{
static const G4double PositronMass = CLHEP::electron_mass_c2;
const G4double ymax = 8.1;
CLHEP::HepRandomEngine* rndmEngine = G4Random::getTheEngine();
G4double cos12;
G4double cos13;
G4double r1;
G4double r2;
G4double r3;
G4double theta12;
G4double theta13;
G4double sin12;
G4double sin13;
G4double pdf;
G4double rndmv2[2];
G4double rndmv1;
do {
rndmv1 = rndmEngine->flat();
do {
rndmEngine->flatArray(2, rndmv2);
// energies of photon1 and photon2 normalized to electron rest mass
r1 = rndmv2[0];
r2 = rndmv2[1];
// energy conservation, with positronium assumed = 2 * electron rest mass
r3 = 2.0 - (r1+r2);
// cosine of angles between photons, from momentum conservation
cos12=(r3*r3 - r1*r1 -r2*r2)/(2*r1*r2);
cos13=(r2*r2 - r1*r1 -r3*r3)/(2*r1*r3);
// request both cosines < 1.
} while ( std::abs(cos12) > 1 || std::abs(cos13) > 1 );
theta12 = std::acos(cos12);
theta13 = - std::acos(cos13);
sin12 = std::sin(theta12);
sin13 = std::sin(theta13);
G4double cos23=cos12*cos13+sin12*sin13;
pdf = (1 - cos12)*(1 - cos12) + (1 - cos13)*(1 - cos13) + (1 - cos23)*(1 - cos23);
} while ( pdf < ymax * rndmv1 );
// END of Sampling
// photon directions in the decay plane, photon 1 along z, x perp to the plane.
G4ThreeVector PhotonMomentum1(0., 0., 1.);
G4ThreeVector PhotonMomentum2(0.,sin12,cos12);
G4ThreeVector PhotonMomentum3(0.,sin13,cos13);
// First Gamma direction
G4ThreeVector dir1 = G4RandomDirection();
PhotonMomentum1.rotateUz(dir1);
PhotonMomentum2.rotateUz(dir1);
PhotonMomentum3.rotateUz(dir1);
auto aGamma1 = new G4DynamicParticle(G4Gamma::Gamma(), PhotonMomentum1,
r1 * PositronMass);
//Random polarization
G4double phi1 = CLHEP::twopi * G4UniformRand();
G4ThreeVector pol1(std::cos(phi1),std::sin(phi1),0.0);
pol1.rotateUz(PhotonMomentum1);
aGamma1->SetPolarization(pol1);
secParticles.push_back(aGamma1);
auto aGamma2 = new G4DynamicParticle(G4Gamma::Gamma(), PhotonMomentum2,
r2 * PositronMass);
G4double phi2 = CLHEP::twopi * G4UniformRand();
G4ThreeVector pol2(std::cos(phi2),std::sin(phi2),0.0);
pol2.rotateUz(PhotonMomentum2);
aGamma2->SetPolarization(pol2);
secParticles.push_back(aGamma2);
auto aGamma3 = new G4DynamicParticle(G4Gamma::Gamma(), PhotonMomentum3,
r3 * PositronMass);
G4double phi3 = CLHEP::twopi * G4UniformRand();
G4ThreeVector pol3(std::cos(phi3),std::sin(phi3),0.0);
pol3.rotateUz(PhotonMomentum3);
aGamma3->SetPolarization(pol3);
secParticles.push_back(aGamma3);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4OrePowellAtRestModel::PrintGeneratorInformation() const
{
G4cout << "Orel Powell AtRest positron 3-gamma annihilation model" << G4endl;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -0,0 +1,89 @@
//
// ********************************************************************
// * 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. *
// ********************************************************************
//
//
// GEANT4 Class file
//
//
// File name: G4SimplePositronAtRestModel
//
// Author: Vladimir Ivanchenko
//
// Creation date: 14 May 2024
//
// -------------------------------------------------------------------
//
#include "G4SimplePositronAtRestModel.hh"
#include "G4DynamicParticle.hh"
#include "G4Material.hh"
#include "Randomize.hh"
#include "G4Gamma.hh"
#include "G4RandomDirection.hh"
#include "G4ThreeVector.hh"
#include "G4PhysicalConstants.hh"
#include "G4SystemOfUnits.hh"
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4SimplePositronAtRestModel::G4SimplePositronAtRestModel()
: G4VPositronAtRestModel("Simple")
{}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4SimplePositronAtRestModel::SampleSecondaries(
std::vector<G4DynamicParticle*>& secParticles,
G4double&, const G4Material*) const
{
G4ThreeVector dir1 = G4RandomDirection();
auto aGamma1 = new G4DynamicParticle(G4Gamma::Gamma(), dir1,
CLHEP::electron_mass_c2);
G4double phi = CLHEP::twopi * G4UniformRand();
G4double cosphi = std::cos(phi);
G4double sinphi = std::sin(phi);
G4ThreeVector pol1(cosphi, sinphi, 0.0);
pol1.rotateUz(dir1);
aGamma1->SetPolarization(pol1);
secParticles.push_back(aGamma1);
G4ThreeVector dir2 = -dir1;
auto aGamma2 = new G4DynamicParticle(G4Gamma::Gamma(), dir2,
CLHEP::electron_mass_c2);
G4ThreeVector pol2(-sinphi, cosphi, 0.0);
pol2.rotateUz(dir1);
aGamma2->SetPolarization(pol2);
secParticles.push_back(aGamma2);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4SimplePositronAtRestModel::PrintGeneratorInformation() const
{
G4cout << "\n" << G4endl;
G4cout << "Simple AtRest positron 2-gamma annihilation model" << G4endl;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -0,0 +1,93 @@
//
// ********************************************************************
// * 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. *
// ********************************************************************
//
//
// GEANT4 Class file
//
//
// File name: G4SimplePsAtRestModel
//
// Author: I.Semeniouk & D.Bernard
//
// Creation date: 04 Juin 2024
//
// -------------------------------------------------------------------
//
#include "G4SimplePsAtRestModel.hh"
#include "G4SimplePositronAtRestModel.hh"
#include "G4OrePowellAtRestModel.hh"
#include "G4DynamicParticle.hh"
#include "Randomize.hh"
#include "G4RandomDirection.hh"
#include "G4ThreeVector.hh"
#include "G4PhysicalConstants.hh"
#include "G4SystemOfUnits.hh"
#include "G4EmParameters.hh"
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4SimplePsAtRestModel::G4SimplePsAtRestModel()
: G4VPositronAtRestModel("SimplePs")
{
f3gFranction = G4EmParameters::Instance()->OrtoPsFraction();
model2g = new G4SimplePositronAtRestModel();
model3g = new G4OrePowellAtRestModel();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4SimplePsAtRestModel::SampleSecondaries(
std::vector<G4DynamicParticle*>& secParticles,
G4double& localEnergyDeposit, const G4Material* mat) const
{
// G4cout << "SampleSecondaries model " << GetName() << G4endl;
// G4cout << "3 gamma fraction " << f3gFranction << G4endl;
if ( G4UniformRand() > f3gFranction ) {
model2g->SampleSecondaries(secParticles,localEnergyDeposit,mat);
} else {
model3g->SampleSecondaries(secParticles,localEnergyDeposit,mat);
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4SimplePsAtRestModel::PrintGeneratorInformation() const
{
G4cout << G4endl;
model2g->PrintGeneratorInformation();
model3g->PrintGeneratorInformation();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4SimplePsAtRestModel::~G4SimplePsAtRestModel()
{
delete model2g;
delete model3g;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -124,7 +124,7 @@ G4UrbanMscModel::G4UrbanMscModel(const G4String& nam)
G4UrbanMscModel::~G4UrbanMscModel()
{
if(isFirstInstance) {
for(auto & ptr : msc) { delete ptr; }
for(auto const & ptr : msc) { delete ptr; }
msc.clear();
}
}
@@ -504,8 +504,9 @@ G4double G4UrbanMscModel::ComputeTruePathLengthLimit(
smallstep += 1.;
insideskin = false;
tgeom = geombig;
// initialisation at firs step and at the boundary
// initialisation at first step and at the boundary
if(firstStep || (stepStatus == fGeomBoundary))
{
rangeinit = currentRange;
@@ -520,22 +521,17 @@ G4double G4UrbanMscModel::ComputeTruePathLengthLimit(
<< " tlimitmin= " << tlimitmin << " geomlimit= "
<< geomlimit <<G4endl;
*/
// constraint from the geometry
if((geomlimit < geombig) && (geomlimit > geommin))
{
// geomlimit is a geometrical step length
// transform it to true path length (estimation)
if(lambda0 > geomlimit) {
geomlimit = -lambda0*G4Log(1.-geomlimit/lambda0)+tlimitmin;
}
tgeom = (stepStatus == fGeomBoundary)
? geomlimit/facgeom : 2.*geomlimit/facgeom;
}
else
{
tgeom = geombig;
}
}
// constraint from the geometry
if((geomlimit < geombig) && (geomlimit > geommin))
{
// geomlimit is a geometrical step length
// transform it to true path length (estimation)
if(lambda0 > geomlimit) {
geomlimit = -lambda0*G4Log(1.-geomlimit/lambda0)+tlimitmin;
}
tgeom = (stepStatus == fGeomBoundary) ? geomlimit/facgeom
: facrange*rangeinit + stepmin;
}
//step limit
@@ -83,14 +83,10 @@
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
using namespace std;
G4bool G4eeToTwoGammaModel::fSampleAtomicPDF = false;
G4eeToTwoGammaModel::G4eeToTwoGammaModel(const G4ParticleDefinition*,
const G4String& nam)
: G4VEmModel(nam),
pi_rcl2(pi*classic_electr_radius*classic_electr_radius)
pi_rcl2(CLHEP::pi*CLHEP::classic_electr_radius*CLHEP::classic_electr_radius)
{
theGamma = G4Gamma::Gamma();
fParticleChange = nullptr;
@@ -105,27 +101,7 @@ G4eeToTwoGammaModel::~G4eeToTwoGammaModel() = default;
void G4eeToTwoGammaModel::Initialise(const G4ParticleDefinition*,
const G4DataVector&)
{
if(IsMaster()) {
G4int verbose = G4EmParameters::Instance()->Verbose();
// redo initialisation for each new run
fSampleAtomicPDF = false;
const auto& materialTable = G4Material::GetMaterialTable();
for (const auto& material: *materialTable) {
const G4double meanEnergyPerIonPair = material->GetIonisation()->GetMeanEnergyPerIonPair();
if (meanEnergyPerIonPair > 0.) {
fSampleAtomicPDF = true;
if(verbose > 0) {
G4cout << "### G4eeToTwoGammaModel: for " << material->GetName() << " mean energy per ion pair is "
<< meanEnergyPerIonPair/CLHEP::eV << " eV" << G4endl;
}
}
}
}
// If no materials have meanEnergyPerIonPair set. This is probably the usual
// case, since most applications are not senstive to the slight
// non-collinearity of gammas in eeToTwoGamma. Do not issue any warning.
if(fParticleChange) { return; }
if (nullptr != fParticleChange) { return; }
fParticleChange = GetParticleChangeForGamma();
}
@@ -137,13 +113,13 @@ G4eeToTwoGammaModel::ComputeCrossSectionPerElectron(G4double kineticEnergy)
// Calculates the cross section per electron of annihilation into two photons
// from the Heilter formula.
G4double ekin = std::max(eV,kineticEnergy);
G4double ekin = std::max(CLHEP::eV, kineticEnergy);
G4double tau = ekin/electron_mass_c2;
G4double tau = ekin/CLHEP::electron_mass_c2;
G4double gam = tau + 1.0;
G4double gamma2= gam*gam;
G4double bg2 = tau * (tau+2.0);
G4double bg = sqrt(bg2);
G4double bg = std::sqrt(bg2);
G4double cross = pi_rcl2*((gamma2+4*gam+1.)*G4Log(gam+bg) - (gam+3.)*bg)
/ (bg2*(gam+1.));
@@ -178,183 +154,46 @@ G4double G4eeToTwoGammaModel::CrossSectionPerVolume(
// Polarisation of gamma according to M.H.L.Pryce and J.C.Ward,
// Nature 4065 (1947) 435.
void G4eeToTwoGammaModel::SampleSecondaries(vector<G4DynamicParticle*>* vdp,
const G4MaterialCutsCouple* pCutsCouple,
void G4eeToTwoGammaModel::SampleSecondaries(std::vector<G4DynamicParticle*>* vdp,
const G4MaterialCutsCouple*,
const G4DynamicParticle* dp,
G4double,
G4double)
{
G4double posiKinEnergy = dp->GetKineticEnergy();
G4DynamicParticle *aGamma1, *aGamma2;
CLHEP::HepRandomEngine* rndmEngine = G4Random::getTheEngine();
// Case at rest
if(posiKinEnergy == 0.0) {
const G4double eGamma = electron_mass_c2;
// In rest frame of positronium gammas are back to back
const G4ThreeVector& dir1 = G4RandomDirection();
const G4ThreeVector& dir2 = -dir1;
aGamma1 = new G4DynamicParticle(G4Gamma::Gamma(),dir1,eGamma);
aGamma2 = new G4DynamicParticle(G4Gamma::Gamma(),dir2,eGamma);
// In rest frame the gammas are polarised perpendicular to each other - see
// Pryce and Ward, Nature No 4065 (1947) p.435.
// Snyder et al, Physical Review 73 (1948) p.440.
G4ThreeVector pol1 = (G4RandomDirection().cross(dir1)).unit();
G4ThreeVector pol2 = (pol1.cross(dir2)).unit();
// But the positronium is moving...
// A positron in matter slows down and combines with an atomic electron to
// make a neutral “atom” called positronium, about half the size of a normal
// atom. I expect that when the energy of the positron is small enough,
// less than the binding energy of positronium (6.8 eV), it is
// energetically favourable for an electron from the outer orbitals of a
// nearby atom or molecule to transfer and bind to the positron, as in an
// ionic bond, leaving behind a mildly ionised nearby atom/molecule. I
// would expect the positronium to come away with a kinetic energy of a
// few eV on average. In its para (spin 0) state it annihilates into two
// photons, which in the rest frame of the positronium are collinear
// (back-to-back) due to momentum conservation. Because of the motion of the
// positronium, photons will be not quite back-to-back in the laboratory.
// The positroniuim acquires an energy of order its binding energy and
// doesn't have time to thermalise. Nevertheless, here we approximate its
// energy distribution by a Maxwell-Boltzman with mean energy <KE>. In terms
// of a more familiar concept of temperature, and the law of equipartition
// of energy of translational motion, <KE>=3kT/2. Each component of velocity
// has a distribution exp(-mv^2/2kT), which is a Gaussian of mean zero
// and variance kT/m=2<KE>/3m, where m is the positronium mass.
// We take <KE> = material->GetIonisation()->GetMeanEnergyPerIonPair().
if(fSampleAtomicPDF) {
const G4Material* material = pCutsCouple->GetMaterial();
const G4double meanEnergyPerIonPair = material->GetIonisation()->GetMeanEnergyPerIonPair();
const G4double& meanKE = meanEnergyPerIonPair; // Just an alias
if (meanKE > 0.) { // Positronium haas motion
// Mass of positronium
const G4double mass = 2.*electron_mass_c2;
// Mean <KE>=3kT/2, as described above
// const G4double T = 2.*meanKE/(3.*k_Boltzmann);
// Component velocities: Gaussian, variance kT/m=2<KE>/3m.
const G4double sigmav = std::sqrt(2.*meanKE/(3.*mass));
// This is in units where c=1
const G4double vx = G4RandGauss::shoot(0.,sigmav);
const G4double vy = G4RandGauss::shoot(0.,sigmav);
const G4double vz = G4RandGauss::shoot(0.,sigmav);
const G4ThreeVector v(vx,vy,vz); // In unit where c=1
const G4ThreeVector& beta = v; // so beta=v/c=v
aGamma1->Set4Momentum(aGamma1->Get4Momentum().boost(beta));
aGamma2->Set4Momentum(aGamma2->Get4Momentum().boost(beta));
// Rotate polarisation vectors
const G4ThreeVector& newDir1 = aGamma1->GetMomentumDirection();
const G4ThreeVector& newDir2 = aGamma2->GetMomentumDirection();
const G4ThreeVector& axis1 = dir1.cross(newDir1); // No need to be unit
const G4ThreeVector& axis2 = dir2.cross(newDir2); // No need to be unit
const G4double& angle1 = std::acos(dir1*newDir1);
const G4double& angle2 = std::acos(dir2*newDir2);
if (axis1 != G4ThreeVector()) pol1.rotate(axis1,angle1);
if (axis2 != G4ThreeVector()) pol2.rotate(axis2,angle2);
}
}
aGamma1->SetPolarization(pol1.x(),pol1.y(),pol1.z());
aGamma2->SetPolarization(pol2.x(),pol2.y(),pol2.z());
} else { // Positron interacts in flight
G4ThreeVector posiDirection = dp->GetMomentumDirection();
G4double tau = posiKinEnergy/electron_mass_c2;
G4double gam = tau + 1.0;
G4double tau2 = tau + 2.0;
G4double sqgrate = sqrt(tau/tau2)*0.5;
G4double sqg2m1 = sqrt(tau*tau2);
// limits of the energy sampling
G4double epsilmin = 0.5 - sqgrate;
G4double epsilmax = 0.5 + sqgrate;
G4double epsilqot = epsilmax/epsilmin;
//
// sample the energy rate of the created gammas
//
G4double epsil, greject;
do {
epsil = epsilmin*G4Exp(G4Log(epsilqot)*rndmEngine->flat());
greject = 1. - epsil + (2.*gam*epsil-1.)/(epsil*tau2*tau2);
// Loop checking, 03-Aug-2015, Vladimir Ivanchenko
} while( greject < rndmEngine->flat());
//
// scattered Gamma angles. ( Z - axis along the parent positron)
//
G4double cost = (epsil*tau2-1.)/(epsil*sqg2m1);
if(std::abs(cost) > 1.0) {
G4cout << "### G4eeToTwoGammaModel WARNING cost= " << cost
<< " positron Ekin(MeV)= " << posiKinEnergy
<< " gamma epsil= " << epsil
<< G4endl;
if(cost > 1.0) cost = 1.0;
else cost = -1.0;
}
G4double sint = sqrt((1.+cost)*(1.-cost));
G4double phi = twopi * rndmEngine->flat();
//
// kinematic of the created pair
//
G4double totalEnergy = posiKinEnergy + 2.0*electron_mass_c2;
G4double phot1Energy = epsil*totalEnergy;
G4ThreeVector phot1Direction(sint*cos(phi), sint*sin(phi), cost);
phot1Direction.rotateUz(posiDirection);
aGamma1 = new G4DynamicParticle (theGamma,phot1Direction, phot1Energy);
phi = twopi * rndmEngine->flat();
G4double cosphi = cos(phi);
G4double sinphi = sin(phi);
G4ThreeVector pol(cosphi, sinphi, 0.0);
pol.rotateUz(phot1Direction);
aGamma1->SetPolarization(pol.x(),pol.y(),pol.z());
G4double phot2Energy =(1.-epsil)*totalEnergy;
G4double posiP= sqrt(posiKinEnergy*(posiKinEnergy+2.*electron_mass_c2));
G4ThreeVector dir = posiDirection*posiP - phot1Direction*phot1Energy;
G4ThreeVector phot2Direction = dir.unit();
// create G4DynamicParticle object for the particle2
aGamma2 = new G4DynamicParticle (theGamma, phot2Direction, phot2Energy);
//!!! likely problematic direction to be checked
pol.set(-sinphi, cosphi, 0.0);
pol.rotateUz(phot1Direction);
cost = pol*phot2Direction;
pol -= cost*phot2Direction;
pol = pol.unit();
aGamma2->SetPolarization(pol.x(),pol.y(),pol.z());
/*
G4cout << "Annihilation on fly: e0= " << posiKinEnergy
<< " m= " << electron_mass_c2
<< " e1= " << phot1Energy
<< " e2= " << phot2Energy << " dir= " << dir
<< " -> " << phot1Direction << " "
<< phot2Direction << G4endl;
*/
}
vdp->push_back(aGamma1);
vdp->push_back(aGamma2);
// kill primary positron
fParticleChange->SetProposedKineticEnergy(0.0);
fParticleChange->ProposeTrackStatus(fStopAndKill);
// Case at rest not considered anymore inside this model
G4LorentzVector lv(dp->GetMomentum(),
dp->GetKineticEnergy() + 2*CLHEP::electron_mass_c2);
G4double eGammaCMS = 0.5 * lv.mag();
G4ThreeVector dir1 = G4RandomDirection();
G4double phi = CLHEP::twopi * G4UniformRand();
G4double cosphi = std::cos(phi);
G4double sinphi = std::sin(phi);
G4ThreeVector pol1(cosphi, sinphi, 0.0);
pol1.rotateUz(dir1);
G4LorentzVector lv1(eGammaCMS*dir1, eGammaCMS);
G4ThreeVector pol2(-sinphi, cosphi, 0.0);
pol2.rotateUz(dir1);
// transformation to lab system
lv1.boost(lv.boostVector());
lv -= lv1;
//!!! boost of polarisation vector is not yet implemented
// use constructors optimal for massless particle
auto aGamma1 = new G4DynamicParticle(G4Gamma::Gamma(), lv1.vect());
aGamma1->SetPolarization(pol1);
auto aGamma2 = new G4DynamicParticle(G4Gamma::Gamma(), lv.vect());
aGamma2->SetPolarization(pol2);
vdp->push_back(aGamma1);
vdp->push_back(aGamma2);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -54,12 +54,13 @@
#include "G4PhysicalConstants.hh"
#include "G4MaterialCutsCouple.hh"
#include "G4Gamma.hh"
#include "G4Electron.hh"
#include "G4Positron.hh"
#include "G4eeToTwoGammaModel.hh"
#include "G4EmBiasingManager.hh"
#include "G4EntanglementAuxInfo.hh"
#include "G4eplusAnnihilationEntanglementClipBoard.hh"
#include "G4SimplePositronAtRestModel.hh"
#include "G4AllisonPositronAtRestModel.hh"
#include "G4EmParameters.hh"
#include "G4PhysicsModelCatalog.hh"
@@ -68,12 +69,10 @@
G4eplusAnnihilation::G4eplusAnnihilation(const G4String& name)
: G4VEmProcess(name)
{
theGamma = G4Gamma::Gamma();
theElectron = G4Electron::Electron();
SetCrossSectionType(fEmDecreasing);
SetBuildTableFlag(false);
SetStartFromNullFlag(false);
SetSecondaryParticle(theGamma);
SetSecondaryParticle(G4Gamma::Gamma());
SetProcessSubType(fAnnihilation);
enableAtRestDoIt = true;
mainSecondaries = 2;
@@ -82,7 +81,10 @@ G4eplusAnnihilation::G4eplusAnnihilation(const G4String& name)
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4eplusAnnihilation::~G4eplusAnnihilation() = default;
G4eplusAnnihilation::~G4eplusAnnihilation()
{
delete fAtRestModel;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -104,13 +106,28 @@ G4double G4eplusAnnihilation::AtRestGetPhysicalInteractionLength(
void G4eplusAnnihilation::InitialiseProcess(const G4ParticleDefinition*)
{
if(!isInitialised) {
if (!isInitialised) {
isInitialised = true;
if(nullptr == EmModel(0)) { SetEmModel(new G4eeToTwoGammaModel()); }
EmModel(0)->SetLowEnergyLimit(MinKinEnergy());
EmModel(0)->SetHighEnergyLimit(MaxKinEnergy());
AddEmModel(1, EmModel(0));
}
auto param = G4EmParameters::Instance();
// AtRest model should be chosen only once
if (nullptr == fAtRestModel) {
auto type = param->PositronAtRestModelType();
if (type == fAllisonPositronium) {
fAtRestModel = new G4AllisonPositronAtRestModel();
} else {
fAtRestModel = new G4SimplePositronAtRestModel();
}
}
// Check that entanglement is switched on
// It may be set by the UI command "/process/em/QuantumEntanglement true".
fEntangled = param->QuantumEntanglement();
fApplyCuts = param->ApplyCuts();
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -122,136 +139,96 @@ void G4eplusAnnihilation::StreamProcessInfo(std::ostream&) const
G4VParticleChange* G4eplusAnnihilation::AtRestDoIt(const G4Track& track,
const G4Step& step)
// Performs the e+ e- annihilation when both particles are assumed at rest.
{
// positron at rest should be killed
fParticleChange.InitializeForPostStep(track);
fParticleChange.SetProposedKineticEnergy(0.);
fParticleChange.ProposeTrackStatus(fStopAndKill);
DefineMaterial(track.GetMaterialCutsCouple());
G4int idx = (G4int)CurrentMaterialCutsCoupleIndex();
G4double ene(0.0);
G4VEmModel* model = SelectModel(ene, idx);
auto couple = step.GetPreStepPoint()->GetMaterialCutsCouple();
DefineMaterial(couple);
G4double gammaCut = GetGammaEnergyCut();
// define new weight for primary and secondaries
G4double weight = fParticleChange.GetParentWeight();
// apply cuts
if (fApplyCuts && gammaCut > CLHEP::electron_mass_c2) {
fParticleChange.ProposeLocalEnergyDeposit(2*CLHEP::electron_mass_c2);
return &fParticleChange;
}
// sample secondaries
secParticles.clear();
G4double gammaCut = GetGammaEnergyCut();
model->SampleSecondaries(&secParticles, MaterialCutsCouple(),
track.GetDynamicParticle(), gammaCut);
G4int num0 = (G4int)secParticles.size();
G4double edep = 0.0;
fAtRestModel->SampleSecondaries(secParticles, edep, couple->GetMaterial());
// define new weight for primary and secondaries
G4double weight = fParticleChange.GetParentWeight();
std::size_t num0 = secParticles.size();
// splitting or Russian roulette
if(biasManager) {
if(biasManager->SecondaryBiasingRegion(idx)) {
if (nullptr != biasManager) {
G4int idx = couple->GetIndex();
if (biasManager->SecondaryBiasingRegion(idx) &&
!biasManager->GetDirectionalSplitting()) {
G4VEmModel* mod = nullptr;
G4double eloss = 0.0;
weight *= biasManager->ApplySecondaryBiasing(
secParticles, track, model, &fParticleChange, eloss,
idx, gammaCut, step.GetPostStepPoint()->GetSafety());
if(eloss > 0.0) {
eloss += fParticleChange.GetLocalEnergyDeposit();
fParticleChange.ProposeLocalEnergyDeposit(eloss);
}
weight *= biasManager->ApplySecondaryBiasing(secParticles, track, mod,
&fParticleChange, eloss,
idx, gammaCut);
edep += eloss;
}
}
// save secondaries
G4int num = (G4int)secParticles.size();
std::size_t num = secParticles.size();
// Check that entanglement is switched on... (the following flag is
// set by /process/em/QuantumEntanglement).
G4bool entangled = G4EmParameters::Instance()->QuantumEntanglement();
// ...and that we have two gammas with both gammas' energies above
// gammaCut (entanglement is only programmed for e+ e- -> gamma gamma).
G4bool entangledgammagamma = false;
if (entangled) {
if (num == 2) {
entangledgammagamma = true;
for (const auto* p: secParticles) {
if (p->GetDefinition() != theGamma ||
p->GetKineticEnergy() < gammaCut) {
entangledgammagamma = false;
}
}
}
}
// Prepare a shared pointer for psossible use below. If it is used, the
// Prepare a shared pointer only for two first gamma. If it is used, the
// shared pointer is copied into the tracks through G4EntanglementAuxInfo.
// This ensures the clip board lasts until both tracks are destroyed.
// It is assumed that 2 first secondary particles are the most energetic gamma
std::shared_ptr<G4eplusAnnihilationEntanglementClipBoard> clipBoard;
if (entangledgammagamma) {
if (fEntangled && num >= 2) {
clipBoard = std::make_shared<G4eplusAnnihilationEntanglementClipBoard>();
clipBoard->SetParentParticleDefinition(track.GetDefinition());
}
if(num > 0) {
if (num > 0) {
const G4double time = track.GetGlobalTime();
const G4ThreeVector& pos = track.GetPosition();
auto touch = track.GetTouchableHandle();
for (std::size_t i=0; i<num; ++i) {
G4DynamicParticle* dp = secParticles[i];
G4Track* t = new G4Track(dp, time, pos);
t->SetTouchableHandle(touch);
if (fEntangled && i < 2) {
// entangledgammagamma is only true when there are only two gammas
// (See code above where entangledgammagamma is calculated.)
if (i == 0) { // First gamma
clipBoard->SetTrackA(t);
} else if (i == 1) { // Second gamma
clipBoard->SetTrackB(t);
}
t->SetAuxiliaryTrackInformation
(fEntanglementModelID, new G4EntanglementAuxInfo(clipBoard));
}
if (nullptr != biasManager) {
t->SetWeight(weight * biasManager->GetWeight((G4int)i));
} else {
t->SetWeight(weight);
}
pParticleChange->AddSecondary(t);
fParticleChange.SetNumberOfSecondaries(num);
G4double edep = fParticleChange.GetLocalEnergyDeposit();
G4double time = track.GetGlobalTime();
for (G4int i=0; i<num; ++i) {
if (secParticles[i]) {
G4DynamicParticle* dp = secParticles[i];
const G4ParticleDefinition* p = dp->GetParticleDefinition();
G4double e = dp->GetKineticEnergy();
G4bool good = true;
if(ApplyCuts()) {
if (p == theGamma) {
if (e < gammaCut) { good = false; }
} else if (p == theElectron) {
if (e < GetElectronEnergyCut()) { good = false; }
}
// added secondary if it is good
}
if (good) {
G4Track* t = new G4Track(dp, time, track.GetPosition());
t->SetTouchableHandle(track.GetTouchableHandle());
if (entangledgammagamma) {
// entangledgammagamma is only true when there are only two gammas
// (See code above where entangledgammagamma is calculated.)
if (i == 0) { // First gamma
clipBoard->SetTrackA(t);
} else if (i == 1) { // Second gamma
clipBoard->SetTrackB(t);
}
t->SetAuxiliaryTrackInformation
(fEntanglementModelID,new G4EntanglementAuxInfo(clipBoard));
}
if (biasManager) {
t->SetWeight(weight * biasManager->GetWeight(i));
} else {
t->SetWeight(weight);
}
pParticleChange->AddSecondary(t);
// define type of secondary
if(i < mainSecondaries) { t->SetCreatorModelID(secID); }
else if(i < num0) {
if(p == theGamma) {
t->SetCreatorModelID(fluoID);
} else {
t->SetCreatorModelID(augerID);
}
} else {
t->SetCreatorModelID(biasID);
}
/*
G4cout << "Secondary(post step) has weight " << t->GetWeight()
<< ", Ekin= " << t->GetKineticEnergy()/MeV << " MeV "
<< GetProcessName() << " fluoID= " << fluoID
<< " augerID= " << augerID <<G4endl;
*/
} else {
delete dp;
edep += e;
}
}
// define type of secondary
if (i < num0) {
t->SetCreatorModelID(secID);
}
else {
t->SetCreatorModelID(biasID);
}
}
fParticleChange.ProposeLocalEnergyDeposit(edep);
}
fParticleChange.ProposeLocalEnergyDeposit(edep);
return &fParticleChange;
}
@@ -35,6 +35,7 @@
//
// Creation date: 29.03.2018
//
//
// -------------------------------------------------------------------
//
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -53,6 +54,7 @@
#include "G4DataVector.hh"
#include "G4PhysicsVector.hh"
#include "G4PhysicsLogVector.hh"
#include "G4RandomDirection.hh"
#include "Randomize.hh"
#include "G4ParticleChangeForGamma.hh"
#include "G4Log.hh"
@@ -60,64 +62,70 @@
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
using namespace std;
G4PhysicsVector* G4eplusTo2GammaOKVIModel::fCrossSection = nullptr;
G4PhysicsVector* G4eplusTo2GammaOKVIModel::fCrossSection3G = nullptr;
G4PhysicsVector* G4eplusTo2GammaOKVIModel::f3GProbability = nullptr;
G4eplusTo2GammaOKVIModel::G4eplusTo2GammaOKVIModel(const G4ParticleDefinition*,
const G4String& nam)
: G4VEmModel(nam),
fDelta(0.001),
fGammaTh(MeV)
G4eplusTo2GammaOKVIModel::G4eplusTo2GammaOKVIModel()
: G4VEmModel("eplus2ggOKVI"),
fDeltaMin(0.001),
fDelta(fDeltaMin),
fGammaTh(CLHEP::MeV)
{
theGamma = G4Gamma::Gamma();
fParticleChange = nullptr;
fCuts = nullptr;
f3GModel = new G4eplusTo3GammaOKVIModel();
SetTripletModel(f3GModel);
// instantiate vectors once
if (nullptr == fCrossSection) {
G4double emin = 10*CLHEP::eV;
G4double emax = 100*CLHEP::TeV;
G4int nbins = 20*G4lrint(std::log10(emax/emin));
fCrossSection = new G4PhysicsLogVector(emin, emax, nbins, true);
f3GProbability = new G4PhysicsLogVector(emin, emax, nbins, true);
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
G4eplusTo2GammaOKVIModel::~G4eplusTo2GammaOKVIModel() = default;
G4eplusTo2GammaOKVIModel::~G4eplusTo2GammaOKVIModel()
{
if (IsMaster()) {
delete fCrossSection;
delete f3GProbability;
fCrossSection = nullptr;
f3GProbability = nullptr;
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void G4eplusTo2GammaOKVIModel::Initialise(const G4ParticleDefinition* p,
const G4DataVector& cuts)
{
f3GModel->Initialise(p, cuts);
fCuts = &cuts;
fGammaTh = G4EmParameters::Instance()->LowestTripletEnergy();
f3GModel->SetDelta(fDelta);
if(IsMaster()) {
if(!fCrossSection) {
G4double emin = 10*eV;
G4double emax = 100*TeV;
G4int nbins = 20*G4lrint(std::log10(emax/emin));
fCrossSection = new G4PhysicsLogVector(emin, emax, nbins, true);
fCrossSection3G = new G4PhysicsLogVector(emin, emax, nbins, true);
f3GProbability = new G4PhysicsLogVector(emin, emax, nbins, true);
for(G4int i=0; i<= nbins; ++i) {
G4double e = fCrossSection->Energy(i);
G4double cs2 = ComputeCrossSectionPerElectron(e);
G4double cs3 = f3GModel->ComputeCrossSectionPerElectron(e);
cs2 += cs3;
fCrossSection->PutValue(i, cs2);
fCrossSection3G->PutValue(i, cs3);
f3GProbability->PutValue(i, cs3/cs2);
}
fCrossSection->FillSecondDerivatives();
fCrossSection3G->FillSecondDerivatives();
f3GProbability->FillSecondDerivatives();
}
}
// here particle change is set for the triplet model
if(fParticleChange) { return; }
fParticleChange = GetParticleChangeForGamma();
if (nullptr == fParticleChange) {
fParticleChange = GetParticleChangeForGamma();
}
// initialialise 3-gamma model before new run
f3GModel->Initialise(p, cuts);
fGammaTh = G4EmParameters::Instance()->LowestTripletEnergy();
// initialise vectors
if (IsMaster()) {
std::size_t num = fCrossSection->GetVectorLength();
for (std::size_t i=0; i<num; ++i) {
G4double e = fCrossSection->Energy(i);
G4double cs2 = ComputeCrossSectionPerElectron(e);
G4double cs3 = f3GModel->ComputeCrossSectionPerElectron(e);
cs2 += cs3;
fCrossSection->PutValue(i, cs2);
G4double y = (cs2 > 0.0) ? cs3/cs2 : 0.0;
f3GProbability->PutValue(i, y);
}
fCrossSection->FillSecondDerivatives();
f3GProbability->FillSecondDerivatives();
}
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -129,16 +137,20 @@ G4eplusTo2GammaOKVIModel::ComputeCrossSectionPerElectron(G4double kinEnergy)
// photons from the Heilter formula with the radiation correction to 3 gamma
// annihilation channel. (A.A.) rho is changed
G4double ekin = std::max(eV,kinEnergy);
G4double tau = ekin/electron_mass_c2;
G4double ekin = std::max(CLHEP::eV, kinEnergy);
G4double tau = ekin/CLHEP::electron_mass_c2;
G4double gam = tau + 1.0;
G4double gamma2 = gam*gam;
G4double bg2 = tau * (tau+2.0);
G4double bg = sqrt(bg2);
G4double bg = std::sqrt(bg2);
G4double rho = (gamma2+4.*gam+1.)*G4Log(gam+bg)/(gamma2-1.)
- (gam+3.)/(sqrt(gam*gam - 1.));
- (gam+3.)/(std::sqrt(gam*gam - 1.));
G4double eGammaCMS = CLHEP::electron_mass_c2 * std::sqrt(0.5*(tau + 2.0));
fDelta = std::max(fDeltaMin, fGammaTh/eGammaCMS);
f3GModel->SetDelta(fDelta);
static const G4double pir2 = pi*classic_electr_radius*classic_electr_radius;
static const G4double pir2 =
CLHEP::pi*CLHEP::classic_electr_radius*CLHEP::classic_electr_radius;
G4double cross = (pir2*rho + alpha_rcl2*2.*G4Log(fDelta)*rho*rho)/(gam+1.);
return cross;
@@ -175,143 +187,54 @@ G4double G4eplusTo2GammaOKVIModel::CrossSectionPerVolume(
// Polarisation of gamma according to M.H.L.Pryce and J.C.Ward,
// Nature 4065 (1947) 435.
void
G4eplusTo2GammaOKVIModel::SampleSecondaries(vector<G4DynamicParticle*>* vdp,
const G4MaterialCutsCouple* mcc,
const G4DynamicParticle* dp,
G4double, G4double)
void G4eplusTo2GammaOKVIModel::SampleSecondaries(
std::vector<G4DynamicParticle*>* vdp,
const G4MaterialCutsCouple* couple,
const G4DynamicParticle* dp,
G4double, G4double)
{
G4double posiKinEnergy = dp->GetKineticEnergy();
CLHEP::HepRandomEngine* rndmEngine = G4Random::getTheEngine();
if(rndmEngine->flat() < f3GProbability->Value(posiKinEnergy)) {
G4double cutd = std::max(fGammaTh,(*fCuts)[mcc->GetIndex()])
/(posiKinEnergy + electron_mass_c2);
// check cut to avoid production of 3d gamma below
if(cutd > fDelta) {
G4double cs30 = fCrossSection3G->Value(posiKinEnergy);
f3GModel->SetDelta(cutd);
G4double cs3 = f3GModel->ComputeCrossSectionPerElectron(posiKinEnergy);
if(rndmEngine->flat()*cs30 < cs3) {
f3GModel->SampleSecondaries(vdp, mcc, dp);
return;
}
} else {
f3GModel->SampleSecondaries(vdp, mcc, dp);
return;
}
}
G4DynamicParticle *aGamma1, *aGamma2;
// Case at rest
if(posiKinEnergy == 0.0) {
G4double cost = 2.*rndmEngine->flat()-1.;
G4double sint = sqrt((1. - cost)*(1. + cost));
G4double phi = twopi * rndmEngine->flat();
G4ThreeVector dir(sint*cos(phi), sint*sin(phi), cost);
phi = twopi * rndmEngine->flat();
G4double cosphi = cos(phi);
G4double sinphi = sin(phi);
G4ThreeVector pol(cosphi, sinphi, 0.0);
pol.rotateUz(dir);
aGamma1 = new G4DynamicParticle(theGamma, dir, electron_mass_c2);
aGamma1->SetPolarization(pol.x(),pol.y(),pol.z());
aGamma2 = new G4DynamicParticle(theGamma,-dir, electron_mass_c2);
pol.set(-sinphi, cosphi, 0.0);
pol.rotateUz(dir);
aGamma2->SetPolarization(pol.x(),pol.y(),pol.z());
} else {
G4ThreeVector posiDirection = dp->GetMomentumDirection();
G4double tau = posiKinEnergy/electron_mass_c2;
G4double gam = tau + 1.0;
G4double tau2 = tau + 2.0;
G4double sqgrate = sqrt(tau/tau2)*0.5;
G4double sqg2m1 = sqrt(tau*tau2);
// limits of the energy sampling
G4double epsilmin = 0.5 - sqgrate;
G4double epsilmax = 0.5 + sqgrate;
G4double epsilqot = epsilmax/epsilmin;
//
// sample the energy rate of the created gammas
//
G4double epsil, greject;
do {
epsil = epsilmin*G4Exp(G4Log(epsilqot)*rndmEngine->flat());
greject = 1. - epsil + (2.*gam*epsil-1.)/(epsil*tau2*tau2);
// Loop checking, 03-Aug-2015, Vladimir Ivanchenko
} while( greject < rndmEngine->flat());
//
// scattered Gamma angles. ( Z - axis along the parent positron)
//
G4double cost = (epsil*tau2-1.)/(epsil*sqg2m1);
if(std::abs(cost) > 1.0) {
G4cout << "### G4eplusTo2GammaOKVIModel WARNING cost= " << cost
<< " positron Ekin(MeV)= " << posiKinEnergy
<< " gamma epsil= " << epsil
<< G4endl;
if(cost > 1.0) cost = 1.0;
else cost = -1.0;
}
G4double sint = sqrt((1.+cost)*(1.-cost));
G4double phi = twopi * rndmEngine->flat();
//
// kinematic of the created pair
//
G4double TotalAvailableEnergy = posiKinEnergy + 2.0*electron_mass_c2;
G4double phot1Energy = epsil*TotalAvailableEnergy;
G4ThreeVector phot1Direction(sint*cos(phi), sint*sin(phi), cost);
phot1Direction.rotateUz(posiDirection);
aGamma1 = new G4DynamicParticle (theGamma,phot1Direction, phot1Energy);
phi = twopi * rndmEngine->flat();
G4double cosphi = cos(phi);
G4double sinphi = sin(phi);
G4ThreeVector pol(cosphi, sinphi, 0.0);
pol.rotateUz(phot1Direction);
aGamma1->SetPolarization(pol.x(),pol.y(),pol.z());
G4double phot2Energy =(1.-epsil)*TotalAvailableEnergy;
G4double posiP= sqrt(posiKinEnergy*(posiKinEnergy+2.*electron_mass_c2));
G4ThreeVector dir = posiDirection*posiP - phot1Direction*phot1Energy;
G4ThreeVector phot2Direction = dir.unit();
// create G4DynamicParticle object for the particle2
aGamma2 = new G4DynamicParticle (theGamma,phot2Direction, phot2Energy);
//!!! likely problematic direction to be checked
pol.set(-sinphi, cosphi, 0.0);
pol.rotateUz(phot1Direction);
cost = pol*phot2Direction;
pol -= cost*phot2Direction;
pol = pol.unit();
aGamma2->SetPolarization(pol.x(),pol.y(),pol.z());
}
/*
G4cout << "Annihilation in fly: e0= " << posiKinEnergy
<< " m= " << electron_mass_c2
<< " e1= " << phot1Energy
<< " e2= " << phot2Energy << " dir= " << dir
<< " -> " << phot1Direction << " "
<< phot2Direction << G4endl;
*/
vdp->push_back(aGamma1);
vdp->push_back(aGamma2);
// kill primary positron
fParticleChange->SetProposedKineticEnergy(0.0);
fParticleChange->ProposeTrackStatus(fStopAndKill);
// Case at rest not considered anymore
G4double posiKinEnergy = dp->GetKineticEnergy();
G4LorentzVector lv(dp->GetMomentum(),
posiKinEnergy + 2*CLHEP::electron_mass_c2);
G4double eGammaCMS = 0.5 * lv.mag();
if (G4UniformRand() < f3GProbability->Value(posiKinEnergy)) {
fDelta = std::max(fDeltaMin, fGammaTh/eGammaCMS);
f3GModel->SetDelta(fDelta);
f3GModel->SampleSecondaries(vdp, couple, dp);
return;
}
G4ThreeVector dir1 = G4RandomDirection();
G4double phi = CLHEP::twopi * G4UniformRand();
G4double cosphi = std::cos(phi);
G4double sinphi = std::sin(phi);
G4ThreeVector pol1(cosphi, sinphi, 0.0);
pol1.rotateUz(dir1);
G4LorentzVector lv1(eGammaCMS*dir1, eGammaCMS);
G4ThreeVector pol2(-sinphi, cosphi, 0.0);
pol2.rotateUz(dir1);
// transformation to lab system
lv1.boost(lv.boostVector());
lv -= lv1;
//!!! boost of polarisation vector is not yet implemented
// use constructors optimal for massless particle
auto aGamma1 = new G4DynamicParticle(G4Gamma::Gamma(), lv1.vect());
aGamma1->SetPolarization(pol1);
auto aGamma2 = new G4DynamicParticle(G4Gamma::Gamma(), lv.vect());
aGamma2->SetPolarization(pol2);
vdp->push_back(aGamma1);
vdp->push_back(aGamma2);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -62,7 +62,6 @@ G4eplusTo3GammaOKVIModel::G4eplusTo3GammaOKVIModel(const G4ParticleDefinition*,
: G4VEmModel(nam), fDelta(0.001)
{
theGamma = G4Gamma::Gamma();
fParticleChange = nullptr;
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
@@ -73,11 +72,7 @@ G4eplusTo3GammaOKVIModel::~G4eplusTo3GammaOKVIModel() = default;
void G4eplusTo3GammaOKVIModel::Initialise(const G4ParticleDefinition*,
const G4DataVector&)
{
// here particle change is set for the triplet model
if(fParticleChange) { return; }
fParticleChange = GetParticleChangeForGamma();
}
{}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
@@ -183,8 +178,8 @@ G4eplusTo3GammaOKVIModel::ComputeCrossSectionPerElectron(G4double kinEnergy)
// Calculates the cross section per electron of annihilation into 3 photons
// from the Heilter formula.
G4double ekin = std::max(eV,kinEnergy);
G4double tau = ekin/electron_mass_c2;
G4double ekin = std::max(CLHEP::eV, kinEnergy);
G4double tau = ekin/CLHEP::electron_mass_c2;
G4double gam = tau + 1.0;
G4double gamma2 = gam*gam;
G4double bg2 = tau * (tau+2.0);
@@ -204,10 +199,6 @@ G4double G4eplusTo3GammaOKVIModel::ComputeCrossSectionPerAtom(
G4double kineticEnergy, G4double Z,
G4double, G4double, G4double)
{
// Calculates the cross section per atom of annihilation into two photons
G4double cross = Z*ComputeCrossSectionPerElectron(kineticEnergy);
return cross;
}
@@ -238,149 +229,78 @@ G4eplusTo3GammaOKVIModel::SampleSecondaries(vector<G4DynamicParticle*>* vdp,
const G4DynamicParticle* dp,
G4double, G4double)
{
// let us perform sampling in C.M.S. reference frame of e- at rest and e+ on fly
G4double posiKinEnergy = dp->GetKineticEnergy();
G4DynamicParticle *aGamma1, *aGamma2;
G4DynamicParticle* aGamma3 = nullptr;
G4double border;
G4LorentzVector lv(dp->GetMomentum(),
posiKinEnergy + 2*CLHEP::electron_mass_c2);
G4double eGammaCMS = 0.5 * lv.mag();
if(posiKinEnergy < 500*MeV) {
border = 1. - (electron_mass_c2)/(2*(posiKinEnergy + electron_mass_c2));
} else {
border = 1. - (100*electron_mass_c2)/(2*(posiKinEnergy + electron_mass_c2));
}
border = std::min(border, 0.9999);
// the limit value fDelta is defined by a class, which call this method
// thickness of border defined by C.M.S. energy
G4double border =
1.0 - std::min(std::max(CLHEP::electron_mass_c2/eGammaCMS, fDelta), 0.1);
CLHEP::HepRandomEngine* rndmEngine = G4Random::getTheEngine();
// Case at rest
if(posiKinEnergy == 0.0) {
G4double cost = 2.*rndmEngine->flat()-1.;
G4double sint = sqrt((1. - cost)*(1. + cost));
G4double phi = twopi * rndmEngine->flat();
G4ThreeVector dir(sint*cos(phi), sint*sin(phi), cost);
phi = twopi * rndmEngine->flat();
G4double cosphi = cos(phi);
G4double sinphi = sin(phi);
G4ThreeVector pol(cosphi, sinphi, 0.0);
pol.rotateUz(dir);
aGamma1 = new G4DynamicParticle(theGamma, dir, electron_mass_c2);
aGamma1->SetPolarization(pol.x(),pol.y(),pol.z());
aGamma2 = new G4DynamicParticle(theGamma,-dir, electron_mass_c2);
pol.set(-sinphi, cosphi, 0.0);
pol.rotateUz(dir);
aGamma2->SetPolarization(pol.x(),pol.y(),pol.z());
G4ThreeVector posiDirection = dp->GetMomentumDirection();
} else {
G4ThreeVector posiDirection = dp->GetMomentumDirection();
// (A.A.) LIMITS FOR 1st GAMMA
G4double xmin = 0.01;
G4double xmax = 0.667; // CHANGE to 3/2
// (A.A.) LIMITS FOR 1st GAMMA
G4double xmin = 0.01;
G4double xmax = 0.667; // CHANGE to 3/2
G4double d1, d0, x1, x2, dmax, x2min;
G4double d1, d0, x1, x2, dmax, x2min;
// (A.A.) sampling of x1 x2 x3 (whole cycle of rejection)
do {
x1 = 1/((1/xmin) - ((1/xmin)-(1/xmax))*rndmEngine->flat());
dmax = ComputeFS(posiKinEnergy, x1,1.-x1,border);
x2min = 1.-x1;
x2 = 1 - rndmEngine->flat()*(1-x2min);
d1 = dmax*rndmEngine->flat();
d0 = ComputeFS(posiKinEnergy,x1,x2,2-x1-x2);
}
while(d0 < d1);
G4double x3 = 2 - x1 - x2;
//
// angles between Gammas
//
G4double psi13 = 2*asin(sqrt(std::abs((x1+x3-1)/(x1*x3))));
G4double psi12 = 2*asin(sqrt(std::abs((x1+x2-1)/(x1*x2))));
// sin^t
//G4double phi = twopi * rndmEngine->flat();
//G4double psi = acos(x3); // Angle of the plane
//
// kinematic of the created pair
//
G4double TotalAvailableEnergy = posiKinEnergy + 2.0*electron_mass_c2;
G4double phot1Energy = 0.5*x1*TotalAvailableEnergy;
G4double phot2Energy = 0.5*x2*TotalAvailableEnergy;
G4double phot3Energy = 0.5*x3*TotalAvailableEnergy;
// (A.A.) sampling of x1 x2 x3 (whole cycle of rejection)
do {
x1 = 1./((1./xmin) - ((1./xmin)-(1./xmax))*rndmEngine->flat());
dmax = ComputeFS(eGammaCMS, x1, 1.-x1, border);
x2min = 1. - x1;
x2 = 1 - rndmEngine->flat()*(1. - x2min);
d1 = dmax*rndmEngine->flat();
d0 = ComputeFS(eGammaCMS, x1, x2, 2.-x1-x2);
}
while(d0 < d1);
G4double x3 = 2 - x1 - x2;
//
// angles between Gammas
//
G4double psi13 = 2*std::asin(std::sqrt(std::abs((x1+x3-1.)/(x1*x3))));
G4double psi12 = 2*std::asin(std::sqrt(std::abs((x1+x2-1.)/(x1*x2))));
//
// kinematic of the created pair
//
G4double phot1Energy = x1*eGammaCMS;
G4double phot2Energy = x2*eGammaCMS;
G4double phot3Energy = x3*eGammaCMS;
// DIRECTIONS
// DIRECTIONS
// The azimuthal angles of ql and q3 with respect to some plane
// through the beam axis are generated at random.
// The azimuthal angles of q1 and q3 with respect to some plane
// through the beam axis are generated at random.
G4ThreeVector phot1Direction(0, 0, 1);
G4ThreeVector phot2Direction(0, sin(psi12), cos(psi12));
G4ThreeVector phot3Direction(0, sin(psi13), cos(psi13));
G4ThreeVector phot1Direction(0, 0, 1);
G4ThreeVector phot2Direction(0, std::sin(psi12), std::cos(psi12));
G4ThreeVector phot3Direction(0, std::sin(psi13), std::cos(psi13));
phot1Direction.rotateUz(posiDirection);
phot2Direction.rotateUz(posiDirection);
phot3Direction.rotateUz(posiDirection);
G4LorentzVector lv1(phot1Energy*phot1Direction, phot1Energy);
G4LorentzVector lv2(phot2Energy*phot2Direction, phot2Energy);
G4LorentzVector lv3(phot3Energy*phot3Direction, phot3Energy);
aGamma1 = new G4DynamicParticle (theGamma,phot1Direction, phot1Energy);
aGamma2 = new G4DynamicParticle (theGamma,phot2Direction, phot2Energy);
aGamma3 = new G4DynamicParticle (theGamma,phot3Direction, phot3Energy);
auto boostV = lv.boostVector();
lv1.boost(boostV);
lv2.boost(boostV);
lv3.boost(boostV);
auto aGamma1 = new G4DynamicParticle (theGamma, lv1.vect());
auto aGamma2 = new G4DynamicParticle (theGamma, lv2.vect());
auto aGamma3 = new G4DynamicParticle (theGamma, lv3.vect());
//POLARIZATION - ???
/*
phi = twopi * rndmEngine->flat();
G4double cosphi = cos(phi);
G4double sinphi = sin(phi);
G4ThreeVector pol(cosphi, sinphi, 0.0);
pol.rotateUz(phot1Direction);
aGamma1->SetPolarization(pol.x(),pol.y(),pol.z());
G4double phot2Energy =(1.-epsil)*TotalAvailableEnergy;
G4double posiP= sqrt(posiKinEnergy*(posiKinEnergy+2.*electron_mass_c2));
G4ThreeVector dir = posiDirection*posiP - phot1Direction*phot1Energy;
G4ThreeVector phot2Direction = dir.unit();
// create G4DynamicParticle object for the particle2
aGamma2 = new G4DynamicParticle (theGamma,phot2Direction, phot2Energy);
//!!! likely problematic direction to be checked
pol.set(-sinphi, cosphi, 0.0);
pol.rotateUz(phot1Direction);
cost = pol*phot2Direction;
pol -= cost*phot2Direction;
pol = pol.unit();
aGamma2->SetPolarization(pol.x(),pol.y(),pol.z());
*/
}
/*
G4cout << "Annihilation in fly: e0= " << posiKinEnergy
<< " m= " << electron_mass_c2
<< " e1= " << phot1Energy
<< " e2= " << phot2Energy << " dir= " << dir
<< " -> " << phot1Direction << " "
<< phot2Direction << G4endl;
*/
//!!! POLARIZATION - not yet implemented
vdp->push_back(aGamma1);
vdp->push_back(aGamma2);
if(aGamma3 != nullptr) { vdp->push_back(aGamma3); }
// kill primary positron
fParticleChange->SetProposedKineticEnergy(0.0);
fParticleChange->ProposeTrackStatus(fStopAndKill);
vdp->push_back(aGamma3);
}
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....