697 lines
27 KiB
C++
697 lines
27 KiB
C++
//
|
|
// ********************************************************************
|
|
// * 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: G4BetheHeitler5DModel.cc
|
|
//
|
|
// Authors:
|
|
// Igor Semeniouk and Denis Bernard,
|
|
// LLR, Ecole polytechnique & CNRS/IN2P3, 91128 Palaiseau, France
|
|
//
|
|
// Acknowledgement of the support of the French National Research Agency
|
|
// (ANR-13-BS05-0002).
|
|
//
|
|
// Reference: Nucl. Instrum. Meth. A 899 (2018) 85 (arXiv:1802.08253 [hep-ph])
|
|
// Nucl. Instrum. Meth., A 936 (2019) 290
|
|
//
|
|
// Class Description:
|
|
//
|
|
// Generates the conversion of a high-energy photon to an e+e- pair, either in the field of an
|
|
// atomic electron (triplet) or nucleus (nuclear).
|
|
// Samples the five-dimensional (5D) differential cross-section analytical expression:
|
|
// . Non polarized conversion:
|
|
// H.A. Bethe, W. Heitler, Proc. R. Soc. Lond. Ser. A 146 (1934) 83.
|
|
// . Polarized conversion:
|
|
// T. H. Berlin and L. Madansky, Phys. Rev. 78 (1950) 623,
|
|
// M. M. May, Phys. Rev. 84 (1951) 265,
|
|
// J. M. Jauch and F. Rohrlich, The theory of photons and electrons, 1976.
|
|
//
|
|
// All the above expressions are named "Bethe-Heitler" here.
|
|
//
|
|
// Bethe & Heitler, put in Feynman diagram parlance, compute only the two dominant diagrams of
|
|
// the first order Born development, which is an excellent approximation for nuclear conversion
|
|
// and for high-energy triplet conversion.
|
|
//
|
|
// Only the linear polarisation of the incoming photon takes part in these expressions.
|
|
// The circular polarisation of the incoming photon does not (take part) and no polarisation
|
|
// is transfered to the final leptons.
|
|
//
|
|
// In case conversion takes place in the field of an isolated nucleus or electron, the bare
|
|
// Bethe-Heitler expression is used.
|
|
//
|
|
// In case the nucleus or the electron are part of an atom, the screening of the target field
|
|
// by the other electrons of the atom is described by a simple form factor, function of q2:
|
|
// . nuclear: N.F. Mott, H.S.W. Massey, The Theory of Atomic Collisions, 1934.
|
|
// . triplet: J.A. Wheeler and W.E. Lamb, Phys. Rev. 55 (1939) 858.
|
|
//
|
|
// The nuclear form factor that affects the probability of very large-q2 events, is not considered.
|
|
//
|
|
// In principle the code is valid from threshold, that is from 2 * m_e c^2 for nuclear and from
|
|
// 4 * m_e c^2 for triplet, up to infinity, while in pratice the divergence of the differential
|
|
// cross section at small q2 and, at high-energy, at small polar angle, make it break down at
|
|
// some point that depends on machine precision.
|
|
//
|
|
// Very-high-energy (above a few tens of TeV) LPM suppression effects in the normalized differential
|
|
// cross-section are not considered.
|
|
//
|
|
// The 5D differential cross section is sampled without any high-energy nor small
|
|
// angle approximation(s).
|
|
// The generation is strictly energy-momentum conserving when all particles in the final state
|
|
// are taken into account, that is, including the recoiling target.
|
|
// (In contrast with the BH expressions taken at face values, for which the electron energy is
|
|
// taken to be EMinus = GammaEnergy - EPlus)
|
|
//
|
|
// Tests include the examination of 1D distributions: see TestEm15
|
|
//
|
|
// Total cross sections are not computed (we inherit from other classes).
|
|
// We just convert a photon on a target when asked to do so.
|
|
//
|
|
// Pure nuclear, pure triplet and 1/Z triplet/nuclear mixture can be generated.
|
|
//
|
|
// -------------------------------------------------------------------
|
|
|
|
#include "G4BetheHeitler5DModel.hh"
|
|
#include "G4EmParameters.hh"
|
|
|
|
#include "G4PhysicalConstants.hh"
|
|
#include "G4SystemOfUnits.hh"
|
|
#include "G4Electron.hh"
|
|
#include "G4Positron.hh"
|
|
#include "G4Gamma.hh"
|
|
#include "G4MuonPlus.hh"
|
|
#include "G4MuonMinus.hh"
|
|
#include "G4IonTable.hh"
|
|
#include "G4NucleiProperties.hh"
|
|
|
|
#include "Randomize.hh"
|
|
#include "G4ParticleChangeForGamma.hh"
|
|
#include "G4Pow.hh"
|
|
#include "G4Log.hh"
|
|
#include "G4Exp.hh"
|
|
|
|
#include "G4LorentzVector.hh"
|
|
#include "G4ThreeVector.hh"
|
|
#include "G4RotationMatrix.hh"
|
|
|
|
#include <cassert>
|
|
|
|
// // Q : Use enum G4EmProcessSubType hire ?
|
|
// enum G45DConversionMode
|
|
// {
|
|
// kEPair, kMuPair
|
|
// };
|
|
|
|
const G4int kEPair = 0;
|
|
const G4int kMuPair = 1;
|
|
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
G4BetheHeitler5DModel::G4BetheHeitler5DModel(const G4ParticleDefinition* pd,
|
|
const G4String& nam)
|
|
: G4PairProductionRelModel(pd, nam),fVerbose(1),fConversionType(0),
|
|
iraw(false),
|
|
fLepton1(G4Electron::Definition()),fLepton2(G4Positron::Definition()),
|
|
fConvMode(kEPair),
|
|
fTheMuPlus(G4MuonPlus::Definition()),fTheMuMinus(G4MuonMinus::Definition())
|
|
{
|
|
theIonTable = G4IonTable::GetIonTable();
|
|
//Q: Do we need this on Model
|
|
SetLowEnergyLimit(2*fTheElectron->GetPDGMass());
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
G4BetheHeitler5DModel::~G4BetheHeitler5DModel()
|
|
{}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
void G4BetheHeitler5DModel::Initialise(const G4ParticleDefinition* part,
|
|
const G4DataVector& vec)
|
|
{
|
|
G4PairProductionRelModel::Initialise(part, vec);
|
|
|
|
G4EmParameters* theManager = G4EmParameters::Instance();
|
|
// place to initialise model parameters
|
|
// Verbosity levels: ( Can redefine as needed, but some consideration )
|
|
// 0 = nothing
|
|
// > 2 print results
|
|
// > 3 print rejection warning from transformation (fix bug from gammaray .. )
|
|
// > 4 print photon direction & polarisation
|
|
fVerbose = theManager->Verbose();
|
|
fConversionType = theManager->GetConversionType();
|
|
//////////////////////////////////////////////////////////////
|
|
// iraw :
|
|
// true : isolated electron or nucleus.
|
|
// false : inside atom -> screening form factor
|
|
iraw = theManager->OnIsolated();
|
|
// G4cout << "BH5DModel::Initialise verbose " << fVerbose
|
|
// << " isolated " << iraw << " ctype "<< fConversionType << G4endl;
|
|
|
|
//Q: Do we need this on Model
|
|
// The Leptons defined via SetLeptonPair(..) method
|
|
SetLowEnergyLimit(2*CLHEP::electron_mass_c2);
|
|
|
|
if (fConvMode == kEPair) {
|
|
assert(fLepton1->GetPDGEncoding() == fTheElectron->GetPDGEncoding()) ;
|
|
if (fVerbose > 3)
|
|
G4cout << "BH5DModel::Initialise conversion to e+ e-" << G4endl;
|
|
}
|
|
|
|
if (fConvMode == kMuPair) {
|
|
assert(fLepton1->GetPDGEncoding() == fTheMuMinus->GetPDGEncoding()) ;
|
|
if (fVerbose > 3)
|
|
G4cout << "BH5DModel::Initialise conversion to mu+ mu-" << G4endl;
|
|
}
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
void G4BetheHeitler5DModel::SetLeptonPair(const G4ParticleDefinition* p1,
|
|
const G4ParticleDefinition* p2)
|
|
{
|
|
// Lepton1 - nagative charged particle
|
|
if ( p1->GetPDGEncoding() < 0 ){
|
|
if ( p1->GetPDGEncoding() ==
|
|
G4Positron::Definition()->GetPDGEncoding() ) {
|
|
SetConversionMode(kEPair);
|
|
fLepton1 = p2;
|
|
fLepton2 = p1;
|
|
// if (fVerbose)
|
|
G4cout << "G4BetheHeitler5DModel::SetLeptonPair conversion to e+ e-"
|
|
<< G4endl;
|
|
} else if ( p1->GetPDGEncoding() ==
|
|
G4MuonPlus::Definition()->GetPDGEncoding() ) {
|
|
SetConversionMode(kMuPair);
|
|
fLepton1 = p2;
|
|
fLepton2 = p1;
|
|
// if (fVerbose)
|
|
G4cout << "G4BetheHeitler5DModel::SetLeptonPair conversion to mu+ mu-"
|
|
<< G4endl;
|
|
} else {
|
|
// Exception
|
|
G4ExceptionDescription ed;
|
|
ed << "Model not applicable to particle(s) "
|
|
<< p1->GetParticleName() << ", "
|
|
<< p2->GetParticleName();
|
|
G4Exception("G4BetheHeitler5DModel::SetLeptonPair","em0002",
|
|
FatalException, ed);
|
|
}
|
|
} else {
|
|
if ( p1->GetPDGEncoding() ==
|
|
G4Electron::Definition()->GetPDGEncoding() ) {
|
|
SetConversionMode(kEPair);
|
|
fLepton1 = p1;
|
|
fLepton2 = p2;
|
|
// if (fVerbose)
|
|
G4cout << "G4BetheHeitler5DModel::SetLeptonPair conversion to e+ e-"
|
|
<< G4endl;
|
|
} else if ( p1->GetPDGEncoding() ==
|
|
G4MuonMinus::Definition()->GetPDGEncoding() ) {
|
|
SetConversionMode(kMuPair);
|
|
fLepton1 = p1;
|
|
fLepton2 = p2;
|
|
// if (fVerbose)
|
|
G4cout << "G4BetheHeitler5DModel::SetLeptonPair conversion to mu+ mu-"
|
|
<< G4endl;
|
|
} else {
|
|
// Exception
|
|
G4ExceptionDescription ed;
|
|
ed << "Model not applicable to particle(s) "
|
|
<< p1->GetParticleName() << ", "
|
|
<< p2->GetParticleName();
|
|
G4Exception("G4BetheHeitler5DModel::SetLeptonPair","em0002",
|
|
FatalException, ed);
|
|
}
|
|
}
|
|
if ( fLepton1->GetPDGEncoding() != fLepton2->GetAntiPDGEncoding() ) {
|
|
G4Exception("G4BetheHeitler5DModel::SetLeptonPair","em0007",
|
|
FatalErrorInArgument, "pair must be particle, antiparticle ");
|
|
G4cerr << "BH5DModel::SetLeptonPair BAD paricle/anti particle pair"
|
|
<< fLepton1->GetParticleName() << ", "
|
|
<< fLepton2->GetParticleName() << G4endl;
|
|
}
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
G4double G4BetheHeitler5DModel::MaxDiffCrossSection(const G4double* par,
|
|
G4double Z,
|
|
G4double e,
|
|
G4double loge) const
|
|
{
|
|
const G4double Q = e/par[9];
|
|
return par[0] * G4Exp((par[2]+loge*par[4])*loge)
|
|
/ (par[1]+ G4Exp(par[3]*loge)+G4Exp(par[5]*loge))
|
|
* (1+par[7]*G4Exp(par[8]*G4Log(Z))*Q/(1+Q));
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|
|
|
|
void
|
|
G4BetheHeitler5DModel::SampleSecondaries(std::vector<G4DynamicParticle*>* fvect,
|
|
const G4MaterialCutsCouple* couple,
|
|
const G4DynamicParticle* aDynamicGamma,
|
|
G4double, G4double)
|
|
{
|
|
// MeV
|
|
static const G4double ElectronMass = CLHEP::electron_mass_c2;
|
|
|
|
const G4double LeptonMass = fLepton1->GetPDGMass();
|
|
const G4double LeptonMass2 = LeptonMass*LeptonMass;
|
|
|
|
static const G4double alpha0 = CLHEP::fine_structure_const;
|
|
// mm
|
|
static const G4double r0 = CLHEP::classic_electr_radius;
|
|
// mbarn
|
|
static const G4double r02 = r0*r0*1.e+25;
|
|
static const G4double twoPi = CLHEP::twopi;
|
|
static const G4double factor = alpha0 * r02 / (twoPi*twoPi);
|
|
// static const G4double factor1 = pow((6.0 * pi),(1.0/3.0))/(8.*alpha0*ElectronMass);
|
|
static const G4double factor1 = 2.66134007899/(8.*alpha0*ElectronMass);
|
|
//
|
|
G4double PairInvMassMin = 2.*LeptonMass;
|
|
G4double TrThreshold = 2.0 * ( (LeptonMass2)/ElectronMass + LeptonMass);
|
|
|
|
//
|
|
static const G4double nu[2][10] = {
|
|
//electron
|
|
{ 0.0227436, 0.0582046, 3.0322675, 2.8275065, -0.0034004,
|
|
1.1212766, 1.8989468, 68.3492750, 0.0211186, 14.4},
|
|
//muon
|
|
{0.67810E-06, 0.86037E+05, 2.0008395, 1.6739719, -0.0057279,
|
|
1.4222, 0.0, 263230.0, 0.0521, 51.1338}
|
|
};
|
|
static const G4double tr[2][10] = {
|
|
//electron
|
|
{ 0.0332350, 4.3942537, 2.8515925, 2.6351695, -0.0031510,
|
|
1.5737305, 1.8104647, 20.6434021, -0.0272586, 28.9},
|
|
//muon
|
|
{0.10382E-03, 0.14408E+17, 4.1368679, 3.2662121, -0.0163091,
|
|
0.0000, 0.0, 0.0, 0.0000, 1.0000}
|
|
};
|
|
//
|
|
static const G4double para[2][3][2] = {
|
|
//electron
|
|
{ {11., -16.},{-1.17, -2.95},{-2., -0.5} },
|
|
//muon
|
|
{ {17.5, 1.},{-1.17, -2.95},{2., 6.} }
|
|
};
|
|
//
|
|
static const G4double correctionIndex = 1.4;
|
|
//
|
|
const G4double GammaEnergy = aDynamicGamma->GetKineticEnergy();
|
|
// Protection, Will not be true tot cross section = 0
|
|
if ( GammaEnergy <= PairInvMassMin) { return; }
|
|
|
|
const G4double GammaEnergy2 = GammaEnergy*GammaEnergy;
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
const G4ParticleMomentum GammaDirection =
|
|
aDynamicGamma->GetMomentumDirection();
|
|
G4ThreeVector GammaPolarization = aDynamicGamma->GetPolarization();
|
|
|
|
// The protection polarization perpendicular to the direction vector,
|
|
// as it done in G4LivermorePolarizedGammaConversionModel,
|
|
// assuming Direction is unitary vector
|
|
// (projection to plane) p_proj = p - (p o d)/(d o d) x d
|
|
if ( GammaPolarization.howOrthogonal(GammaDirection) != 0) {
|
|
GammaPolarization -= GammaPolarization.dot(GammaDirection) * GammaDirection;
|
|
}
|
|
// End of Protection
|
|
//
|
|
const G4double GammaPolarizationMag = GammaPolarization.mag();
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// target element
|
|
// select randomly one element constituting the material
|
|
const G4Element* anElement = SelectTargetAtom(couple, fTheGamma, GammaEnergy,
|
|
aDynamicGamma->GetLogKineticEnergy() );
|
|
// Atomic number
|
|
const G4int Z = anElement->GetZasInt();
|
|
const G4int A = SelectIsotopeNumber(anElement);
|
|
const G4double iZ13 = 1./anElement->GetIonisation()->GetZ3();
|
|
const G4double targetMass = G4NucleiProperties::GetNuclearMass(A, Z);
|
|
|
|
const G4double NuThreshold = 2.0 * ( (LeptonMass2)/targetMass + LeptonMass);
|
|
// No conversion possible below nuclear threshold
|
|
if ( GammaEnergy <= NuThreshold) { return; }
|
|
|
|
CLHEP::HepRandomEngine* rndmEngine = G4Random::getTheEngine();
|
|
|
|
// itriplet : true -- triplet, false -- nuclear.
|
|
G4bool itriplet = false;
|
|
if (fConversionType == 1) {
|
|
itriplet = false;
|
|
} else if (fConversionType == 2) {
|
|
itriplet = true;
|
|
if ( GammaEnergy <= TrThreshold ) return;
|
|
} else if ( GammaEnergy > TrThreshold ) {
|
|
// choose triplet or nuclear from a triplet/nuclear=1/Z
|
|
// total cross section ratio.
|
|
// approximate at low energies !
|
|
if(rndmEngine->flat()*(Z+1) < 1.) {
|
|
itriplet = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
const G4double RecoilMass = itriplet ? ElectronMass : targetMass;
|
|
const G4double RecoilMass2 = RecoilMass*RecoilMass;
|
|
const G4double sCMS = 2.*RecoilMass*GammaEnergy + RecoilMass2;
|
|
const G4double sCMSPlusRM2 = sCMS + RecoilMass2;
|
|
const G4double sqrts = std::sqrt(sCMS);
|
|
const G4double isqrts2 = 1./(2.*sqrts);
|
|
//
|
|
const G4double PairInvMassMax = sqrts-RecoilMass;
|
|
const G4double PairInvMassRange = PairInvMassMax/PairInvMassMin;
|
|
const G4double lnPairInvMassRange = G4Log(PairInvMassRange);
|
|
|
|
// initial state. Defines z axis of "0" frame as along photon propagation.
|
|
// Since CMS(0., 0., GammaEnergy, GammaEnergy+RecoilMass) set some constants
|
|
const G4double betaCMS = G4LorentzVector(0.0,0.0,GammaEnergy,GammaEnergy+RecoilMass).beta();
|
|
|
|
// maximum value of pdf
|
|
const G4double EffectiveZ = iraw ? 0.5 : Z;
|
|
const G4double Threshold = itriplet ? TrThreshold : NuThreshold;
|
|
const G4double AvailableEnergy = GammaEnergy - Threshold;
|
|
const G4double LogAvailableEnergy = G4Log(AvailableEnergy);
|
|
//
|
|
const G4double MaxDiffCross = itriplet
|
|
? MaxDiffCrossSection(tr[fConvMode],
|
|
EffectiveZ, AvailableEnergy, LogAvailableEnergy)
|
|
: MaxDiffCrossSection(nu[fConvMode],
|
|
EffectiveZ, AvailableEnergy, LogAvailableEnergy);
|
|
//
|
|
// 50% safety marging factor
|
|
const G4double ymax = 1.5 * MaxDiffCross;
|
|
// x1 bounds
|
|
const G4double xu1 = (LogAvailableEnergy > para[fConvMode][2][0])
|
|
? para[fConvMode][0][0] +
|
|
para[fConvMode][1][0]*LogAvailableEnergy
|
|
: para[fConvMode][0][0] +
|
|
para[fConvMode][2][0]*para[fConvMode][1][0];
|
|
const G4double xl1 = (LogAvailableEnergy > para[fConvMode][2][1])
|
|
? para[fConvMode][0][1] +
|
|
para[fConvMode][1][1]*LogAvailableEnergy
|
|
: para[fConvMode][0][1] +
|
|
para[fConvMode][2][1]*para[fConvMode][1][1];
|
|
//
|
|
G4LorentzVector Recoil;
|
|
G4LorentzVector LeptonPlus;
|
|
G4LorentzVector LeptonMinus;
|
|
G4double pdf = 0.;
|
|
|
|
G4double rndmv6[6];
|
|
// START Sampling
|
|
do {
|
|
|
|
rndmEngine->flatArray(6, rndmv6);
|
|
|
|
//////////////////////////////////////////////////
|
|
// pdf pow(x,c) with c = 1.4
|
|
// integral y = pow(x,(c+1))/(c+1) @ x = 1 => y = 1 /(1+c)
|
|
// invCdf exp( log(y /* *( c + 1.0 )/ (c + 1.0 ) */ ) /( c + 1.0) )
|
|
//////////////////////////////////////////////////
|
|
const G4double X1 =
|
|
G4Exp(G4Log(rndmv6[0])/(correctionIndex + 1.0));
|
|
|
|
const G4double x0 = G4Exp(xl1 + (xu1 - xl1)*rndmv6[1]);
|
|
const G4double dum0 = 1./(1.+x0);
|
|
const G4double cosTheta = (x0-1.)*dum0;
|
|
const G4double sinTheta = std::sqrt(4.*x0)*dum0;
|
|
|
|
const G4double PairInvMass = PairInvMassMin*G4Exp(X1*X1*lnPairInvMassRange);
|
|
|
|
// G4double rndmv3[3];
|
|
// rndmEngine->flatArray(3, rndmv3);
|
|
|
|
// cos and sin theta-lepton
|
|
const G4double cosThetaLept = std::cos(pi*rndmv6[2]);
|
|
// sin(ThetaLept) is always in [0,+1] if ThetaLept is in [0,pi]
|
|
const G4double sinThetaLept = std::sqrt((1.-cosThetaLept)*(1.+cosThetaLept));
|
|
// cos and sin phi-lepton
|
|
const G4double cosPhiLept = std::cos(twoPi*rndmv6[3]-pi);
|
|
// sin(PhiLept) is in [-1,0] if PhiLept in [-pi,0) and
|
|
// is in [0,+1] if PhiLept in [0,+pi]
|
|
const G4double sinPhiLept = std::copysign(std::sqrt((1.-cosPhiLept)*(1.+cosPhiLept)),rndmv6[3]-0.5);
|
|
// cos and sin phi
|
|
const G4double cosPhi = std::cos(twoPi*rndmv6[4]-pi);
|
|
const G4double sinPhi = std::copysign(std::sqrt((1.-cosPhi)*(1.+cosPhi)),rndmv6[4]-0.5);
|
|
|
|
//////////////////////////////////////////////////
|
|
// frames:
|
|
// 3 : the laboratory Lorentz frame, Geant4 axes definition
|
|
// 0 : the laboratory Lorentz frame, axes along photon direction and polarisation
|
|
// 1 : the center-of-mass Lorentz frame
|
|
// 2 : the pair Lorentz frame
|
|
//////////////////////////////////////////////////
|
|
|
|
// in the center-of-mass frame
|
|
|
|
const G4double RecEnergyCMS = (sCMSPlusRM2-PairInvMass*PairInvMass)*isqrts2;
|
|
const G4double LeptonEnergy2 = PairInvMass*0.5;
|
|
|
|
// New way of calucaltion thePRecoil to avoid underflow
|
|
G4double abp = std::max((2.0*GammaEnergy*RecoilMass -
|
|
PairInvMass*PairInvMass + 2.0*PairInvMass*RecoilMass)*
|
|
(2.0*GammaEnergy*RecoilMass -
|
|
PairInvMass*PairInvMass - 2.0*PairInvMass*RecoilMass),0.0);
|
|
|
|
G4double thePRecoil = std::sqrt(abp) * isqrts2;
|
|
|
|
// back to the center-of-mass frame
|
|
Recoil.set( thePRecoil*sinTheta*cosPhi,
|
|
thePRecoil*sinTheta*sinPhi,
|
|
thePRecoil*cosTheta,
|
|
RecEnergyCMS);
|
|
|
|
// in the pair frame
|
|
const G4double thePLepton = std::sqrt( (LeptonEnergy2-LeptonMass)
|
|
*(LeptonEnergy2+LeptonMass));
|
|
|
|
LeptonPlus.set(thePLepton*sinThetaLept*cosPhiLept,
|
|
thePLepton*sinThetaLept*sinPhiLept,
|
|
thePLepton*cosThetaLept,
|
|
LeptonEnergy2);
|
|
|
|
LeptonMinus.set(-LeptonPlus.x(),
|
|
-LeptonPlus.y(),
|
|
-LeptonPlus.z(),
|
|
LeptonEnergy2);
|
|
|
|
|
|
// Normalisation of final state phase space:
|
|
// Section 47 of Particle Data Group, Chin. Phys. C, 40, 100001 (2016)
|
|
// const G4double Norme = Recoil1.vect().mag() * LeptonPlus2.vect().mag();
|
|
const G4double Norme = Recoil.vect().mag() * LeptonPlus.vect().mag();
|
|
|
|
// e+, e- to CMS frame from pair frame
|
|
|
|
// boost vector from Pair to CMS
|
|
const G4ThreeVector pair2cms =
|
|
G4LorentzVector( -Recoil.x(), -Recoil.y(), -Recoil.z(),
|
|
sqrts-RecEnergyCMS).boostVector();
|
|
|
|
LeptonPlus.boost(pair2cms);
|
|
LeptonMinus.boost(pair2cms);
|
|
|
|
// back to the laboratory frame (make use of the CMS(0,0,Eg,Eg+RM)) form
|
|
|
|
Recoil.boostZ(betaCMS);
|
|
LeptonPlus.boostZ(betaCMS);
|
|
LeptonMinus.boostZ(betaCMS);
|
|
|
|
// Jacobian factors
|
|
const G4double Jacob0 = x0*dum0*dum0;
|
|
const G4double Jacob1 = 2.*X1*lnPairInvMassRange*PairInvMass;
|
|
const G4double Jacob2 = std::abs(sinThetaLept);
|
|
|
|
const G4double EPlus = LeptonPlus.t();
|
|
const G4double PPlus = LeptonPlus.vect().mag();
|
|
const G4double sinThetaPlus = LeptonPlus.vect().perp()/PPlus;
|
|
const G4double cosThetaPlus = LeptonPlus.vect().cosTheta();
|
|
|
|
const G4double pPX = LeptonPlus.x();
|
|
const G4double pPY = LeptonPlus.y();
|
|
const G4double dum1 = 1./std::sqrt( pPX*pPX + pPY*pPY );
|
|
const G4double cosPhiPlus = pPX*dum1;
|
|
const G4double sinPhiPlus = pPY*dum1;
|
|
|
|
// denominators:
|
|
// the two cancelling leading terms for forward emission at high energy, removed
|
|
const G4double elMassCTP = LeptonMass*cosThetaPlus;
|
|
const G4double ePlusSTP = EPlus*sinThetaPlus;
|
|
const G4double DPlus = (elMassCTP*elMassCTP + ePlusSTP*ePlusSTP)
|
|
/(EPlus + PPlus*cosThetaPlus);
|
|
|
|
const G4double EMinus = LeptonMinus.t();
|
|
const G4double PMinus = LeptonMinus.vect().mag();
|
|
const G4double sinThetaMinus = LeptonMinus.vect().perp()/PMinus;
|
|
const G4double cosThetaMinus = LeptonMinus.vect().cosTheta();
|
|
|
|
const G4double ePX = LeptonMinus.x();
|
|
const G4double ePY = LeptonMinus.y();
|
|
const G4double dum2 = 1./std::sqrt( ePX*ePX + ePY*ePY );
|
|
const G4double cosPhiMinus = ePX*dum2;
|
|
const G4double sinPhiMinus = ePY*dum2;
|
|
|
|
const G4double elMassCTM = LeptonMass*cosThetaMinus;
|
|
const G4double eMinSTM = EMinus*sinThetaMinus;
|
|
const G4double DMinus = (elMassCTM*elMassCTM + eMinSTM*eMinSTM)
|
|
/(EMinus + PMinus*cosThetaMinus);
|
|
|
|
// cos(phiMinus-PhiPlus)
|
|
const G4double cosdPhi = cosPhiPlus*cosPhiMinus + sinPhiPlus*sinPhiMinus;
|
|
const G4double PRec = Recoil.vect().mag();
|
|
const G4double q2 = PRec*PRec;
|
|
|
|
const G4double BigPhi = -LeptonMass2 / (GammaEnergy*GammaEnergy2 * q2*q2);
|
|
|
|
G4double FormFactor = 1.;
|
|
if (!iraw) {
|
|
if (itriplet) {
|
|
const G4double qun = factor1*iZ13*iZ13;
|
|
const G4double nun = qun * PRec;
|
|
if (nun < 1.) {
|
|
FormFactor = (nun < 0.01) ? (13.8-55.4*std::sqrt(nun))*nun
|
|
: std::sqrt(1-(nun-1)*(nun-1));
|
|
} // else FormFactor = 1 by default
|
|
} else {
|
|
const G4double dum3 = 217.*PRec*iZ13;
|
|
const G4double AFF = 1./(1. + dum3*dum3);
|
|
FormFactor = (1.-AFF)*(1-AFF);
|
|
}
|
|
} // else FormFactor = 1 by default
|
|
|
|
G4double betheheitler;
|
|
if (GammaPolarizationMag==0.) {
|
|
const G4double pPlusSTP = PPlus*sinThetaPlus;
|
|
const G4double pMinusSTM = PMinus*sinThetaMinus;
|
|
const G4double pPlusSTPperDP = pPlusSTP/DPlus;
|
|
const G4double pMinusSTMperDM = pMinusSTM/DMinus;
|
|
const G4double dunpol = BigPhi*(
|
|
pPlusSTPperDP *pPlusSTPperDP *(4.*EMinus*EMinus-q2)
|
|
+ pMinusSTMperDM*pMinusSTMperDM*(4.*EPlus*EPlus - q2)
|
|
+ 2.*pPlusSTPperDP*pMinusSTMperDM*cosdPhi
|
|
*(4.*EPlus*EMinus + q2 - 2.*GammaEnergy2)
|
|
- 2.*GammaEnergy2*(pPlusSTP*pPlusSTP+pMinusSTM*pMinusSTM)/(DMinus*DPlus));
|
|
betheheitler = dunpol * factor;
|
|
} else {
|
|
const G4double pPlusSTP = PPlus*sinThetaPlus;
|
|
const G4double pMinusSTM = PMinus*sinThetaMinus;
|
|
const G4double pPlusSTPCPPperDP = pPlusSTP*cosPhiPlus/DPlus;
|
|
const G4double pMinusSTMCPMperDM = pMinusSTM*cosPhiMinus/DMinus;
|
|
const G4double caa = 2.*(EPlus*pMinusSTMCPMperDM+EMinus*pPlusSTPCPPperDP);
|
|
const G4double cbb = pMinusSTMCPMperDM-pPlusSTPCPPperDP;
|
|
const G4double ccc = (pPlusSTP*pPlusSTP + pMinusSTM*pMinusSTM
|
|
+2.*pPlusSTP*pMinusSTM*cosdPhi)/ (DMinus*DPlus);
|
|
const G4double dtot= 2.*BigPhi*( caa*caa - q2*cbb*cbb - GammaEnergy2*ccc);
|
|
betheheitler = dtot * factor;
|
|
}
|
|
//
|
|
const G4double cross = Norme * Jacob0 * Jacob1 * Jacob2 * betheheitler
|
|
* FormFactor * RecoilMass / sqrts;
|
|
pdf = cross * (xu1 - xl1) / G4Exp(correctionIndex*G4Log(X1)); // cond1;
|
|
} while ( pdf < ymax * rndmv6[5] );
|
|
// END of Sampling
|
|
|
|
if ( fVerbose > 2 ) {
|
|
G4double recul = std::sqrt(Recoil.x()*Recoil.x()+Recoil.y()*Recoil.y()
|
|
+Recoil.z()*Recoil.z());
|
|
G4cout << "BetheHeitler5DModel GammaEnergy= " << GammaEnergy
|
|
<< " PDF= " << pdf << " ymax= " << ymax
|
|
<< " recul= " << recul << G4endl;
|
|
}
|
|
|
|
// back to Geant4 system
|
|
|
|
if ( fVerbose > 4 ) {
|
|
G4cout << "BetheHeitler5DModel GammaDirection " << GammaDirection << G4endl;
|
|
G4cout << "BetheHeitler5DModel GammaPolarization " << GammaPolarization << G4endl;
|
|
G4cout << "BetheHeitler5DModel GammaEnergy " << GammaEnergy << G4endl;
|
|
G4cout << "BetheHeitler5DModel Conv "
|
|
<< (itriplet ? "triplet" : "nucl") << G4endl;
|
|
}
|
|
|
|
if (GammaPolarizationMag == 0.0) {
|
|
// set polarization axis orthohonal to direction
|
|
GammaPolarization = GammaDirection.orthogonal().unit();
|
|
} else {
|
|
// GammaPolarization not a unit vector
|
|
GammaPolarization /= GammaPolarizationMag;
|
|
}
|
|
|
|
// The unit norm vector that is orthogonal to the two others
|
|
G4ThreeVector yGrec = GammaDirection.cross(GammaPolarization);
|
|
|
|
// rotation from gamma ref. sys. to World
|
|
G4RotationMatrix GtoW(GammaPolarization,yGrec,GammaDirection);
|
|
|
|
Recoil.transform(GtoW);
|
|
LeptonPlus.transform(GtoW);
|
|
LeptonMinus.transform(GtoW);
|
|
|
|
if ( fVerbose > 2 ) {
|
|
G4cout << "BetheHeitler5DModel Recoil " << Recoil.x() << " " << Recoil.y() << " " << Recoil.z()
|
|
<< " " << Recoil.t() << " " << G4endl;
|
|
G4cout << "BetheHeitler5DModel LeptonPlus " << LeptonPlus.x() << " " << LeptonPlus.y() << " "
|
|
<< LeptonPlus.z() << " " << LeptonPlus.t() << " " << G4endl;
|
|
G4cout << "BetheHeitler5DModel LeptonMinus " << LeptonMinus.x() << " " << LeptonMinus.y() << " "
|
|
<< LeptonMinus.z() << " " << LeptonMinus.t() << " " << G4endl;
|
|
}
|
|
|
|
// Create secondaries
|
|
G4DynamicParticle* aParticle1 = new G4DynamicParticle(fLepton1,LeptonMinus);
|
|
G4DynamicParticle* aParticle2 = new G4DynamicParticle(fLepton2,LeptonPlus);
|
|
|
|
// create G4DynamicParticle object for the particle3 ( recoil )
|
|
G4ParticleDefinition* RecoilPart;
|
|
if (itriplet) {
|
|
// triplet
|
|
RecoilPart = fTheElectron;
|
|
} else{
|
|
RecoilPart = theIonTable->GetIon(Z, A, 0);
|
|
}
|
|
G4DynamicParticle* aParticle3 = new G4DynamicParticle(RecoilPart,Recoil);
|
|
|
|
// Fill output vector
|
|
fvect->push_back(aParticle1);
|
|
fvect->push_back(aParticle2);
|
|
fvect->push_back(aParticle3);
|
|
|
|
// kill incident photon
|
|
fParticleChange->SetProposedKineticEnergy(0.);
|
|
fParticleChange->ProposeTrackStatus(fStopAndKill);
|
|
}
|
|
|
|
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
|