Files
geant4/source/processes/hadronic/models/inclxx/incl_physics/src/G4INCLNucleus.cc
T

860 lines
34 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. *
// ********************************************************************
//
// INCL++ intra-nuclear cascade model
// Alain Boudard, CEA-Saclay, France
// Joseph Cugnon, University of Liege, Belgium
// Jean-Christophe David, CEA-Saclay, France
// Pekka Kaitaniemi, CEA-Saclay, France, and Helsinki Institute of Physics, Finland
// Sylvie Leray, CEA-Saclay, France
// Davide Mancusi, CEA-Saclay, France
//
#define INCLXX_IN_GEANT4_MODE 1
#include "globals.hh"
/*
* G4INCLNucleus.cc
*
* \date Jun 5, 2009
* \author Pekka Kaitaniemi
*/
#ifndef G4INCLNucleus_hh
#define G4INCLNucleus_hh 1
#include "G4INCLGlobals.hh"
#include "G4INCLLogger.hh"
#include "G4INCLParticle.hh"
#include "G4INCLIAvatar.hh"
#include "G4INCLNucleus.hh"
#include "G4INCLKinematicsUtils.hh"
#include "G4INCLDecayAvatar.hh"
#include "G4INCLCluster.hh"
#include "G4INCLClusterDecay.hh"
#include "G4INCLDeJongSpin.hh"
#include <iterator>
#include <cstdlib>
#include <sstream>
// #include <cassert>
namespace G4INCL {
Nucleus::Nucleus(G4int mass, G4int charge, Config const * const conf, const G4double universeRadius)
: Cluster(charge,mass,true),
theInitialZ(charge), theInitialA(mass),
theNpInitial(0), theNnInitial(0),
initialInternalEnergy(0.),
incomingAngularMomentum(0.,0.,0.), incomingMomentum(0.,0.,0.),
initialCenterOfMass(0.,0.,0.),
remnant(true),
initialEnergy(0.),
tryCN(false),
theUniverseRadius(universeRadius),
isNucleusNucleus(false),
theProjectileRemnant(NULL),
theDensity(NULL),
thePotential(NULL)
{
PotentialType potentialType;
G4bool pionPotential;
if(conf) {
potentialType = conf->getPotentialType();
pionPotential = conf->getPionPotential();
} else { // By default we don't use energy dependent
// potential. This is convenient for some tests.
potentialType = IsospinPotential;
pionPotential = true;
}
thePotential = NuclearPotential::createPotential(potentialType, theA, theZ, pionPotential);
ParticleTable::setProtonSeparationEnergy(thePotential->getSeparationEnergy(Proton));
ParticleTable::setNeutronSeparationEnergy(thePotential->getSeparationEnergy(Neutron));
theDensity = NuclearDensityFactory::createDensity(theA, theZ);
theParticleSampler->setPotential(thePotential);
theParticleSampler->setDensity(theDensity);
if(theUniverseRadius<0)
theUniverseRadius = theDensity->getMaximumRadius();
theStore = new Store(conf);
}
Nucleus::~Nucleus() {
delete theStore;
deleteProjectileRemnant();
/* We don't delete the potential and the density here any more -- Factories
* are caching them
delete thePotential;
delete theDensity;*/
}
void Nucleus::initializeParticles() {
// Reset the variables connected with the projectile remnant
delete theProjectileRemnant;
theProjectileRemnant = NULL;
Cluster::initializeParticles();
for(ParticleIter i=particles.begin(), e=particles.end(); i!=e; ++i) {
updatePotentialEnergy(*i);
}
theStore->add(particles);
particles.clear();
initialInternalEnergy = computeTotalEnergy();
initialCenterOfMass = thePosition;
}
void Nucleus::applyFinalState(FinalState *finalstate) {
if(!finalstate) // do nothing if no final state was returned
return;
G4double totalEnergy = 0.0;
FinalStateValidity const validity = finalstate->getValidity();
if(validity == ValidFS) {
ParticleList const &created = finalstate->getCreatedParticles();
for(ParticleIter iter=created.begin(), e=created.end(); iter!=e; ++iter) {
theStore->add((*iter));
if(!(*iter)->isOutOfWell()) {
totalEnergy += (*iter)->getEnergy() - (*iter)->getPotentialEnergy();
}
}
ParticleList const &deleted = finalstate->getDestroyedParticles();
for(ParticleIter iter=deleted.begin(), e=deleted.end(); iter!=e; ++iter) {
theStore->particleHasBeenDestroyed(*iter);
}
ParticleList const &modified = finalstate->getModifiedParticles();
for(ParticleIter iter=modified.begin(), e=modified.end(); iter!=e; ++iter) {
theStore->particleHasBeenUpdated(*iter);
totalEnergy += (*iter)->getEnergy() - (*iter)->getPotentialEnergy();
}
ParticleList const &out = finalstate->getOutgoingParticles();
for(ParticleIter iter=out.begin(), e=out.end(); iter!=e; ++iter) {
if((*iter)->isCluster()) {
Cluster *clusterOut = dynamic_cast<Cluster*>((*iter));
// assert(clusterOut);
#ifdef INCLXX_IN_GEANT4_MODE
if(!clusterOut)
continue;
#endif
ParticleList const &components = clusterOut->getParticles();
for(ParticleIter in=components.begin(), end=components.end(); in!=end; ++in)
theStore->particleHasBeenEjected(*in);
} else {
theStore->particleHasBeenEjected(*iter);
}
totalEnergy += (*iter)->getEnergy(); // No potential here because the particle is gone
theA -= (*iter)->getA();
theZ -= (*iter)->getZ();
theStore->addToOutgoing(*iter);
(*iter)->setEmissionTime(theStore->getBook().getCurrentTime());
}
ParticleList const &entering = finalstate->getEnteringParticles();
for(ParticleIter iter=entering.begin(), e=entering.end(); iter!=e; ++iter) {
insertParticle(*iter);
totalEnergy += (*iter)->getEnergy() - (*iter)->getPotentialEnergy();
}
// actually perform the removal of the scheduled avatars
theStore->removeScheduledAvatars();
} else if(validity == ParticleBelowFermiFS || validity == ParticleBelowZeroFS) {
INCL_DEBUG("A Particle is entering below the Fermi sea:" << '\n' << finalstate->print() << '\n');
tryCN = true;
ParticleList const &entering = finalstate->getEnteringParticles();
for(ParticleIter iter=entering.begin(), e=entering.end(); iter!=e; ++iter) {
insertParticle(*iter);
}
}
if(validity==ValidFS &&
std::abs(totalEnergy - finalstate->getTotalEnergyBeforeInteraction()) > 0.1) {
INCL_ERROR("Energy nonconservation! Energy at the beginning of the event = "
<< finalstate->getTotalEnergyBeforeInteraction()
<<" and after interaction = "
<< totalEnergy << '\n'
<< finalstate->print());
}
}
void Nucleus::propagateParticles(G4double /*step*/) {
INCL_WARN("Useless Nucleus::propagateParticles -method called." << '\n');
}
G4double Nucleus::computeTotalEnergy() const {
G4double totalEnergy = 0.0;
ParticleList const &inside = theStore->getParticles();
for(ParticleIter p=inside.begin(), e=inside.end(); p!=e; ++p) {
if((*p)->isNucleon()) // Ugly: we should calculate everything using total energies!
totalEnergy += (*p)->getKineticEnergy() - (*p)->getPotentialEnergy();
else if((*p)->isResonance())
totalEnergy += (*p)->getEnergy() - (*p)->getPotentialEnergy() - ParticleTable::effectiveNucleonMass;
else
totalEnergy += (*p)->getEnergy() - (*p)->getPotentialEnergy();
}
return totalEnergy;
}
void Nucleus::computeRecoilKinematics() {
// If the remnant consists of only one nucleon, we need to apply a special
// procedure to put it on mass shell.
if(theA==1) {
emitInsidePions();
computeOneNucleonRecoilKinematics();
remnant=false;
return;
}
// Compute the recoil momentum and angular momentum
theMomentum = incomingMomentum;
theSpin = incomingAngularMomentum;
ParticleList const &outgoing = theStore->getOutgoingParticles();
for(ParticleIter p=outgoing.begin(), e=outgoing.end(); p!=e; ++p) {
theMomentum -= (*p)->getMomentum();
theSpin -= (*p)->getAngularMomentum();
}
if(theProjectileRemnant) {
theMomentum -= theProjectileRemnant->getMomentum();
theSpin -= theProjectileRemnant->getAngularMomentum();
}
// Subtract orbital angular momentum
thePosition = computeCenterOfMass();
theSpin -= (thePosition-initialCenterOfMass).vector(theMomentum);
setMass(ParticleTable::getTableMass(theA,theZ) + theExcitationEnergy);
adjustEnergyFromMomentum();
remnant=true;
}
ThreeVector Nucleus::computeCenterOfMass() const {
ThreeVector cm(0.,0.,0.);
G4double totalMass = 0.0;
ParticleList const &inside = theStore->getParticles();
for(ParticleIter p=inside.begin(), e=inside.end(); p!=e; ++p) {
const G4double mass = (*p)->getMass();
cm += (*p)->getPosition() * mass;
totalMass += mass;
}
cm /= totalMass;
return cm;
}
G4double Nucleus::computeExcitationEnergy() const {
const G4double totalEnergy = computeTotalEnergy();
const G4double separationEnergies = computeSeparationEnergyBalance();
return totalEnergy - initialInternalEnergy - separationEnergies;
}
std::string Nucleus::print()
{
std::stringstream ss;
ss << "Particles in the nucleus:" << '\n'
<< "Inside:" << '\n';
G4int counter = 1;
ParticleList const &inside = theStore->getParticles();
for(ParticleIter p=inside.begin(), e=inside.end(); p!=e; ++p) {
ss << "index = " << counter << '\n'
<< (*p)->print();
counter++;
}
ss <<"Outgoing:" << '\n';
ParticleList const &outgoing = theStore->getOutgoingParticles();
for(ParticleIter p=outgoing.begin(), e=outgoing.end(); p!=e; ++p)
ss << (*p)->print();
return ss.str();
}
G4bool Nucleus::decayOutgoingDeltas() {
ParticleList const &out = theStore->getOutgoingParticles();
ParticleList deltas;
for(ParticleIter i=out.begin(), e=out.end(); i!=e; ++i) {
if((*i)->isDelta()) deltas.push_back((*i));
}
if(deltas.empty()) return false;
for(ParticleIter i=deltas.begin(), e=deltas.end(); i!=e; ++i) {
INCL_DEBUG("Decay outgoing delta particle:" << '\n'
<< (*i)->print() << '\n');
const ThreeVector beta = -(*i)->boostVector();
const G4double deltaMass = (*i)->getMass();
// Set the delta momentum to zero and sample the decay in the CM frame.
// This makes life simpler if we are using real particle masses.
(*i)->setMomentum(ThreeVector());
(*i)->setEnergy((*i)->getMass());
// Use a DecayAvatar
IAvatar *decay = new DecayAvatar((*i), 0.0, NULL);
FinalState *fs = decay->getFinalState();
Particle * const pion = fs->getCreatedParticles().front();
Particle * const nucleon = fs->getModifiedParticles().front();
// Adjust the decay momentum if we are using the real masses
const G4double decayMomentum = KinematicsUtils::momentumInCM(deltaMass,
nucleon->getTableMass(),
pion->getTableMass());
ThreeVector newMomentum = pion->getMomentum();
newMomentum *= decayMomentum / newMomentum.mag();
pion->setTableMass();
pion->setMomentum(newMomentum);
pion->adjustEnergyFromMomentum();
pion->setEmissionTime(nucleon->getEmissionTime());
pion->boost(beta);
nucleon->setTableMass();
nucleon->setMomentum(-newMomentum);
nucleon->adjustEnergyFromMomentum();
nucleon->boost(beta);
theStore->addToOutgoing(pion);
delete fs;
delete decay;
}
return true;
}
G4bool Nucleus::decayInsideDeltas() {
/* If there is a pion potential, do nothing (deltas will be counted as
* excitation energy).
* If, however, the remnant is unphysical (Z<0 or Z>A), force the deltas to
* decay and get rid of all the pions. In case you're wondering, you can
* end up with Z<0 or Z>A if the remnant contains more pi- than protons or
* more pi+ than neutrons, respectively.
*/
const G4bool unphysicalRemnant = (theZ<0 || theZ>theA);
if(thePotential->hasPionPotential() && !unphysicalRemnant)
return false;
// Build a list of deltas (avoid modifying the list you are iterating on).
ParticleList const &inside = theStore->getParticles();
ParticleList deltas;
for(ParticleIter i=inside.begin(), e=inside.end(); i!=e; ++i)
if((*i)->isDelta()) deltas.push_back((*i));
// Loop over the deltas, make them decay
for(ParticleIter i=deltas.begin(), e=deltas.end(); i!=e; ++i) {
INCL_DEBUG("Decay inside delta particle:" << '\n'
<< (*i)->print() << '\n');
// Create a forced-decay avatar. Note the last boolean parameter. Note
// also that if the remnant is unphysical we more or less explicitly give
// up energy conservation and CDPP by passing a NULL pointer for the
// nucleus.
IAvatar *decay;
if(unphysicalRemnant) {
INCL_WARN("Forcing delta decay inside an unphysical remnant (A=" << theA
<< ", Z=" << theZ << "). Might lead to energy-violation warnings."
<< '\n');
decay = new DecayAvatar((*i), 0.0, NULL, true);
} else
decay = new DecayAvatar((*i), 0.0, this, true);
FinalState *fs = decay->getFinalState();
// The pion can be ejected only if we managed to satisfy energy
// conservation and if pion emission does not lead to negative excitation
// energies.
if(fs->getValidity()==ValidFS) {
// Apply the final state to the nucleus
applyFinalState(fs);
}
delete fs;
delete decay;
}
// If the remnant is unphysical, emit all the pions
if(unphysicalRemnant) {
INCL_DEBUG("Remnant is unphysical: Z=" << theZ << ", A=" << theA << ", emitting all the pions" << '\n');
emitInsidePions();
}
return true;
}
G4bool Nucleus::decayOutgoingClusters() {
ParticleList const &out = theStore->getOutgoingParticles();
ParticleList clusters;
for(ParticleIter i=out.begin(), e=out.end(); i!=e; ++i) {
if((*i)->isCluster()) clusters.push_back((*i));
}
if(clusters.empty()) return false;
for(ParticleIter i=clusters.begin(), e=clusters.end(); i!=e; ++i) {
Cluster *cluster = dynamic_cast<Cluster*>(*i); // Can't avoid using a cast here
// assert(cluster);
#ifdef INCLXX_IN_GEANT4_MODE
if(!cluster)
continue;
#endif
cluster->deleteParticles(); // Don't need them
ParticleList decayProducts = ClusterDecay::decay(cluster);
for(ParticleIter j=decayProducts.begin(), end=decayProducts.end(); j!=end; ++j)
theStore->addToOutgoing(*j);
}
return true;
}
G4bool Nucleus::decayMe() {
// Do the phase-space decay only if Z=0 or Z=A
if(theA<=1 || (theZ!=0 && theA!=theZ))
return false;
ParticleList decayProducts = ClusterDecay::decay(this);
for(ParticleIter j=decayProducts.begin(), e=decayProducts.end(); j!=e; ++j)
theStore->addToOutgoing(*j);
return true;
}
void Nucleus::emitInsidePions() {
/* Forcing emissions of all pions in the nucleus. This probably violates
* energy conservation (although the computation of the recoil kinematics
* might sweep this under the carpet).
*/
INCL_WARN("Forcing emissions of all pions in the nucleus." << '\n');
// Emit the pions with this kinetic energy
const G4double tinyPionEnergy = 0.1; // MeV
// Push out the emitted pions
ParticleList const &inside = theStore->getParticles();
ParticleList toEject;
for(ParticleIter i=inside.begin(), e=inside.end(); i!=e; ++i) {
if((*i)->isPion()) {
Particle * const thePion = *i;
INCL_DEBUG("Forcing emission of the following particle: "
<< thePion->print() << '\n');
thePion->setEmissionTime(theStore->getBook().getCurrentTime());
// Correction for real masses
const G4double theQValueCorrection = thePion->getEmissionQValueCorrection(theA,theZ);
const G4double kineticEnergyOutside = thePion->getKineticEnergy() - thePion->getPotentialEnergy() + theQValueCorrection;
thePion->setTableMass();
if(kineticEnergyOutside > 0.0)
thePion->setEnergy(thePion->getMass()+kineticEnergyOutside);
else
thePion->setEnergy(thePion->getMass()+tinyPionEnergy);
thePion->adjustMomentumFromEnergy();
thePion->setPotentialEnergy(0.);
theZ -= thePion->getZ();
toEject.push_back(thePion);
}
}
for(ParticleIter i=toEject.begin(), e=toEject.end(); i!=e; ++i) {
theStore->particleHasBeenEjected(*i);
theStore->addToOutgoing(*i);
}
}
G4bool Nucleus::isEventTransparent() const {
Book const &theBook = theStore->getBook();
const G4int nEventCollisions = theBook.getAcceptedCollisions();
const G4int nEventDecays = theBook.getAcceptedDecays();
const G4int nEventClusters = theBook.getEmittedClusters();
if(nEventCollisions==0 && nEventDecays==0 && nEventClusters==0)
return true;
return false;
}
void Nucleus::computeOneNucleonRecoilKinematics() {
// We should be here only if the nucleus contains only one nucleon
// assert(theStore->getParticles().size()==1);
// No excitation energy!
theExcitationEnergy = 0.0;
// Move the nucleon to the outgoing list
Particle *remN = theStore->getParticles().front();
theA -= remN->getA();
theZ -= remN->getZ();
theStore->particleHasBeenEjected(remN);
theStore->addToOutgoing(remN);
remN->setEmissionTime(theStore->getBook().getCurrentTime());
// Treat the special case of a remaining delta
if(remN->isDelta()) {
IAvatar *decay = new DecayAvatar(remN, 0.0, NULL);
FinalState *fs = decay->getFinalState();
// Eject the pion
ParticleList const &created = fs->getCreatedParticles();
for(ParticleIter j=created.begin(), e=created.end(); j!=e; ++j)
theStore->addToOutgoing(*j);
delete fs;
delete decay;
}
// Do different things depending on how many outgoing particles we have
ParticleList const &outgoing = theStore->getOutgoingParticles();
if(outgoing.size() == 2) {
INCL_DEBUG("Two particles in the outgoing channel, applying exact two-body kinematics" << '\n');
// Can apply exact 2-body kinematics here. Keep the CM emission angle of
// the first particle.
Particle *p1 = outgoing.front(), *p2 = outgoing.back();
const ThreeVector aBoostVector = incomingMomentum / initialEnergy;
// Boost to the initial CM
p1->boost(aBoostVector);
const G4double sqrts = std::sqrt(initialEnergy*initialEnergy - incomingMomentum.mag2());
const G4double pcm = KinematicsUtils::momentumInCM(sqrts, p1->getMass(), p2->getMass());
const G4double scale = pcm/(p1->getMomentum().mag());
// Reset the momenta
p1->setMomentum(p1->getMomentum()*scale);
p2->setMomentum(-p1->getMomentum());
p1->adjustEnergyFromMomentum();
p2->adjustEnergyFromMomentum();
// Unboost
p1->boost(-aBoostVector);
p2->boost(-aBoostVector);
} else {
INCL_DEBUG("Trying to adjust final-state momenta to achieve energy and momentum conservation" << '\n');
const G4int maxIterations=8;
G4double totalEnergy, energyScale;
G4double val=1.E+100, oldVal=1.E+100, oldOldVal=1.E+100, oldOldOldVal;
ThreeVector totalMomentum, deltaP;
std::vector<ThreeVector> minMomenta; // use it to store the particle momenta that minimize the merit function
// Reserve the vector size
minMomenta.reserve(outgoing.size());
// Compute the initial total momentum
totalMomentum.setX(0.0);
totalMomentum.setY(0.0);
totalMomentum.setZ(0.0);
for(ParticleIter i=outgoing.begin(), e=outgoing.end(); i!=e; ++i)
totalMomentum += (*i)->getMomentum();
// Compute the initial total energy
totalEnergy = 0.0;
for(ParticleIter i=outgoing.begin(), e=outgoing.end(); i!=e; ++i)
totalEnergy += (*i)->getEnergy();
// Iterative algorithm starts here:
for(G4int iterations=0; iterations < maxIterations; ++iterations) {
// Save the old merit-function values
oldOldOldVal = oldOldVal;
oldOldVal = oldVal;
oldVal = val;
if(iterations%2 == 0) {
INCL_DEBUG("Momentum step" << '\n');
// Momentum step: modify all the particle momenta
deltaP = incomingMomentum - totalMomentum;
G4double pOldTot = 0.0;
for(ParticleIter i=outgoing.begin(), e=outgoing.end(); i!=e; ++i)
pOldTot += (*i)->getMomentum().mag();
for(ParticleIter i=outgoing.begin(), e=outgoing.end(); i!=e; ++i) {
const ThreeVector mom = (*i)->getMomentum();
(*i)->setMomentum(mom + deltaP*mom.mag()/pOldTot);
(*i)->adjustEnergyFromMomentum();
}
} else {
INCL_DEBUG("Energy step" << '\n');
// Energy step: modify all the particle momenta
energyScale = initialEnergy/totalEnergy;
for(ParticleIter i=outgoing.begin(), e=outgoing.end(); i!=e; ++i) {
const ThreeVector mom = (*i)->getMomentum();
G4double pScale = ((*i)->getEnergy()*energyScale - std::pow((*i)->getMass(),2))/mom.mag2();
if(pScale>0) {
(*i)->setEnergy((*i)->getEnergy()*energyScale);
(*i)->adjustMomentumFromEnergy();
}
}
}
// Compute the current total momentum and energy
totalMomentum.setX(0.0);
totalMomentum.setY(0.0);
totalMomentum.setZ(0.0);
totalEnergy = 0.0;
for(ParticleIter i=outgoing.begin(), e=outgoing.end(); i!=e; ++i) {
totalMomentum += (*i)->getMomentum();
totalEnergy += (*i)->getEnergy();
}
// Merit factor
val = std::pow(totalEnergy - initialEnergy,2) +
0.25*(totalMomentum - incomingMomentum).mag2();
INCL_DEBUG("Merit function: val=" << val << ", oldVal=" << oldVal << ", oldOldVal=" << oldOldVal << ", oldOldOldVal=" << oldOldOldVal << '\n');
// Store the minimum
if(val < oldVal) {
INCL_DEBUG("New minimum found, storing the particle momenta" << '\n');
minMomenta.clear();
for(ParticleIter i=outgoing.begin(), e=outgoing.end(); i!=e; ++i)
minMomenta.push_back((*i)->getMomentum());
}
// Stop the algorithm if the search diverges
if(val > oldOldVal && oldVal > oldOldOldVal) {
INCL_DEBUG("Search is diverging, breaking out of the iteration loop: val=" << val << ", oldVal=" << oldVal << ", oldOldVal=" << oldOldVal << ", oldOldOldVal=" << oldOldOldVal << '\n');
break;
}
}
// We should have made at least one successful iteration here
// assert(minMomenta.size()==outgoing.size());
// Apply the optimal momenta
INCL_DEBUG("Applying the solution" << '\n');
std::vector<ThreeVector>::const_iterator v = minMomenta.begin();
for(ParticleIter i=outgoing.begin(), e=outgoing.end(); i!=e; ++i, ++v) {
(*i)->setMomentum(*v);
(*i)->adjustEnergyFromMomentum();
INCL_DATABLOCK((*i)->print());
}
}
}
void Nucleus::fillEventInfo(EventInfo *eventInfo) {
eventInfo->nParticles = 0;
G4bool isNucleonAbsorption = false;
G4bool isPionAbsorption = false;
// It is possible to have pion absorption event only if the
// projectile is pion.
if(eventInfo->projectileType == PiPlus ||
eventInfo->projectileType == PiMinus ||
eventInfo->projectileType == PiZero) {
isPionAbsorption = true;
}
// Forced CN
eventInfo->forcedCompoundNucleus = tryCN;
// Outgoing particles
ParticleList const &outgoingParticles = getStore()->getOutgoingParticles();
// Check if we have a nucleon absorption event: nucleon projectile
// and no ejected particles.
if(outgoingParticles.size() == 0 &&
(eventInfo->projectileType == Proton ||
eventInfo->projectileType == Neutron)) {
isNucleonAbsorption = true;
}
// Reset the remnant counter
eventInfo->nRemnants = 0;
eventInfo->history.clear();
for(ParticleIter i=outgoingParticles.begin(), e=outgoingParticles.end(); i!=e; ++i ) {
// We have a pion absorption event only if the projectile is
// pion and there are no ejected pions.
if(isPionAbsorption) {
if((*i)->isPion()) {
isPionAbsorption = false;
}
}
eventInfo->A[eventInfo->nParticles] = (*i)->getA();
eventInfo->Z[eventInfo->nParticles] = (*i)->getZ();
eventInfo->emissionTime[eventInfo->nParticles] = (*i)->getEmissionTime();
eventInfo->EKin[eventInfo->nParticles] = (*i)->getKineticEnergy();
ThreeVector mom = (*i)->getMomentum();
eventInfo->px[eventInfo->nParticles] = mom.getX();
eventInfo->py[eventInfo->nParticles] = mom.getY();
eventInfo->pz[eventInfo->nParticles] = mom.getZ();
eventInfo->theta[eventInfo->nParticles] = Math::toDegrees(mom.theta());
eventInfo->phi[eventInfo->nParticles] = Math::toDegrees(mom.phi());
eventInfo->origin[eventInfo->nParticles] = -1;
eventInfo->history.push_back("");
eventInfo->nParticles++;
}
eventInfo->nucleonAbsorption = isNucleonAbsorption;
eventInfo->pionAbsorption = isPionAbsorption;
eventInfo->nCascadeParticles = eventInfo->nParticles;
// Projectile-like remnant characteristics
if(theProjectileRemnant && theProjectileRemnant->getA()>0) {
eventInfo->ARem[eventInfo->nRemnants] = theProjectileRemnant->getA();
eventInfo->ZRem[eventInfo->nRemnants] = theProjectileRemnant->getZ();
G4double eStar = theProjectileRemnant->getExcitationEnergy();
if(std::abs(eStar)<1E-10)
eStar = 0.0; // blame rounding and set the excitation energy to zero
eventInfo->EStarRem[eventInfo->nRemnants] = eStar;
if(eventInfo->EStarRem[eventInfo->nRemnants]<0.) {
INCL_WARN("Negative excitation energy in projectile-like remnant! EStarRem = " << eventInfo->EStarRem[eventInfo->nRemnants] << '\n');
}
const ThreeVector &spin = theProjectileRemnant->getSpin();
if(eventInfo->ARem[eventInfo->nRemnants]%2==0) { // even-A nucleus
eventInfo->JRem[eventInfo->nRemnants] = (G4int) (spin.mag()/PhysicalConstants::hc + 0.5);
} else { // odd-A nucleus
eventInfo->JRem[eventInfo->nRemnants] = ((G4int) (spin.mag()/PhysicalConstants::hc)) + 0.5;
}
eventInfo->EKinRem[eventInfo->nRemnants] = theProjectileRemnant->getKineticEnergy();
const ThreeVector &mom = theProjectileRemnant->getMomentum();
eventInfo->pxRem[eventInfo->nRemnants] = mom.getX();
eventInfo->pyRem[eventInfo->nRemnants] = mom.getY();
eventInfo->pzRem[eventInfo->nRemnants] = mom.getZ();
eventInfo->jxRem[eventInfo->nRemnants] = spin.getX() / PhysicalConstants::hc;
eventInfo->jyRem[eventInfo->nRemnants] = spin.getY() / PhysicalConstants::hc;
eventInfo->jzRem[eventInfo->nRemnants] = spin.getZ() / PhysicalConstants::hc;
eventInfo->thetaRem[eventInfo->nRemnants] = Math::toDegrees(mom.theta());
eventInfo->phiRem[eventInfo->nRemnants] = Math::toDegrees(mom.phi());
eventInfo->nRemnants++;
}
// Target-like remnant characteristics
if(hasRemnant()) {
eventInfo->ARem[eventInfo->nRemnants] = getA();
eventInfo->ZRem[eventInfo->nRemnants] = getZ();
eventInfo->EStarRem[eventInfo->nRemnants] = getExcitationEnergy();
if(eventInfo->EStarRem[eventInfo->nRemnants]<0.) {
INCL_WARN("Negative excitation energy in target-like remnant! EStarRem = " << eventInfo->EStarRem[eventInfo->nRemnants] << '\n');
}
const ThreeVector &spin = getSpin();
if(eventInfo->ARem[eventInfo->nRemnants]%2==0) { // even-A nucleus
eventInfo->JRem[eventInfo->nRemnants] = (G4int) (spin.mag()/PhysicalConstants::hc + 0.5);
} else { // odd-A nucleus
eventInfo->JRem[eventInfo->nRemnants] = ((G4int) (spin.mag()/PhysicalConstants::hc)) + 0.5;
}
eventInfo->EKinRem[eventInfo->nRemnants] = getKineticEnergy();
const ThreeVector &mom = getMomentum();
eventInfo->pxRem[eventInfo->nRemnants] = mom.getX();
eventInfo->pyRem[eventInfo->nRemnants] = mom.getY();
eventInfo->pzRem[eventInfo->nRemnants] = mom.getZ();
eventInfo->jxRem[eventInfo->nRemnants] = spin.getX() / PhysicalConstants::hc;
eventInfo->jyRem[eventInfo->nRemnants] = spin.getY() / PhysicalConstants::hc;
eventInfo->jzRem[eventInfo->nRemnants] = spin.getZ() / PhysicalConstants::hc;
eventInfo->thetaRem[eventInfo->nRemnants] = Math::toDegrees(mom.theta());
eventInfo->phiRem[eventInfo->nRemnants] = Math::toDegrees(mom.phi());
eventInfo->nRemnants++;
}
// Global counters, flags, etc.
Book const &theBook = theStore->getBook();
eventInfo->nCollisions = theBook.getAcceptedCollisions();
eventInfo->nBlockedCollisions = theBook.getBlockedCollisions();
eventInfo->nDecays = theBook.getAcceptedDecays();
eventInfo->nBlockedDecays = theBook.getBlockedDecays();
eventInfo->firstCollisionTime = theBook.getFirstCollisionTime();
eventInfo->firstCollisionXSec = theBook.getFirstCollisionXSec();
eventInfo->firstCollisionSpectatorPosition = theBook.getFirstCollisionSpectatorPosition();
eventInfo->firstCollisionSpectatorMomentum = theBook.getFirstCollisionSpectatorMomentum();
eventInfo->firstCollisionIsElastic = theBook.getFirstCollisionIsElastic();
eventInfo->nReflectionAvatars = theBook.getAvatars(SurfaceAvatarType);
eventInfo->nCollisionAvatars = theBook.getAvatars(CollisionAvatarType);
eventInfo->nDecayAvatars = theBook.getAvatars(DecayAvatarType);
eventInfo->nEnergyViolationInteraction = theBook.getEnergyViolationInteraction();
}
Nucleus::ConservationBalance Nucleus::getConservationBalance(const EventInfo &theEventInfo, const G4bool afterRecoil) const {
ConservationBalance theBalance;
// Initialise balance variables with the incoming values
theBalance.Z = theEventInfo.Zp + theEventInfo.Zt;
theBalance.A = theEventInfo.Ap + theEventInfo.At;
theBalance.energy = getInitialEnergy();
theBalance.momentum = getIncomingMomentum();
// Process outgoing particles
ParticleList const &outgoingParticles = theStore->getOutgoingParticles();
for(ParticleIter i=outgoingParticles.begin(), e=outgoingParticles.end(); i!=e; ++i ) {
theBalance.Z -= (*i)->getZ();
theBalance.A -= (*i)->getA();
// For outgoing clusters, the total energy automatically includes the
// excitation energy
theBalance.energy -= (*i)->getEnergy(); // Note that outgoing particles should have the real mass
theBalance.momentum -= (*i)->getMomentum();
}
// Projectile-like remnant contribution, if present
if(theProjectileRemnant && theProjectileRemnant->getA()>0) {
theBalance.Z -= theProjectileRemnant->getZ();
theBalance.A -= theProjectileRemnant->getA();
theBalance.energy -= ParticleTable::getTableMass(theProjectileRemnant->getA(),theProjectileRemnant->getZ()) +
theProjectileRemnant->getExcitationEnergy();
theBalance.energy -= theProjectileRemnant->getKineticEnergy();
theBalance.momentum -= theProjectileRemnant->getMomentum();
}
// Target-like remnant contribution, if present
if(hasRemnant()) {
theBalance.Z -= getZ();
theBalance.A -= getA();
theBalance.energy -= ParticleTable::getTableMass(getA(),getZ()) +
getExcitationEnergy();
if(afterRecoil)
theBalance.energy -= getKineticEnergy();
theBalance.momentum -= getMomentum();
}
return theBalance;
}
void Nucleus::useFusionKinematics() {
setEnergy(initialEnergy);
setMomentum(incomingMomentum);
setSpin(incomingAngularMomentum);
theExcitationEnergy = std::sqrt(theEnergy*theEnergy-theMomentum.mag2()) - getTableMass();
setMass(getTableMass() + theExcitationEnergy);
}
void Nucleus::finalizeProjectileRemnant(const G4double anEmissionTime) {
// Deal with the projectile remnant
const G4int prA = theProjectileRemnant->getA();
if(prA>=1) {
// Set the mass
const G4double aMass = theProjectileRemnant->getInvariantMass();
theProjectileRemnant->setMass(aMass);
// Compute the excitation energy from the invariant mass
const G4double anExcitationEnergy = aMass
- ParticleTable::getTableMass(prA, theProjectileRemnant->getZ());
// Set the excitation energy
theProjectileRemnant->setExcitationEnergy(anExcitationEnergy);
// No spin!
theProjectileRemnant->setSpin(ThreeVector());
// Set the emission time
theProjectileRemnant->setEmissionTime(anEmissionTime);
}
}
}
#endif