// // ******************************************************************** // * 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 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