|
|
|
@@ -42,6 +42,13 @@
|
|
|
|
|
#include "G4IonTable.hh"
|
|
|
|
|
#include "Randomize.hh"
|
|
|
|
|
#include "G4NucleiProperties.hh"
|
|
|
|
|
#include "G4DecayTable.hh"
|
|
|
|
|
#include "G4VDecayChannel.hh"
|
|
|
|
|
#include "G4DecayProducts.hh"
|
|
|
|
|
#include "G4NistManager.hh"
|
|
|
|
|
#include "G4Fragment.hh"
|
|
|
|
|
#include "G4ExcitationHandler.hh"
|
|
|
|
|
#include "G4ReactionProductVector.hh"
|
|
|
|
|
|
|
|
|
|
#include "G4Exp.hh"
|
|
|
|
|
#include "G4Log.hh"
|
|
|
|
@@ -50,13 +57,28 @@
|
|
|
|
|
#include "G4HadronicParameters.hh"
|
|
|
|
|
#include "G4PhysicsModelCatalog.hh"
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
constexpr G4int maxN = 1000;
|
|
|
|
|
constexpr G4double emin = 2*136.9*CLHEP::MeV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
G4ChargeExchange::G4ChargeExchange(G4ChargeExchangeXS* ptr)
|
|
|
|
|
: G4HadronicInteraction("ChargeExchange"),
|
|
|
|
|
fXSection(ptr)
|
|
|
|
|
fXSection(ptr), fXSWeightFactor(1.0)
|
|
|
|
|
{
|
|
|
|
|
lowEnergyLimit = 1.*CLHEP::MeV;
|
|
|
|
|
secID = G4PhysicsModelCatalog::GetModelID( "model_ChargeExchange" );
|
|
|
|
|
nist = G4NistManager::Instance();
|
|
|
|
|
fHandler = new G4ExcitationHandler();
|
|
|
|
|
if (nullptr != fXSection) {
|
|
|
|
|
fXSWeightFactor = 1.0/fXSection->GetCrossSectionFactor();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
G4ChargeExchange::~G4ChargeExchange()
|
|
|
|
|
{
|
|
|
|
|
delete fHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
G4HadFinalState* G4ChargeExchange::ApplyYourself(
|
|
|
|
@@ -64,7 +86,6 @@ G4HadFinalState* G4ChargeExchange::ApplyYourself(
|
|
|
|
|
{
|
|
|
|
|
theParticleChange.Clear();
|
|
|
|
|
auto part = aTrack.GetDefinition();
|
|
|
|
|
G4int pdg = part->GetPDGEncoding();
|
|
|
|
|
G4double ekin = aTrack.GetKineticEnergy();
|
|
|
|
|
|
|
|
|
|
G4int A = targetNucleus.GetA_asInt();
|
|
|
|
@@ -73,8 +94,14 @@ G4HadFinalState* G4ChargeExchange::ApplyYourself(
|
|
|
|
|
if (ekin <= lowEnergyLimit) {
|
|
|
|
|
return &theParticleChange;
|
|
|
|
|
}
|
|
|
|
|
theParticleChange.SetWeightChange(fXSWeightFactor);
|
|
|
|
|
|
|
|
|
|
G4int projPDG = part->GetPDGEncoding();
|
|
|
|
|
|
|
|
|
|
// for hydrogen targets and positive projectile change exchange
|
|
|
|
|
// is not possible on proton, only on deuteron
|
|
|
|
|
if (1 == Z && (211 == projPDG || 321 == projPDG)) { A = 2; }
|
|
|
|
|
|
|
|
|
|
if (verboseLevel > 1)
|
|
|
|
|
G4cout << "G4ChargeExchange for " << part->GetParticleName()
|
|
|
|
|
<< " PDGcode= " << projPDG << " on nucleus Z= " << Z
|
|
|
|
@@ -86,16 +113,19 @@ G4HadFinalState* G4ChargeExchange::ApplyYourself(
|
|
|
|
|
G4double etot = mass1 + lv0.e();
|
|
|
|
|
|
|
|
|
|
// select final state
|
|
|
|
|
const G4ParticleDefinition* theRecoil = nullptr;
|
|
|
|
|
const G4ParticleDefinition* theSecondary =
|
|
|
|
|
fXSection->SampleSecondaryType(part, Z, A);
|
|
|
|
|
G4int pdg = theSecondary->GetPDGEncoding();
|
|
|
|
|
|
|
|
|
|
// omega(782) and f2(1270)
|
|
|
|
|
G4bool isShortLived = (pdg == 223 || pdg == 225);
|
|
|
|
|
|
|
|
|
|
// atomic number of the recoil nucleus
|
|
|
|
|
if (pdg == -211) { --Z; }
|
|
|
|
|
else if (pdg == 211) { ++Z; }
|
|
|
|
|
else if (pdg == -321) { --Z; }
|
|
|
|
|
else if (pdg == 321) { ++Z; }
|
|
|
|
|
else if (pdg == 130) {
|
|
|
|
|
if (projPDG == -211) { --Z; }
|
|
|
|
|
else if (projPDG == 211) { ++Z; }
|
|
|
|
|
else if (projPDG == -321) { --Z; }
|
|
|
|
|
else if (projPDG == 321) { ++Z; }
|
|
|
|
|
else if (projPDG == 130) {
|
|
|
|
|
if (theSecondary->GetPDGCharge() > 0.0) { --Z; }
|
|
|
|
|
else { ++Z; }
|
|
|
|
|
} else {
|
|
|
|
@@ -103,23 +133,32 @@ G4HadFinalState* G4ChargeExchange::ApplyYourself(
|
|
|
|
|
return &theParticleChange;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Z == 0 && A == 1) theRecoil = G4Neutron::Neutron();
|
|
|
|
|
else if (Z == 1 && A == 1) theRecoil = G4Proton::Proton();
|
|
|
|
|
else if (Z == 1 && A == 2) theRecoil = G4Deuteron::Deuteron();
|
|
|
|
|
else if (Z == 1 && A == 3) theRecoil = G4Triton::Triton();
|
|
|
|
|
else if (Z == 2 && A == 3) theRecoil = G4He3::He3();
|
|
|
|
|
else if (Z == 2 && A == 4) theRecoil = G4Alpha::Alpha();
|
|
|
|
|
else {
|
|
|
|
|
// recoil nucleus
|
|
|
|
|
const G4ParticleDefinition* theRecoil = nullptr;
|
|
|
|
|
if (Z == 0 && A == 1) { theRecoil = G4Neutron::Neutron(); }
|
|
|
|
|
else if (Z == 1 && A == 1) { theRecoil = G4Proton::Proton(); }
|
|
|
|
|
else if (Z == 1 && A == 2) { theRecoil = G4Deuteron::Deuteron(); }
|
|
|
|
|
else if (Z == 1 && A == 3) { theRecoil = G4Triton::Triton(); }
|
|
|
|
|
else if (Z == 2 && A == 3) { theRecoil = G4He3::He3(); }
|
|
|
|
|
else if (Z == 2 && A == 4) { theRecoil = G4Alpha::Alpha(); }
|
|
|
|
|
else if (nist->GetIsotopeAbundance(Z, A) > 0.0) {
|
|
|
|
|
theRecoil = G4ParticleTable::GetParticleTable()
|
|
|
|
|
->GetIonTable()->GetIon(Z, A, 0.0);
|
|
|
|
|
}
|
|
|
|
|
if (nullptr == theRecoil) { return &theParticleChange; }
|
|
|
|
|
|
|
|
|
|
G4double mass2 = theSecondary->GetPDGMass();
|
|
|
|
|
G4double mass3 = theRecoil->GetPDGMass();
|
|
|
|
|
// check if there is enough energy for the final state
|
|
|
|
|
// and sample mass of produced state
|
|
|
|
|
const G4double mass0 = theSecondary->GetPDGMass();
|
|
|
|
|
G4double mass3 = (nullptr == theRecoil) ?
|
|
|
|
|
G4NucleiProperties::GetNuclearMass(A, Z) : theRecoil->GetPDGMass();
|
|
|
|
|
G4double mass2 = mass0;
|
|
|
|
|
if (isShortLived &&
|
|
|
|
|
!SampleMass(mass2, theSecondary->GetPDGWidth(), etot - mass3)) {
|
|
|
|
|
return &theParticleChange;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// not possible kinematically
|
|
|
|
|
if (etot <= mass2 + mass3) {
|
|
|
|
|
// not possible kinematically
|
|
|
|
|
return &theParticleChange;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -153,7 +192,7 @@ G4HadFinalState* G4ChargeExchange::ApplyYourself(
|
|
|
|
|
momentumCMS*cost,
|
|
|
|
|
std::sqrt(momentumCMS*momentumCMS + mass2*mass2));
|
|
|
|
|
|
|
|
|
|
// kinematics in the final stae, may be a warning should be added if
|
|
|
|
|
// kinematics in the final state, may be a warning should be added if
|
|
|
|
|
lv2.boost(bst);
|
|
|
|
|
if (lv2.e() < mass2) {
|
|
|
|
|
lv2.setE(mass2);
|
|
|
|
@@ -167,16 +206,44 @@ G4HadFinalState* G4ChargeExchange::ApplyYourself(
|
|
|
|
|
theParticleChange.SetStatusChange(stopAndKill);
|
|
|
|
|
theParticleChange.SetEnergyChange(0.0);
|
|
|
|
|
|
|
|
|
|
G4DynamicParticle * aSec = new G4DynamicParticle(theSecondary, lv2);
|
|
|
|
|
theParticleChange.AddSecondary(aSec, secID);
|
|
|
|
|
if (!isShortLived) {
|
|
|
|
|
auto aSec = new G4DynamicParticle(theSecondary, lv2);
|
|
|
|
|
theParticleChange.AddSecondary(aSec, secID);
|
|
|
|
|
} else {
|
|
|
|
|
auto channel = theSecondary->GetDecayTable()->SelectADecayChannel(mass2);
|
|
|
|
|
auto products = channel->DecayIt(mass2);
|
|
|
|
|
G4ThreeVector bst1 = lv2.boostVector();
|
|
|
|
|
G4int N = products->entries();
|
|
|
|
|
for (G4int i=0; i<N; ++i) {
|
|
|
|
|
auto p = (*products)[i];
|
|
|
|
|
auto lvp = p->Get4Momentum();
|
|
|
|
|
lvp.boost(bst1);
|
|
|
|
|
p->Set4Momentum(lvp);
|
|
|
|
|
theParticleChange.AddSecondary(p, secID);
|
|
|
|
|
}
|
|
|
|
|
delete products;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
G4DynamicParticle * aRec = new G4DynamicParticle(theRecoil, lv);
|
|
|
|
|
theParticleChange.AddSecondary(aRec, secID);
|
|
|
|
|
// recoil is a stable isotope
|
|
|
|
|
if (nullptr != theRecoil) {
|
|
|
|
|
auto aRec = new G4DynamicParticle(theRecoil, lv);
|
|
|
|
|
theParticleChange.AddSecondary(aRec, secID);
|
|
|
|
|
} else {
|
|
|
|
|
// recoil is an unstable fragment
|
|
|
|
|
G4Fragment frag(A, Z, lv);
|
|
|
|
|
auto products = fHandler->BreakItUp(frag);
|
|
|
|
|
for (auto & prod : *products) {
|
|
|
|
|
auto dp = new G4DynamicParticle(prod->GetDefinition(), prod->GetMomentum());
|
|
|
|
|
theParticleChange.AddSecondary(dp, secID);
|
|
|
|
|
delete prod;
|
|
|
|
|
}
|
|
|
|
|
delete products;
|
|
|
|
|
}
|
|
|
|
|
return &theParticleChange;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
G4double G4ChargeExchange::SampleT(const G4ParticleDefinition*,
|
|
|
|
|
G4int A, G4double tmax) const
|
|
|
|
|
const G4int A, const G4double tmax) const
|
|
|
|
|
{
|
|
|
|
|
G4double aa, bb, cc, dd;
|
|
|
|
|
G4Pow* g4pow = G4Pow::GetInstance();
|
|
|
|
@@ -198,14 +265,31 @@ G4double G4ChargeExchange::SampleT(const G4ParticleDefinition*,
|
|
|
|
|
G4double y = bb;
|
|
|
|
|
if(G4UniformRand()*(x1 + x2) < x2) y = dd;
|
|
|
|
|
|
|
|
|
|
const G4int maxN = 10000;
|
|
|
|
|
G4int count = 0;
|
|
|
|
|
do {
|
|
|
|
|
for (G4int i=0; i<maxN; ++i) {
|
|
|
|
|
t = -G4Log(G4UniformRand())/y;
|
|
|
|
|
} while ( (t > tmax) && ++count < maxN );
|
|
|
|
|
/* Loop checking, 10.08.2015, A.Ribon */
|
|
|
|
|
if ( count >= maxN ) {
|
|
|
|
|
t = 0.0;
|
|
|
|
|
if (t <= tmax) { return t; }
|
|
|
|
|
}
|
|
|
|
|
return t;
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
G4bool G4ChargeExchange::SampleMass(G4double& M, const G4double G, const G4double elim)
|
|
|
|
|
{
|
|
|
|
|
// +- 4 width but above 2 pion mass
|
|
|
|
|
const G4double e1 = std::max(M - 4*G, emin);
|
|
|
|
|
const G4double e2 = std::min(M + 4*G, elim) - e1;
|
|
|
|
|
if (e2 <= 0.0) { return false; }
|
|
|
|
|
const G4double M2 = M*M;
|
|
|
|
|
const G4double MG2 = M2*G*G;
|
|
|
|
|
|
|
|
|
|
// sampling Breit-Wigner function
|
|
|
|
|
for (G4int i=0; i<maxN; ++i) {
|
|
|
|
|
G4double e = e1 + e2*G4UniformRand();
|
|
|
|
|
G4double x = e*e - M2;
|
|
|
|
|
G4double y = MG2/(x*x + MG2);
|
|
|
|
|
if (y >= G4UniformRand()) {
|
|
|
|
|
M = e;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|