Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/processes/hadronic/models/inclxx/interface/src/G4INCLXXInterface.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  1 //
  2 // ********************************************************************
  3 // * License and Disclaimer                                           *
  4 // *                                                                  *
  5 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
  6 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
  7 // * conditions of the Geant4 Software License,  included in the file *
  8 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
  9 // * include a list of copyright holders.                             *
 10 // *                                                                  *
 11 // * Neither the authors of this software system, nor their employing *
 12 // * institutes,nor the agencies providing financial support for this *
 13 // * work  make  any representation or  warranty, express or implied, *
 14 // * regarding  this  software system or assume any liability for its *
 15 // * use.  Please see the license in the file  LICENSE  and URL above *
 16 // * for the full disclaimer and the limitation of liability.         *
 17 // *                                                                  *
 18 // * This  code  implementation is the result of  the  scientific and *
 19 // * technical work of the GEANT4 collaboration.                      *
 20 // * By using,  copying,  modifying or  distributing the software (or *
 21 // * any work based  on the software)  you  agree  to acknowledge its *
 22 // * use  in  resulting  scientific  publications,  and indicate your *
 23 // * acceptance of all terms of the Geant4 Software license.          *
 24 // ********************************************************************
 25 //
 26 // INCL++ intra-nuclear cascade model
 27 // Alain Boudard, CEA-Saclay, France
 28 // Joseph Cugnon, University of Liege, Belgium
 29 // Jean-Christophe David, CEA-Saclay, France
 30 // Pekka Kaitaniemi, CEA-Saclay, France, and Helsinki Institute of Physics, Finland
 31 // Sylvie Leray, CEA-Saclay, France
 32 // Davide Mancusi, CEA-Saclay, France
 33 //
 34 #define INCLXX_IN_GEANT4_MODE 1
 35 
 36 #include "globals.hh"
 37 
 38 #include <cmath>
 39 
 40 #include "G4PhysicalConstants.hh"
 41 #include "G4SystemOfUnits.hh"
 42 #include "G4INCLXXInterface.hh"
 43 #include "G4GenericIon.hh"
 44 #include "G4INCLCascade.hh"
 45 #include "G4ReactionProductVector.hh"
 46 #include "G4ReactionProduct.hh"
 47 #include "G4HadSecondary.hh"
 48 #include "G4ParticleTable.hh"
 49 #include "G4INCLXXInterfaceStore.hh"
 50 #include "G4INCLXXVInterfaceTally.hh"
 51 #include "G4String.hh"
 52 #include "G4PhysicalConstants.hh"
 53 #include "G4SystemOfUnits.hh"
 54 #include "G4HadronicInteractionRegistry.hh"
 55 #include "G4INCLVersion.hh"
 56 #include "G4VEvaporation.hh"
 57 #include "G4VEvaporationChannel.hh"
 58 #include "G4CompetitiveFission.hh"
 59 #include "G4FissionLevelDensityParameterINCLXX.hh"
 60 #include "G4PhysicsModelCatalog.hh"
 61 
 62 #include "G4HyperNucleiProperties.hh"
 63 #include "G4HyperTriton.hh"
 64 #include "G4HyperH4.hh"
 65 #include "G4HyperAlpha.hh"
 66 #include "G4DoubleHyperH4.hh"
 67 #include "G4DoubleHyperDoubleNeutron.hh"
 68 #include "G4HyperHe5.hh"
 69 
 70 G4INCLXXInterface::G4INCLXXInterface(G4VPreCompoundModel * const aPreCompound) :
 71   G4VIntraNuclearTransportModel(G4INCLXXInterfaceStore::GetInstance()->getINCLXXVersionName()),
 72   theINCLModel(NULL),
 73   thePreCompoundModel(aPreCompound),
 74   theInterfaceStore(G4INCLXXInterfaceStore::GetInstance()),
 75   theTally(NULL),
 76   complainedAboutBackupModel(false),
 77   complainedAboutPreCompound(false),
 78   theIonTable(G4IonTable::GetIonTable()),
 79   theINCLXXLevelDensity(NULL),
 80   theINCLXXFissionProbability(NULL),
 81   secID(-1)
 82 {
 83   if(!thePreCompoundModel) {
 84     G4HadronicInteraction* p =
 85       G4HadronicInteractionRegistry::Instance()->FindModel("PRECO");
 86     thePreCompoundModel = static_cast<G4VPreCompoundModel*>(p);
 87     if(!thePreCompoundModel) { thePreCompoundModel = new G4PreCompoundModel; }
 88   }
 89 
 90   // Use the environment variable G4INCLXX_NO_DE_EXCITATION to disable de-excitation
 91   if(std::getenv("G4INCLXX_NO_DE_EXCITATION")) {
 92     G4String message = "de-excitation is completely disabled!";
 93     theInterfaceStore->EmitWarning(message);
 94     theDeExcitation = 0;
 95   } else {
 96     G4HadronicInteraction* p =
 97       G4HadronicInteractionRegistry::Instance()->FindModel("PRECO");
 98     theDeExcitation = static_cast<G4VPreCompoundModel*>(p);
 99     if(!theDeExcitation) { theDeExcitation = new G4PreCompoundModel; }
100 
101     // set the fission parameters for G4ExcitationHandler
102     G4VEvaporationChannel * const theFissionChannel =
103       theDeExcitation->GetExcitationHandler()->GetEvaporation()->GetFissionChannel();
104     G4CompetitiveFission * const theFissionChannelCast = dynamic_cast<G4CompetitiveFission *>(theFissionChannel);
105     if(theFissionChannelCast) {
106       theINCLXXLevelDensity = new G4FissionLevelDensityParameterINCLXX;
107       theFissionChannelCast->SetLevelDensityParameter(theINCLXXLevelDensity);
108       theINCLXXFissionProbability = new G4FissionProbability;
109       theINCLXXFissionProbability->SetFissionLevelDensityParameter(theINCLXXLevelDensity);
110       theFissionChannelCast->SetEmissionStrategy(theINCLXXFissionProbability);
111       theInterfaceStore->EmitBigWarning("INCL++/G4ExcitationHandler uses its own level-density parameter for fission");
112     } else {
113       theInterfaceStore->EmitBigWarning("INCL++/G4ExcitationHandler could not use its own level-density parameter for fission");
114     }
115   }
116 
117   // use the envvar G4INCLXX_DUMP_REMNANT to dump information about the
118   // remnants on stdout
119   if(std::getenv("G4INCLXX_DUMP_REMNANT"))
120     dumpRemnantInfo = true;
121   else
122     dumpRemnantInfo = false;
123 
124   theBackupModel = new G4BinaryLightIonReaction;
125   theBackupModelNucleon = new G4BinaryCascade;
126   secID = G4PhysicsModelCatalog::GetModelID( "model_INCLXXCascade" );
127 }
128 
129 G4INCLXXInterface::~G4INCLXXInterface()
130 {
131   delete theINCLXXLevelDensity;
132   delete theINCLXXFissionProbability;
133 }
134 
135 G4bool G4INCLXXInterface::AccurateProjectile(const G4HadProjectile &aTrack, const G4Nucleus &theNucleus) const {
136   // Use direct kinematics if the projectile is a nucleon or a pion
137   const G4ParticleDefinition *projectileDef = aTrack.GetDefinition();
138   if(std::abs(projectileDef->GetBaryonNumber()) < 2) // Every non-composite particle. abs()-> in case of anti-nucleus projectile
139     return false;
140 
141   // Here all projectiles should be nuclei
142   const G4int pA = projectileDef->GetAtomicMass();
143   if(pA<=0) {
144     std::stringstream ss;
145     ss << "the model does not know how to handle a collision between a "
146       << projectileDef->GetParticleName() << " projectile and a Z="
147       << theNucleus.GetZ_asInt() << ", A=" << theNucleus.GetA_asInt();
148     theInterfaceStore->EmitBigWarning(ss.str());
149     return true;
150   }
151 
152   // If either nucleus is a LCP (A<=4), run the collision as light on heavy
153   const G4int tA = theNucleus.GetA_asInt();
154   if(tA<=4 || pA<=4) {
155     if(pA<tA)
156       return false;
157     else
158       return true;
159   }
160 
161   // If one of the nuclei is heavier than theMaxProjMassINCL, run the collision
162   // as light on heavy.
163   // Note that here we are sure that either the projectile or the target is
164   // smaller than theMaxProjMass; otherwise theBackupModel would have been
165   // called.
166   const G4int theMaxProjMassINCL = theInterfaceStore->GetMaxProjMassINCL();
167   if(pA > theMaxProjMassINCL)
168     return true;
169   else if(tA > theMaxProjMassINCL)
170     return false;
171   else
172     // In all other cases, use the global setting
173     return theInterfaceStore->GetAccurateProjectile();
174 }
175 
176 G4HadFinalState* G4INCLXXInterface::ApplyYourself(const G4HadProjectile& aTrack, G4Nucleus& theNucleus)
177 {
178   G4ParticleDefinition const * const trackDefinition = aTrack.GetDefinition();
179   const G4bool isIonTrack = trackDefinition->GetParticleType()==G4GenericIon::GenericIon()->GetParticleType();
180   const G4int trackA = trackDefinition->GetAtomicMass();
181   const G4int trackZ = (G4int) trackDefinition->GetPDGCharge();
182   const G4int trackL = trackDefinition->GetNumberOfLambdasInHypernucleus();
183   const G4int nucleusA = theNucleus.GetA_asInt();
184   const G4int nucleusZ = theNucleus.GetZ_asInt();
185 
186   // For reactions induced by weird projectiles (e.g. He2), bail out
187   if((isIonTrack && ((trackZ<=0 && trackL==0) || trackA<=trackZ)) ||
188                     (nucleusA>1 && (nucleusZ<=0 || nucleusA<=nucleusZ))) {
189     theResult.Clear();
190     theResult.SetStatusChange(isAlive);
191     theResult.SetEnergyChange(aTrack.GetKineticEnergy());
192     theResult.SetMomentumChange(aTrack.Get4Momentum().vect().unit());
193     return &theResult;
194   }
195 
196   // For reactions on nucleons, use the backup model (without complaining),
197   // except for anti_proton projectile (in this case, INCLXX is used).
198   if(trackA<=1 && nucleusA<=1 && (trackZ>=0 || trackA==0)) {
199     return theBackupModelNucleon->ApplyYourself(aTrack, theNucleus);
200   }
201  
202   // For systems heavier than theMaxProjMassINCL, use another model (typically
203   // BIC)
204   const G4int theMaxProjMassINCL = theInterfaceStore->GetMaxProjMassINCL();
205   if(trackA > theMaxProjMassINCL && nucleusA > theMaxProjMassINCL) {
206     if(!complainedAboutBackupModel) {
207       complainedAboutBackupModel = true;
208       std::stringstream ss;
209       ss << "INCL++ refuses to handle reactions between nuclei with A>"
210         << theMaxProjMassINCL
211         << ". A backup model ("
212         << theBackupModel->GetModelName()
213         << ") will be used instead.";
214       theInterfaceStore->EmitBigWarning(ss.str());
215     }
216     return theBackupModel->ApplyYourself(aTrack, theNucleus);
217   }
218 
219   // For energies lower than cascadeMinEnergyPerNucleon, use PreCompound
220   const G4double cascadeMinEnergyPerNucleon = theInterfaceStore->GetCascadeMinEnergyPerNucleon();
221   const G4double trackKinE = aTrack.GetKineticEnergy();
222   if((trackDefinition==G4Neutron::NeutronDefinition() || trackDefinition==G4Proton::ProtonDefinition())
223       && trackKinE < cascadeMinEnergyPerNucleon) {
224     if(!complainedAboutPreCompound) {
225       complainedAboutPreCompound = true;
226       std::stringstream ss;
227       ss << "INCL++ refuses to handle nucleon-induced reactions below "
228         << cascadeMinEnergyPerNucleon / MeV
229         << " MeV. A PreCoumpound model ("
230         << thePreCompoundModel->GetModelName()
231         << ") will be used instead.";
232       theInterfaceStore->EmitBigWarning(ss.str());
233     }
234     return thePreCompoundModel->ApplyYourself(aTrack, theNucleus);
235   }
236 
237   // Calculate the total four-momentum in the entrance channel
238   const G4double theNucleusMass = theIonTable->GetIonMass(nucleusZ, nucleusA);
239   const G4double theTrackMass = trackDefinition->GetPDGMass();
240   const G4double theTrackEnergy = trackKinE + theTrackMass;
241   const G4double theTrackMomentumAbs2 = theTrackEnergy*theTrackEnergy - theTrackMass*theTrackMass;
242   const G4double theTrackMomentumAbs = ((theTrackMomentumAbs2>0.0) ? std::sqrt(theTrackMomentumAbs2) : 0.0);
243   const G4ThreeVector theTrackMomentum = aTrack.Get4Momentum().getV().unit() * theTrackMomentumAbs;
244 
245   G4LorentzVector goodTrack4Momentum(theTrackMomentum, theTrackEnergy);
246   G4LorentzVector fourMomentumIn;
247   fourMomentumIn.setE(theTrackEnergy + theNucleusMass);
248   fourMomentumIn.setVect(theTrackMomentum);
249 
250   // Check if inverse kinematics should be used
251   const G4bool inverseKinematics = AccurateProjectile(aTrack, theNucleus);
252 
253   // If we are running in inverse kinematics, redefine aTrack and theNucleus
254   G4LorentzRotation *toInverseKinematics = NULL;
255   G4LorentzRotation *toDirectKinematics = NULL;
256   G4HadProjectile const *aProjectileTrack = &aTrack;
257   G4Nucleus *theTargetNucleus = &theNucleus;
258   if(inverseKinematics) {
259     G4ParticleDefinition *oldTargetDef = theIonTable->GetIon(nucleusZ, nucleusA, 0);
260     const G4ParticleDefinition *oldProjectileDef = trackDefinition;
261 
262     if(oldProjectileDef != 0 && oldTargetDef != 0) {
263       const G4int newTargetA = oldProjectileDef->GetAtomicMass();
264       const G4int newTargetZ = oldProjectileDef->GetAtomicNumber();
265       const G4int newTargetL = oldProjectileDef->GetNumberOfLambdasInHypernucleus();
266       if(newTargetA > 0 && newTargetZ > 0) {
267         // This should give us the same energy per nucleon
268         theTargetNucleus = new G4Nucleus(newTargetA, newTargetZ, newTargetL);
269         toInverseKinematics = new G4LorentzRotation(goodTrack4Momentum.boostVector());
270         G4LorentzVector theProjectile4Momentum(0.0, 0.0, 0.0, theNucleusMass);
271         G4DynamicParticle swappedProjectileParticle(oldTargetDef, (*toInverseKinematics) * theProjectile4Momentum);
272         aProjectileTrack = new G4HadProjectile(swappedProjectileParticle);
273       } else {
274         G4String message = "badly defined target after swapping. Falling back to normal (non-swapped) mode.";
275         theInterfaceStore->EmitWarning(message);
276         toInverseKinematics = new G4LorentzRotation;
277       }
278     } else {
279       G4String message = "oldProjectileDef or oldTargetDef was null";
280       theInterfaceStore->EmitWarning(message);
281       toInverseKinematics = new G4LorentzRotation;
282     }
283   }
284 
285   // INCL assumes the projectile particle is going in the direction of
286   // the Z-axis. Here we construct proper rotation to convert the
287   // momentum vectors of the outcoming particles to the original
288   // coordinate system.
289   G4LorentzVector projectileMomentum = aProjectileTrack->Get4Momentum();
290 
291   // INCL++ assumes that the projectile is going in the direction of
292   // the z-axis. In principle, if the coordinate system used by G4
293   // hadronic framework is defined differently we need a rotation to
294   // transform the INCL++ reaction products to the appropriate
295   // frame. Please note that it isn't necessary to apply this
296   // transform to the projectile because when creating the INCL++
297   // projectile particle; \see{toINCLKineticEnergy} needs to use only the
298   // projectile energy (direction is simply assumed to be along z-axis).
299   G4RotationMatrix toZ;
300   toZ.rotateZ(-projectileMomentum.phi());
301   toZ.rotateY(-projectileMomentum.theta());
302   G4RotationMatrix toLabFrame3 = toZ.inverse();
303   G4LorentzRotation toLabFrame(toLabFrame3);
304   // However, it turns out that the projectile given to us by G4
305   // hadronic framework is already going in the direction of the
306   // z-axis so this rotation is actually unnecessary. Both toZ and
307   // toLabFrame turn out to be unit matrices as can be seen by
308   // uncommenting the folowing two lines:
309   // G4cout <<"toZ = " << toZ << G4endl;
310   // G4cout <<"toLabFrame = " << toLabFrame << G4endl;
311 
312   theResult.Clear(); // Make sure the output data structure is clean.
313   theResult.SetStatusChange(stopAndKill);
314 
315   std::list<G4Fragment> remnants;
316 
317   const G4int maxTries = 200;
318   G4int nTries = 0;
319   // INCL can generate transparent events. However, this is meaningful
320   // only in the standalone code. In Geant4 we must "force" INCL to
321   // produce a valid cascade.
322   G4bool eventIsOK = false;
323   do {
324     const G4INCL::ParticleSpecies theSpecies = toINCLParticleSpecies(*aProjectileTrack);
325     const G4double kineticEnergy = toINCLKineticEnergy(*aProjectileTrack);
326 
327     // The INCL model will be created at the first use
328     theINCLModel = G4INCLXXInterfaceStore::GetInstance()->GetINCLModel();
329 
330     const G4INCL::EventInfo eventInfo = theINCLModel->processEvent(theSpecies, kineticEnergy,
331                    theTargetNucleus->GetA_asInt(),
332                    theTargetNucleus->GetZ_asInt(),
333                    -theTargetNucleus->GetL());  // Strangeness has opposite sign
334     //    eventIsOK = !eventInfo.transparent && nTries < maxTries;                              // of the number of Lambdas 
335     eventIsOK = !eventInfo.transparent;
336     if(eventIsOK) {
337 
338       // If the collision was run in inverse kinematics, prepare to boost back
339       // all the reaction products
340       if(inverseKinematics) {
341         toDirectKinematics = new G4LorentzRotation(toInverseKinematics->inverse());
342       }
343 
344       G4LorentzVector fourMomentumOut;
345 
346       for(G4int i = 0; i < eventInfo.nParticles; ++i) {
347         G4int A = eventInfo.A[i];
348         G4int Z = eventInfo.Z[i];
349         G4int S = eventInfo.S[i];  // Strangeness
350         G4int PDGCode = eventInfo.PDGCode[i];
351         G4double kinE = eventInfo.EKin[i];
352         G4double px = eventInfo.px[i];
353         G4double py = eventInfo.py[i];
354         G4double pz = eventInfo.pz[i];
355         G4DynamicParticle *p = toG4Particle(A, Z, S, PDGCode, kinE, px, py, pz);
356         if(p != 0) {
357           G4LorentzVector momentum = p->Get4Momentum();
358 
359           // Apply the toLabFrame rotation
360           momentum *= toLabFrame;
361           // Apply the inverse-kinematics boost
362           if(inverseKinematics) {
363             momentum *= *toDirectKinematics;
364             momentum.setVect(-momentum.vect());
365           }
366 
367     // Set the four-momentum of the reaction products
368           p->Set4Momentum(momentum);
369           fourMomentumOut += momentum;
370 
371     // Propagate the particle's parent resonance information
372           G4HadSecondary secondary(p, 1.0, secID);
373           G4ParticleDefinition* parentResonanceDef = nullptr;
374           if ( eventInfo.parentResonancePDGCode[i] != 0 ) {
375             parentResonanceDef = G4ParticleTable::GetParticleTable()->FindParticle(eventInfo.parentResonancePDGCode[i]);
376           }
377           secondary.SetParentResonanceDef(parentResonanceDef);
378           secondary.SetParentResonanceID(eventInfo.parentResonanceID[i]);
379     
380           theResult.AddSecondary(secondary);
381 
382         } else {
383           G4String message = "the model produced a particle that couldn't be converted to Geant4 particle.";
384           theInterfaceStore->EmitWarning(message);
385         }
386       }
387 
388       for(G4int i = 0; i < eventInfo.nRemnants; ++i) {
389         const G4int A = eventInfo.ARem[i];
390         const G4int Z = eventInfo.ZRem[i];
391         const G4int S = eventInfo.SRem[i];
392         // Check that the remnant is a physical bound state: if not, resample the collision.
393         if(( Z == 0  &&  S == 0  &&  A > 1 ) ||                 // No bound states for nn, nnn, nnnn, ...
394            ( Z == 0  &&  S != 0  &&  A < 4 ) ||                 // No bound states for nl, ll, nnl, nll, lll
395            ( Z != 0  &&  S != 0  &&  A == Z + std::abs(S) )) {  // No bound states for pl, ppl, pll, ...
396           std::stringstream ss;
397           ss << "unphysical residual fragment : Z=" << Z << "  S=" << S << "  A=" << A 
398              << "  skipping it and resampling the collision";
399           theInterfaceStore->EmitWarning(ss.str());
400           eventIsOK = false;
401           continue;
402         }
403         const G4double kinE = eventInfo.EKinRem[i];
404         const G4double px = eventInfo.pxRem[i];
405         const G4double py = eventInfo.pyRem[i];
406         const G4double pz = eventInfo.pzRem[i];
407         G4ThreeVector spin(
408             eventInfo.jxRem[i]*hbar_Planck,
409             eventInfo.jyRem[i]*hbar_Planck,
410             eventInfo.jzRem[i]*hbar_Planck
411             );
412         const G4double excitationE = eventInfo.EStarRem[i];
413         G4double nuclearMass = excitationE;
414         if ( S == 0 ) {
415           nuclearMass += G4NucleiProperties::GetNuclearMass(A, Z);
416         } else {
417     // Assumed that the opposite of the strangeness of the remnant gives the number of Lambdas inside it
418           nuclearMass += G4HyperNucleiProperties::GetNuclearMass(A, Z, std::abs(S));
419         }
420         const G4double scaling = remnant4MomentumScaling(nuclearMass, kinE, px, py, pz);
421         G4LorentzVector fourMomentum(scaling * px, scaling * py, scaling * pz,
422              nuclearMass + kinE);
423         if(std::abs(scaling - 1.0) > 0.01) {
424           std::stringstream ss;
425           ss << "momentum scaling = " << scaling
426              << "\n                Lorentz vector = " << fourMomentum
427              << ")\n                A = " << A << ", Z = " << Z << ", S = " << S
428              << "\n                E* = " << excitationE << ", nuclearMass = " << nuclearMass
429              << "\n                remnant i=" << i << ", nRemnants=" << eventInfo.nRemnants
430              << "\n                Reaction was: " << aTrack.GetKineticEnergy()/MeV
431              << "-MeV " << trackDefinition->GetParticleName() << " + "
432              << theIonTable->GetIonName(theNucleus.GetZ_asInt(), theNucleus.GetA_asInt(), 0)
433              << ", in " << (inverseKinematics ? "inverse" : "direct") << " kinematics.";
434           theInterfaceStore->EmitWarning(ss.str());
435         }
436 
437         // Apply the toLabFrame rotation
438         fourMomentum *= toLabFrame;
439         spin *= toLabFrame3;
440         // Apply the inverse-kinematics boost
441         if(inverseKinematics) {
442           fourMomentum *= *toDirectKinematics;
443           fourMomentum.setVect(-fourMomentum.vect());
444         }
445 
446         fourMomentumOut += fourMomentum;
447         G4Fragment remnant(A, Z, std::abs(S), fourMomentum);  // Assumed that -strangeness gives the number of Lambdas
448         remnant.SetAngularMomentum(spin);
449         remnant.SetCreatorModelID(secID);
450         if(dumpRemnantInfo) {
451           G4cerr << "G4INCLXX_DUMP_REMNANT: " << remnant << "  spin: " << spin << G4endl;
452         }
453         remnants.push_back(remnant);
454       }
455 
456       // Give up is the event is not ok (e.g. unphysical residual)
457       if(!eventIsOK) {
458         const G4int nSecondaries = (G4int)theResult.GetNumberOfSecondaries();
459         for(G4int j=0; j<nSecondaries; ++j) delete theResult.GetSecondary(j)->GetParticle();
460         theResult.Clear();
461         theResult.SetStatusChange(stopAndKill);
462         remnants.clear();
463       } else {
464         // Check four-momentum conservation
465         const G4LorentzVector violation4Momentum = fourMomentumOut - fourMomentumIn;
466         const G4double energyViolation = std::abs(violation4Momentum.e());
467         const G4double momentumViolation = violation4Momentum.rho();
468         if(energyViolation > G4INCLXXInterfaceStore::GetInstance()->GetConservationTolerance()) {
469           std::stringstream ss;
470           ss << "energy conservation violated by " << energyViolation/MeV << " MeV in "
471              << aTrack.GetKineticEnergy()/MeV << "-MeV " << trackDefinition->GetParticleName()
472              << " + " << theIonTable->GetIonName(theNucleus.GetZ_asInt(), theNucleus.GetA_asInt(), 0)
473              << " inelastic reaction, in " << (inverseKinematics ? "inverse" : "direct") << " kinematics. Will resample.";
474           theInterfaceStore->EmitWarning(ss.str());
475           eventIsOK = false;
476           const G4int nSecondaries = (G4int)theResult.GetNumberOfSecondaries();
477           for(G4int j=0; j<nSecondaries; ++j) delete theResult.GetSecondary(j)->GetParticle();
478           theResult.Clear();
479           theResult.SetStatusChange(stopAndKill);
480           remnants.clear();
481         } else if(momentumViolation > G4INCLXXInterfaceStore::GetInstance()->GetConservationTolerance()) {
482           std::stringstream ss;
483           ss << "momentum conservation violated by " << momentumViolation/MeV << " MeV in "
484              << aTrack.GetKineticEnergy()/MeV << "-MeV " << trackDefinition->GetParticleName()
485              << " + " << theIonTable->GetIonName(theNucleus.GetZ_asInt(), theNucleus.GetA_asInt(), 0)
486              << " inelastic reaction, in " << (inverseKinematics ? "inverse" : "direct") << " kinematics. Will resample.";
487           theInterfaceStore->EmitWarning(ss.str());
488           eventIsOK = false;
489           const G4int nSecondaries = (G4int)theResult.GetNumberOfSecondaries();
490           for(G4int j=0; j<nSecondaries; ++j) delete theResult.GetSecondary(j)->GetParticle();
491           theResult.Clear();
492           theResult.SetStatusChange(stopAndKill);
493           remnants.clear();
494         }
495       }
496     }
497     nTries++;
498   } while(!eventIsOK && nTries < maxTries); /* Loop checking, 10.07.2015, D.Mancusi */
499 
500   // Clean up the objects that we created for the inverse kinematics
501   if(inverseKinematics) {
502     delete aProjectileTrack;
503     delete theTargetNucleus;
504     delete toInverseKinematics;
505     delete toDirectKinematics;
506   }
507 
508   if(!eventIsOK) {
509     std::stringstream ss;
510     ss << "maximum number of tries exceeded for the proposed "
511     << aTrack.GetKineticEnergy()/MeV << "-MeV " << trackDefinition->GetParticleName()
512     << " + " << theIonTable->GetIonName(nucleusZ, nucleusA, 0)
513     << " inelastic reaction, in " << (inverseKinematics ? "inverse" : "direct") << " kinematics.";
514     theInterfaceStore->EmitWarning(ss.str());
515     theResult.SetStatusChange(isAlive);
516     theResult.SetEnergyChange(aTrack.GetKineticEnergy());
517     theResult.SetMomentumChange(aTrack.Get4Momentum().vect().unit());
518     return &theResult;
519   }
520 
521   // De-excitation:
522 
523   if(theDeExcitation != 0) {
524     for(std::list<G4Fragment>::iterator i = remnants.begin();
525   i != remnants.end(); ++i) {
526       G4ReactionProductVector *deExcitationResult = theDeExcitation->DeExcite((*i));
527 
528       for(G4ReactionProductVector::iterator fragment = deExcitationResult->begin();
529     fragment != deExcitationResult->end(); ++fragment) {
530   const G4ParticleDefinition *def = (*fragment)->GetDefinition();
531   if(def != 0) {
532     G4DynamicParticle *theFragment = new G4DynamicParticle(def, (*fragment)->GetMomentum());
533     theResult.AddSecondary(theFragment, (*fragment)->GetCreatorModelID());
534   }
535       }
536 
537       for(G4ReactionProductVector::iterator fragment = deExcitationResult->begin();
538     fragment != deExcitationResult->end(); ++fragment) {
539   delete (*fragment);
540       }
541       deExcitationResult->clear();
542       delete deExcitationResult;
543     }
544   }
545 
546   remnants.clear();
547 
548   if((theTally = theInterfaceStore->GetTally()))
549     theTally->Tally(aTrack, theNucleus, theResult);
550 
551   return &theResult;
552 }
553 
554 G4ReactionProductVector* G4INCLXXInterface::Propagate(G4KineticTrackVector* , G4V3DNucleus* ) {
555   return 0;
556 }
557 
558 G4INCL::ParticleType G4INCLXXInterface::toINCLParticleType(G4ParticleDefinition const * const pdef) const {
559   if(     pdef == G4Proton::Proton())               return G4INCL::Proton;
560   else if(pdef == G4Neutron::Neutron())             return G4INCL::Neutron;
561   else if(pdef == G4PionPlus::PionPlus())           return G4INCL::PiPlus;
562   else if(pdef == G4PionMinus::PionMinus())         return G4INCL::PiMinus;
563   else if(pdef == G4PionZero::PionZero())           return G4INCL::PiZero;
564   else if(pdef == G4KaonPlus::KaonPlus())           return G4INCL::KPlus;
565   else if(pdef == G4KaonZero::KaonZero())           return G4INCL::KZero;
566   else if(pdef == G4KaonMinus::KaonMinus())         return G4INCL::KMinus;
567   else if(pdef == G4AntiKaonZero::AntiKaonZero())   return G4INCL::KZeroBar;
568   // For K0L & K0S we do not take into account K0/K0B oscillations
569   else if(pdef == G4KaonZeroLong::KaonZeroLong())   return G4UniformRand() < 0.5 ? G4INCL::KZeroBar : G4INCL::KZero;
570   else if(pdef == G4KaonZeroShort::KaonZeroShort()) return G4UniformRand() < 0.5 ? G4INCL::KZeroBar : G4INCL::KZero; 
571   else if(pdef == G4Deuteron::Deuteron())           return G4INCL::Composite;
572   else if(pdef == G4Triton::Triton())               return G4INCL::Composite;
573   else if(pdef == G4He3::He3())                     return G4INCL::Composite;
574   else if(pdef == G4Alpha::Alpha())                 return G4INCL::Composite;
575   else if(pdef == G4AntiProton::AntiProton())       return G4INCL::antiProton;
576   else if(pdef->GetParticleType() == G4GenericIon::GenericIon()->GetParticleType()) return G4INCL::Composite;
577   else                                              return G4INCL::UnknownParticle;
578 }
579 
580 G4INCL::ParticleSpecies G4INCLXXInterface::toINCLParticleSpecies(G4HadProjectile const &aTrack) const {
581   const G4ParticleDefinition *pdef = aTrack.GetDefinition();
582   const G4INCL::ParticleType theType = toINCLParticleType(pdef);
583   if(theType!=G4INCL::Composite)
584     return G4INCL::ParticleSpecies(theType);
585   else {
586     G4INCL::ParticleSpecies theSpecies;
587     theSpecies.theType=theType;
588     theSpecies.theA=pdef->GetAtomicMass();
589     theSpecies.theZ=pdef->GetAtomicNumber();
590     return theSpecies;
591   }
592 }
593 
594 G4double G4INCLXXInterface::toINCLKineticEnergy(G4HadProjectile const &aTrack) const {
595   return aTrack.GetKineticEnergy();
596 }
597 
598 G4ParticleDefinition *G4INCLXXInterface::toG4ParticleDefinition(G4int A, G4int Z, G4int S, G4int PDGCode) const {
599   if       (PDGCode == 2212) { return G4Proton::Proton();
600   } else if(PDGCode == 2112) { return G4Neutron::Neutron();
601   } else if(PDGCode == 211)  { return G4PionPlus::PionPlus();
602   } else if(PDGCode == 111)  { return G4PionZero::PionZero();
603   } else if(PDGCode == -211) { return G4PionMinus::PionMinus();
604   
605   } else if(PDGCode == 221)  { return G4Eta::Eta();
606   } else if(PDGCode == 22)   { return G4Gamma::Gamma();
607   
608   } else if(PDGCode == 3122) { return G4Lambda::Lambda();
609   } else if(PDGCode == 3222) { return G4SigmaPlus::SigmaPlus();
610   } else if(PDGCode == 3212) { return G4SigmaZero::SigmaZero();
611   } else if(PDGCode == 3112) { return G4SigmaMinus::SigmaMinus();
612   } else if(PDGCode == 321)  { return G4KaonPlus::KaonPlus();
613   } else if(PDGCode == -321) { return G4KaonMinus::KaonMinus();
614   } else if(PDGCode == 130)  { return G4KaonZeroLong::KaonZeroLong();
615   } else if(PDGCode == 310)  { return G4KaonZeroShort::KaonZeroShort();
616   
617   } else if(PDGCode == 1002) { return G4Deuteron::Deuteron();
618   } else if(PDGCode == 1003) { return G4Triton::Triton();
619   } else if(PDGCode == 2003) { return G4He3::He3();
620   } else if(PDGCode == 2004) { return G4Alpha::Alpha();
621 
622   } else if(PDGCode == -2212) { return G4AntiProton::AntiProton();
623   } else if(S != 0) {  // Assumed that -S gives the number of Lambdas
624     if (A == 3 && Z == 1 && S == -1 ) return G4HyperTriton::Definition();
625     if (A == 4 && Z == 1 && S == -1 ) return G4HyperH4::Definition();
626     if (A == 4 && Z == 2 && S == -1 ) return G4HyperAlpha::Definition();
627     if (A == 4 && Z == 1 && S == -2 ) return G4DoubleHyperH4::Definition();
628     if (A == 4 && Z == 0 && S == -2 ) return G4DoubleHyperDoubleNeutron::Definition();
629     if (A == 5 && Z == 2 && S == -1 ) return G4HyperHe5::Definition();
630   } else if(A > 0 && Z > 0 && A > Z) { // Returns ground state ion definition.
631     return theIonTable->GetIon(Z, A, 0);
632   }
633   return 0; // Error, unrecognized particle
634 }
635 
636 G4DynamicParticle *G4INCLXXInterface::toG4Particle(G4int A, G4int Z, G4int S, G4int PDGCode,
637                G4double kinE, G4double px,
638                                                    G4double py, G4double pz) const {
639   const G4ParticleDefinition *def = toG4ParticleDefinition(A, Z, S, PDGCode);
640   if(def == 0) { // Check if we have a valid particle definition
641     return 0;
642   }
643   const G4double energy = kinE * MeV;
644   const G4ThreeVector momentum(px, py, pz);
645   const G4ThreeVector momentumDirection = momentum.unit();
646   G4DynamicParticle *p = new G4DynamicParticle(def, momentumDirection, energy);
647   return p;
648 }
649 
650 G4double G4INCLXXInterface::remnant4MomentumScaling(G4double mass,
651               G4double kineticE,
652               G4double px, G4double py,
653               G4double pz) const {
654   const G4double p2 = px*px + py*py + pz*pz;
655   if(p2 > 0.0) {
656     const G4double pnew2 = kineticE*kineticE + 2.0*kineticE*mass;
657     return std::sqrt(pnew2)/std::sqrt(p2);
658   } else {
659     return 1.0;
660   }
661 }
662 
663 void G4INCLXXInterface::ModelDescription(std::ostream& outFile) const {
664    outFile
665      << "The Liège Intranuclear Cascade (INCL++) is a model for reactions induced\n"
666      << "by nucleons, pions and light ion on any nucleus. The reaction is\n"
667      << "described as an avalanche of binary nucleon-nucleon collisions, which can\n"
668      << "lead to the emission of energetic particles and to the formation of an\n"
669      << "excited thermalised nucleus (remnant). The de-excitation of the remnant is\n"
670      << "outside the scope of INCL++ and is typically described by another model.\n\n"
671      << "INCL++ has been reasonably well tested for nucleon (~50 MeV to ~15 GeV),\n"
672      << "pion (idem) and light-ion projectiles (up to A=18, ~10A MeV to 1A GeV).\n"
673      << "Most tests involved target nuclei close to the stability valley, with\n"
674      << "numbers between 4 and 250.\n\n"
675      << "Reference: D. Mancusi et al., Phys. Rev. C90 (2014) 054602\n\n";
676 }
677 
678 G4String const &G4INCLXXInterface::GetDeExcitationModelName() const {
679   return theDeExcitation->GetModelName();
680 }
681