Files
2025-12-05 08:54:02 +01:00

491 lines
17 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"
#ifndef G4INCLCluster_hh
#define G4INCLCluster_hh 1
#include "G4INCLParticle.hh"
#include "G4INCLNuclearDensityFactory.hh"
#include "G4INCLParticleSampler.hh"
#include "G4INCLAllocationPool.hh"
namespace G4INCL {
/**
* Cluster is a particle (inherits from the Particle class) that is
* actually a collection of elementary particles.
*/
class Cluster : public Particle {
public:
/** \brief Standard Cluster constructor
*
* This constructor should mainly be used when constructing Nucleus or
* when constructing Clusters to be used as composite projectiles.
*/
Cluster(const G4int Z, const G4int A, const G4int S, const G4bool createParticleSampler=true) :
Particle(),
theExcitationEnergy(0.),
theSpin(0.,0.,0.),
theParticleSampler(NULL)
{
if(A >= 0){
setType(Composite);
theZ = Z;
theA = A;
theS = S;
setINCLMass();
if(createParticleSampler)
theParticleSampler = new ParticleSampler(A,Z,S);
}
else {
setType(antiComposite);
theZ = Z;
theA = A;
theS = S;
setINCLMass();
if(createParticleSampler)
theParticleSampler = new ParticleSampler(A,Z,S);
}
}
/**
* A cluster can be directly built from a list of particles.
*/
template<class Iterator>
Cluster(Iterator begin, Iterator end) :
Particle(),
theExcitationEnergy(0.),
theSpin(0.,0.,0.),
theParticleSampler(NULL)
{
setType(Composite);
for(Iterator i = begin; i != end; ++i) {
addParticle(*i);
}
if (theA < 0){
setType(antiComposite);
thePosition /= (-theA);
}
else
thePosition /= theA;
setINCLMass();
adjustMomentumFromEnergy();
}
virtual ~Cluster() {
delete theParticleSampler;
}
/// \brief Copy constructor
Cluster(const Cluster &rhs) :
Particle(rhs),
theExcitationEnergy(rhs.theExcitationEnergy),
theSpin(rhs.theSpin)
{
for(ParticleIter p=rhs.particles.begin(), e=rhs.particles.end(); p!=e; ++p) {
particles.push_back(new Particle(**p));
}
if(rhs.theParticleSampler)
theParticleSampler = new ParticleSampler(rhs.theA,rhs.theZ,rhs.theS);
else
theParticleSampler = NULL;
}
/// \brief Assignment operator
Cluster &operator=(const Cluster &rhs) {
Cluster temporaryCluster(rhs);
Particle::operator=(temporaryCluster);
swap(temporaryCluster);
return *this;
}
/// \brief Helper method for the assignment operator
void swap(Cluster &rhs) {
Particle::swap(rhs);
std::swap(theExcitationEnergy, rhs.theExcitationEnergy);
std::swap(theSpin, rhs.theSpin);
// std::swap is overloaded by std::list and guaranteed to operate in
// constant time
std::swap(particles, rhs.particles);
std::swap(theParticleSampler, rhs.theParticleSampler);
}
ParticleSpecies getSpecies() const {
return ParticleSpecies(theA, theZ, theS);
}
void deleteParticles() {
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
delete (*p);
}
clearParticles();
}
void clearParticles() { particles.clear(); }
/// \brief Set the charge number of the cluster
void setZ(const G4int Z) { theZ = Z; }
/// \brief Set the mass number of the cluster
void setA(const G4int A) { theA = A; }
/// \brief Set the strangess number of the cluster
void setS(const G4int S) { theS = S; }
/// \brief Get the excitation energy of the cluster.
G4double getExcitationEnergy() const { return theExcitationEnergy; }
/// \brief Set the excitation energy of the cluster.
void setExcitationEnergy(const G4double e) { theExcitationEnergy=e; }
/** \brief Get the real particle mass.
*
* Overloads the Particle method.
*/
inline virtual G4double getTableMass() const { return getRealMass(); }
/**
* Get the list of particles in the cluster.
*/
ParticleList const &getParticles() const { return particles; }
/// \brief Remove a particle from the cluster components.
void removeParticle(Particle * const p) { particles.remove(p); }
/**
* Add one particle to the cluster. This updates the cluster mass,
* energy, size, etc.
*/
void addParticle(Particle * const p) {
particles.push_back(p);
theEnergy += p->getEnergy();
thePotentialEnergy += p->getPotentialEnergy();
theMomentum += p->getMomentum();
thePosition += p->getPosition();
theA += p->getA();
theZ += p->getZ();
theS += p->getS();
nCollisions += p->getNumberOfCollisions();
}
/// \brief Set total cluster mass, energy, size, etc. from the particles
void updateClusterParameters() {
theEnergy = 0.;
thePotentialEnergy = 0.;
theMomentum = ThreeVector();
thePosition = ThreeVector();
theA = 0;
theZ = 0;
theS = 0;
nCollisions = 0;
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
theEnergy += (*p)->getEnergy();
thePotentialEnergy += (*p)->getPotentialEnergy();
theMomentum += (*p)->getMomentum();
thePosition += (*p)->getPosition();
theA += (*p)->getA();
theZ += (*p)->getZ();
theS += (*p)->getS();
nCollisions += (*p)->getNumberOfCollisions();
}
}
/// \brief Add a list of particles to the cluster
void addParticles(ParticleList const &pL) {
particles = pL;
updateClusterParameters();
}
/// \brief Returns the list of particles that make up the cluster
ParticleList getParticleList() const { return particles; }
std::string print() const {
std::stringstream ss;
ss << "Cluster (ID = " << ID << ") type = ";
ss << ParticleTable::getName(theType);
ss << '\n'
<< " A = " << theA << '\n'
<< " Z = " << theZ << '\n'
<< " S = " << theS << '\n'
<< " mass = " << getMass() << '\n'
<< " energy = " << theEnergy << '\n'
<< " momentum = "
<< theMomentum.print()
<< '\n'
<< " position = "
<< thePosition.print()
<< '\n'
<< "Contains the following particles:"
<< '\n';
for(ParticleIter i=particles.begin(), e=particles.end(); i!=e; ++i)
ss << (*i)->print();
ss << '\n';
return ss.str();
}
/// \brief Initialise the NuclearDensity pointer and sample the particles
virtual void initializeParticles();
/** \brief Boost to the CM of the component particles
*
* The position of all particles in the particles list is shifted so that
* their centre of mass is in the origin and their total momentum is
* zero.
*/
void internalBoostToCM() {
// First compute the current CM position and total momentum
ThreeVector theCMPosition, theTotalMomentum;
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
theCMPosition += (*p)->getPosition();
theTotalMomentum += (*p)->getMomentum();
//theTotalEnergy += (*p)->getEnergy();
}
if(theA>=0){
theCMPosition /= theA;
// assert((unsigned int)theA==particles.size());
} else if (theA < 0){
theCMPosition /= -theA;
//assert(-theA==particles.size());
}
// Now determine the CM velocity of the particles
// commented out because currently unused, see below
// ThreeVector betaCM = theTotalMomentum / theTotalEnergy;
// The new particle positions and momenta are scaled by a factor of
// \f$\sqrt{A/(A-1)}\f$, so that the resulting density distributions in
// the CM have the same variance as the one we started with.
G4double rescaling;
if (theA>0)
rescaling = std::sqrt(((G4double)theA)/((G4double)(theA-1)));
else if (theA<0)
rescaling = std::sqrt(((G4double)(-theA))/((G4double)((-theA)-1)));
else
rescaling = 0 ;
// Loop again to boost and reposition
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
// \bug{We should do the following, but the Fortran version actually
// does not!
// (*p)->boost(betaCM);
// Here is what the Fortran version does:}
if (theA>0)
(*p)->setMomentum(((*p)->getMomentum()-theTotalMomentum/theA)*rescaling);
else if (theA<0)
(*p)->setMomentum(((*p)->getMomentum()-theTotalMomentum/(-theA))*rescaling);
// Set the CM position of the particles
(*p)->setPosition(((*p)->getPosition()-theCMPosition)*rescaling);
}
// Set the global cluster kinematic variables
thePosition.setX(0.0);
thePosition.setY(0.0);
thePosition.setZ(0.0);
theMomentum.setX(0.0);
theMomentum.setY(0.0);
theMomentum.setZ(0.0);
theEnergy = getMass();
INCL_DEBUG("Cluster boosted to internal CM:" << '\n' << print());
}
/** \brief Put the cluster components off shell
*
* The Cluster components are put off shell in such a way that their total
* energy equals the cluster mass.
*/
void putParticlesOffShell() {
// Compute the dynamical potential
const G4double theDynamicalPotential = computeDynamicalPotential();
INCL_DEBUG("The dynamical potential is " << theDynamicalPotential << " MeV" << '\n');
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
const G4double energy = (*p)->getEnergy() - theDynamicalPotential;
const ThreeVector &momentum = (*p)->getMomentum();
// Here particles are put off-shell so that we can satisfy the energy-
// and momentum-conservation laws
(*p)->setEnergy(energy);
(*p)->setMass(std::sqrt(energy*energy - momentum.mag2()));
}
INCL_DEBUG("Cluster components are now off shell:" << '\n'
<< print());
}
/** \brief Set the position of the cluster
*
* This overloads the Particle method to take into account that the
* positions of the cluster members must be updated as well.
*/
void setPosition(const ThreeVector &position) {
ThreeVector shift(position-thePosition);
Particle::setPosition(position);
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
(*p)->setPosition((*p)->getPosition()+shift);
}
}
/** \brief Boost the cluster with the indicated velocity
*
* The Cluster is boosted as a whole, just like any Particle object;
* moreover, the internal components (particles list) are also boosted,
* according to Alain Boudard's off-shell recipe.
*
* \param aBoostVector the velocity to boost to [c]
*/
void boost(const ThreeVector &aBoostVector) {
Particle::boost(aBoostVector);
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
(*p)->boost(aBoostVector);
// Apply Lorentz contraction to the particle position
(*p)->lorentzContract(aBoostVector,thePosition);
(*p)->rpCorrelate();
}
INCL_DEBUG("Cluster was boosted with (bx,by,bz)=("
<< aBoostVector.getX() << ", " << aBoostVector.getY() << ", " << aBoostVector.getZ() << "):"
<< '\n' << print());
}
/** \brief Freeze the internal motion of the particles
*
* Each particle is assigned a frozen momentum four-vector determined by
* the collective cluster velocity. This is used for propagation, but not
* for dynamics. Normal propagation is restored by calling the
* Particle::thawPropagation() method, which should be done in
* InteractionAvatar::postInteraction.
*/
void freezeInternalMotion() {
const ThreeVector &normMomentum = theMomentum / getMass();
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
const G4double pMass = (*p)->getMass();
const ThreeVector frozenMomentum = normMomentum * pMass;
const G4double frozenEnergy = std::sqrt(frozenMomentum.mag2()+pMass*pMass);
(*p)->setFrozenMomentum(frozenMomentum);
(*p)->setFrozenEnergy(frozenEnergy);
(*p)->freezePropagation();
}
}
/** \brief Rotate position of all the particles
*
* This includes the cluster components. Overloads Particle::rotateMomentum().
*
* \param angle the rotation angle
* \param axis a unit vector representing the rotation axis
*/
virtual void rotatePosition(const G4double angle, const ThreeVector &axis);
/** \brief Rotate momentum of all the particles
*
* This includes the cluster components. Overloads Particle::rotateMomentum().
*
* \param angle the rotation angle
* \param axis a unit vector representing the rotation axis
*/
virtual void rotateMomentum(const G4double angle, const ThreeVector &axis);
/// \brief Make all the components projectile spectators, too
virtual void makeProjectileSpectator() {
Particle::makeProjectileSpectator();
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
(*p)->makeProjectileSpectator();
}
}
/// \brief Make all the components target spectators, too
virtual void makeTargetSpectator() {
Particle::makeTargetSpectator();
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
(*p)->makeTargetSpectator();
}
}
/// \brief Make all the components participants, too
virtual void makeParticipant() {
Particle::makeParticipant();
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
(*p)->makeParticipant();
}
}
/// \brief Get the spin of the nucleus.
ThreeVector const &getSpin() const { return theSpin; }
/// \brief Set the spin of the nucleus.
void setSpin(const ThreeVector &j) { theSpin = j; }
/// \brief Get the total angular momentum (orbital + spin)
G4INCL::ThreeVector getAngularMomentum() const {
return Particle::getAngularMomentum() + getSpin();
}
private:
/** \brief Compute the dynamical cluster potential
*
* Alain Boudard's boost prescription for low-energy beams requires to
* define a "dynamical potential" that allows us to conserve momentum and
* energy when boosting the projectile cluster.
*/
G4double computeDynamicalPotential() {
G4double theDynamicalPotential = 0.0;
for(ParticleIter p=particles.begin(), e=particles.end(); p!=e; ++p) {
theDynamicalPotential += (*p)->getEnergy();
}
theDynamicalPotential -= getTableMass();
theDynamicalPotential /= std::abs(theA);
return theDynamicalPotential;
}
protected:
ParticleList particles;
G4double theExcitationEnergy;
ThreeVector theSpin;
ParticleSampler *theParticleSampler;
INCL_DECLARE_ALLOCATION_POOL(Cluster)
};
}
#endif