Geant4  9.6.p02
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
G4INCLXXInterface.cc
Go to the documentation of this file.
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 // Pekka Kaitaniemi, CEA and Helsinki Institute of Physics
28 // Davide Mancusi, CEA
29 // Alain Boudard, CEA
30 // Sylvie Leray, CEA
31 // Joseph Cugnon, University of Liege
32 //
33 #define INCLXX_IN_GEANT4_MODE 1
34 
35 #include "globals.hh"
36 
37 #include <cmath>
38 
39 #include "G4PhysicalConstants.hh"
40 #include "G4SystemOfUnits.hh"
41 #include "G4INCLXXInterface.hh"
42 #include "G4GenericIon.hh"
43 #include "G4INCLCascade.hh"
45 #include "G4ReactionProduct.hh"
47 #include "G4String.hh"
48 
51  theINCLModel(NULL),
52  theInterfaceStore(G4INCLXXInterfaceStore::GetInstance()),
53  complainedAboutBackupModel(false)
54 {
55  // Use the environment variable G4INCLXX_NO_DE_EXCITATION to disable de-excitation
56  if(getenv("G4INCLXX_NO_DE_EXCITATION")) {
57  G4String message = "de-excitation is completely disabled!";
58  theInterfaceStore->EmitWarning(message);
59  theExcitationHandler = 0;
60  } else {
61  theExcitationHandler = new G4ExcitationHandler;
62  }
63 
64  theBackupModel = new G4BinaryLightIonReaction;
65 }
66 
68 {
69  delete theBackupModel;
70  delete theExcitationHandler;
71 }
72 
73 G4bool G4INCLXXInterface::AccurateProjectile(const G4HadProjectile &aTrack, const G4Nucleus &theNucleus) const {
74  // Use direct kinematics if the projectile is a nucleon or a pion
75  const G4ParticleDefinition *projectileDef = aTrack.GetDefinition();
76  if(projectileDef == G4Proton::Proton()
77  || projectileDef == G4Neutron::Neutron()
78  || projectileDef == G4PionPlus::PionPlus()
79  || projectileDef == G4PionZero::PionZero()
80  || projectileDef == G4PionMinus::PionMinus())
81  return false;
82 
83  // Here all projectiles should be nuclei
84  const G4int pA = projectileDef->GetAtomicMass();
85  if(pA<=0) {
86  std::stringstream ss;
87  ss << "the model does not know how to handle a collision between a "
88  << projectileDef->GetParticleName() << " projectile and a Z="
89  << theNucleus.GetZ_asInt() << ", A=" << theNucleus.GetA_asInt();
90  theInterfaceStore->EmitWarning(ss.str());
91  return true;
92  }
93 
94  // If either nucleus is a LCP (A<=4), run the collision as light on heavy
95  const G4int tA = theNucleus.GetA_asInt();
96  if(tA<=4 || pA<=4) {
97  if(pA<tA)
98  return false;
99  else
100  return true;
101  }
102 
103  // If one of the nuclei is heavier than theMaxProjMassINCL, run the collision
104  // as light on heavy.
105  // Note that here we are sure that either the projectile or the target is
106  // smaller than theMaxProjMass; otherwise theBackupModel would have been
107  // called.
108  const G4int theMaxProjMassINCL = theInterfaceStore->GetMaxProjMassINCL();
109  if(pA > theMaxProjMassINCL)
110  return true;
111  else if(tA > theMaxProjMassINCL)
112  return false;
113  else
114  // In all other cases, use the global setting
115  return theInterfaceStore->GetAccurateProjectile();
116 }
117 
119 {
120  // For systems heavier than theMaxProjMassINCL, use another model (typically
121  // BIC)
122  const G4int theMaxProjMassINCL = theInterfaceStore->GetMaxProjMassINCL();
123  if(aTrack.GetDefinition()->GetAtomicMass() > theMaxProjMassINCL
124  && theNucleus.GetA_asInt() > theMaxProjMassINCL) {
125  if(!complainedAboutBackupModel) {
126  complainedAboutBackupModel = true;
127  std::stringstream ss;
128  ss << "INCL++ refuses to handle reactions between nuclei with A>"
129  << theMaxProjMassINCL
130  << ". A backup model ("
131  << theBackupModel->GetModelName()
132  << ") will be used instead.";
133  G4cout << "[INCL++] Warning: " << ss.str() << G4endl;
134  }
135  return theBackupModel->ApplyYourself(aTrack, theNucleus);
136  }
137 
138  const G4int maxTries = 200;
139 
140  // Check if inverse kinematics should be used
141  const G4bool inverseKinematics = AccurateProjectile(aTrack, theNucleus);
142 
143  // If we are running in inverse kinematics, redefine aTrack and theNucleus
144  G4LorentzRotation *toInverseKinematics = NULL;
145  G4LorentzRotation *toDirectKinematics = NULL;
146  G4HadProjectile const *aProjectileTrack = &aTrack;
147  G4Nucleus *theTargetNucleus = &theNucleus;
148  if(inverseKinematics) {
149  G4ParticleTable * const theParticleTable = G4ParticleTable::GetParticleTable();
150  const G4int oldTargetA = theNucleus.GetA_asInt();
151  const G4int oldTargetZ = theNucleus.GetZ_asInt();
152  G4ParticleDefinition *oldTargetDef = theParticleTable->GetIon(oldTargetZ, oldTargetA, 0.0);
153  const G4ParticleDefinition *oldProjectileDef = aTrack.GetDefinition();
154 
155  if(oldProjectileDef != 0 && oldTargetDef != 0) {
156  const G4int newTargetA = oldProjectileDef->GetAtomicMass();
157  const G4int newTargetZ = oldProjectileDef->GetAtomicNumber();
158 
159  if(newTargetA > 0 && newTargetZ > 0) {
160  // This should give us the same energy per nucleon
161  theTargetNucleus = new G4Nucleus(newTargetA, newTargetZ);
162  const G4double theProjectileMass = theParticleTable->GetIonTable()->GetIonMass(oldTargetZ, oldTargetA);
163  toInverseKinematics = new G4LorentzRotation(aTrack.Get4Momentum().boostVector());
164  G4LorentzVector theProjectile4Momentum(0.0, 0.0, 0.0, theProjectileMass);
165  G4DynamicParticle swappedProjectileParticle(oldTargetDef, (*toInverseKinematics) * theProjectile4Momentum);
166  aProjectileTrack = new G4HadProjectile(swappedProjectileParticle);
167  } else {
168  G4String message = "badly defined target after swapping. Falling back to normal (non-swapped) mode.";
169  theInterfaceStore->EmitWarning(message);
170  toInverseKinematics = new G4LorentzRotation;
171  }
172  } else {
173  G4String message = "oldProjectileDef or oldTargetDef was null";
174  theInterfaceStore->EmitWarning(message);
175  toInverseKinematics = new G4LorentzRotation;
176  }
177  }
178 
179  // INCL assumes the projectile particle is going in the direction of
180  // the Z-axis. Here we construct proper rotation to convert the
181  // momentum vectors of the outcoming particles to the original
182  // coordinate system.
183  G4LorentzVector projectileMomentum = aProjectileTrack->Get4Momentum();
184 
185  // INCL++ assumes that the projectile is going in the direction of
186  // the z-axis. In principle, if the coordinate system used by G4
187  // hadronic framework is defined differently we need a rotation to
188  // transform the INCL++ reaction products to the appropriate
189  // frame. Please note that it isn't necessary to apply this
190  // transform to the projectile because when creating the INCL++
191  // projectile particle; \see{toINCLKineticEnergy} needs to use only the
192  // projectile energy (direction is simply assumed to be along z-axis).
193  G4RotationMatrix toZ;
194  toZ.rotateZ(-projectileMomentum.phi());
195  toZ.rotateY(-projectileMomentum.theta());
196  G4RotationMatrix toLabFrame3 = toZ.inverse();
197  G4LorentzRotation toLabFrame(toLabFrame3);
198  // However, it turns out that the projectile given to us by G4
199  // hadronic framework is already going in the direction of the
200  // z-axis so this rotation is actually unnecessary. Both toZ and
201  // toLabFrame turn out to be unit matrices as can be seen by
202  // uncommenting the folowing two lines:
203  // G4cout <<"toZ = " << toZ << G4endl;
204  // G4cout <<"toLabFrame = " << toLabFrame << G4endl;
205 
206  theResult.Clear(); // Make sure the output data structure is clean.
207  theResult.SetStatusChange(stopAndKill);
208 
209  std::list<G4Fragment> remnants;
210 
211  G4int nTries = 0;
212  // INCL can generate transparent events. However, this is meaningful
213  // only in the standalone code. In Geant4 we must "force" INCL to
214  // produce a valid cascade.
215  G4bool eventIsOK = false;
216  do {
217  const G4INCL::ParticleSpecies theSpecies = toINCLParticleSpecies(*aProjectileTrack);
218  const G4double kineticEnergy = toINCLKineticEnergy(*aProjectileTrack);
219 
220  // The INCL model will be created at the first use
222 
223  if(theInterfaceStore->GetDumpInput()) {
224  G4cout << theINCLModel->configToString() << G4endl;
225  }
226  const G4INCL::EventInfo eventInfo = theINCLModel->processEvent(theSpecies, kineticEnergy, theTargetNucleus->GetA_asInt(), theTargetNucleus->GetZ_asInt());
227  // eventIsOK = !eventInfo.transparent && nTries < maxTries;
228  eventIsOK = !eventInfo.transparent;
229  if(eventIsOK) {
230 
231  // If the collision was run in inverse kinematics, prepare to boost back
232  // all the reaction products
233  if(inverseKinematics) {
234  toDirectKinematics = new G4LorentzRotation(toInverseKinematics->inverse());
235  }
236 
237  for(G4int i = 0; i < eventInfo.nParticles; i++) {
238  G4int A = eventInfo.A[i];
239  G4int Z = eventInfo.Z[i];
240  // G4cout <<"INCL particle A = " << A << " Z = " << Z << G4endl;
241  G4double kinE = eventInfo.EKin[i];
242  G4double px = eventInfo.px[i];
243  G4double py = eventInfo.py[i];
244  G4double pz = eventInfo.pz[i];
245  G4DynamicParticle *p = toG4Particle(A, Z , kinE, px, py, pz);
246  if(p != 0) {
247  G4LorentzVector momentum = p->Get4Momentum();
248 
249  // Apply the toLabFrame rotation
250  momentum *= toLabFrame;
251  // Apply the inverse-kinematics boost
252  if(inverseKinematics) {
253  momentum *= *toDirectKinematics;
254  momentum.setVect(-momentum.vect());
255  }
256 
257  // Set the four-momentum of the reaction products
258  p->Set4Momentum(momentum);
259  theResult.AddSecondary(p);
260 
261  } else {
262  G4String message = "the model produced a particle that couldn't be converted to Geant4 particle.";
263  theInterfaceStore->EmitWarning(message);
264  }
265  }
266 
267  for(G4int i = 0; i < eventInfo.nRemnants; i++) {
268  const G4int A = eventInfo.ARem[i];
269  const G4int Z = eventInfo.ZRem[i];
270  // G4cout <<"INCL particle A = " << A << " Z = " << Z << G4endl;
271  const G4double kinE = eventInfo.EKinRem[i];
272  const G4double px = eventInfo.pxRem[i];
273  const G4double py = eventInfo.pyRem[i];
274  const G4double pz = eventInfo.pzRem[i];
275  G4ThreeVector spin(
276  eventInfo.jxRem[i]*hbar_Planck,
277  eventInfo.jyRem[i]*hbar_Planck,
278  eventInfo.jzRem[i]*hbar_Planck
279  );
280  const G4double excitationE = eventInfo.EStarRem[i];
281  const G4double nuclearMass = G4NucleiProperties::GetNuclearMass(A, Z) + excitationE;
282  const G4double scaling = remnant4MomentumScaling(nuclearMass,
283  kinE,
284  px, py, pz);
285  G4LorentzVector fourMomentum(scaling * px, scaling * py, scaling * pz,
286  nuclearMass + kinE);
287  if(std::abs(scaling - 1.0) > 0.01) {
288  std::stringstream ss;
289  ss << "momentum scaling = " << scaling
290  << "\n Lorentz vector = " << fourMomentum
291  << ")\n A = " << A << ", Z = " << Z
292  << "\n E* = " << excitationE << ", nuclearMass = " << nuclearMass
293  << "\n remnant i=" << i << ", nRemnants=" << eventInfo.nRemnants
294  << "\n Reaction was: " << aTrack.GetKineticEnergy()/MeV
295  << "-MeV " << aTrack.GetDefinition()->GetParticleName() << " + "
296  << G4ParticleTable::GetParticleTable()->GetIon(theNucleus.GetZ_asInt(), theNucleus.GetA_asInt(), 0.0)->GetParticleName()
297  << ", in " << (inverseKinematics ? "inverse" : "direct") << " kinematics.";
298  theInterfaceStore->EmitWarning(ss.str());
299  }
300 
301  // Apply the toLabFrame rotation
302  fourMomentum *= toLabFrame;
303  spin *= toLabFrame3;
304  // Apply the inverse-kinematics boost
305  if(inverseKinematics) {
306  fourMomentum *= *toDirectKinematics;
307  fourMomentum.setVect(-fourMomentum.vect());
308  }
309 
310  G4Fragment remnant(A, Z, fourMomentum);
311  remnant.SetAngularMomentum(spin);
312  remnants.push_back(remnant);
313  }
314  }
315  nTries++;
316  } while(!eventIsOK && nTries < maxTries);
317 
318  // Clean up the objects that we created for the inverse kinematics
319  if(inverseKinematics) {
320  delete aProjectileTrack;
321  delete theTargetNucleus;
322  delete toInverseKinematics;
323  delete toDirectKinematics;
324  }
325 
326  if(!eventIsOK) {
327  std::stringstream ss;
328  ss << "maximum number of tries exceeded for the proposed "
329  << aTrack.GetKineticEnergy()/MeV << "-MeV " << aTrack.GetDefinition()->GetParticleName()
330  << " + " << G4ParticleTable::GetParticleTable()->GetIon(theNucleus.GetZ_asInt(), theNucleus.GetA_asInt(), 0.0)->GetParticleName()
331  << " inelastic reaction, in " << (inverseKinematics ? "inverse" : "direct") << " kinematics.";
332  theInterfaceStore->EmitWarning(ss.str());
333  theResult.SetStatusChange(isAlive);
334  theResult.SetEnergyChange(aTrack.GetKineticEnergy());
335  theResult.SetMomentumChange(aTrack.Get4Momentum().vect().unit());
336  return &theResult;
337  }
338 
339  // De-excitation:
340 
341  if(theExcitationHandler != 0) {
342  for(std::list<G4Fragment>::const_iterator i = remnants.begin();
343  i != remnants.end(); i++) {
344  G4ReactionProductVector *deExcitationResult = theExcitationHandler->BreakItUp((*i));
345 
346  for(G4ReactionProductVector::iterator fragment = deExcitationResult->begin();
347  fragment != deExcitationResult->end(); ++fragment) {
348  G4ParticleDefinition *def = (*fragment)->GetDefinition();
349  if(def != 0) {
350  G4DynamicParticle *theFragment = new G4DynamicParticle(def, (*fragment)->GetMomentum());
351  theResult.AddSecondary(theFragment);
352  }
353  }
354 
355  for(G4ReactionProductVector::iterator fragment = deExcitationResult->begin();
356  fragment != deExcitationResult->end(); ++fragment) {
357  delete (*fragment);
358  }
359  deExcitationResult->clear();
360  delete deExcitationResult;
361  }
362  }
363 
364  remnants.clear();
365 
366  return &theResult;
367 }
368 
370  return 0;
371 }
372 
373 G4INCL::ParticleType G4INCLXXInterface::toINCLParticleType(G4ParticleDefinition const * const pdef) const {
374  if( pdef == G4Proton::Proton()) return G4INCL::Proton;
375  else if(pdef == G4Neutron::Neutron()) return G4INCL::Neutron;
376  else if(pdef == G4PionPlus::PionPlus()) return G4INCL::PiPlus;
377  else if(pdef == G4PionMinus::PionMinus()) return G4INCL::PiMinus;
378  else if(pdef == G4PionZero::PionZero()) return G4INCL::PiZero;
379  else if(pdef == G4Deuteron::Deuteron()) return G4INCL::Composite;
380  else if(pdef == G4Triton::Triton()) return G4INCL::Composite;
381  else if(pdef == G4He3::He3()) return G4INCL::Composite;
382  else if(pdef == G4Alpha::Alpha()) return G4INCL::Composite;
384  else return G4INCL::UnknownParticle;
385 }
386 
387 G4INCL::ParticleSpecies G4INCLXXInterface::toINCLParticleSpecies(G4HadProjectile const &aTrack) const {
388  const G4ParticleDefinition *pdef = aTrack.GetDefinition();
389  const G4INCL::ParticleType theType = toINCLParticleType(pdef);
390  if(theType!=G4INCL::Composite)
391  return G4INCL::ParticleSpecies(theType);
392  else {
393  G4INCL::ParticleSpecies theSpecies;
394  theSpecies.theType=theType;
395  theSpecies.theA=(G4int) pdef->GetBaryonNumber();
396  theSpecies.theZ=(G4int) pdef->GetPDGCharge();
397  return theSpecies;
398  }
399 }
400 
401 G4double G4INCLXXInterface::toINCLKineticEnergy(G4HadProjectile const &aTrack) const {
402  return aTrack.GetKineticEnergy();
403 }
404 
405 G4ParticleDefinition *G4INCLXXInterface::toG4ParticleDefinition(G4int A, G4int Z) const {
406  if (A == 1 && Z == 1) return G4Proton::Proton();
407  else if(A == 1 && Z == 0) return G4Neutron::Neutron();
408  else if(A == 0 && Z == 1) return G4PionPlus::PionPlus();
409  else if(A == 0 && Z == -1) return G4PionMinus::PionMinus();
410  else if(A == 0 && Z == 0) return G4PionZero::PionZero();
411  else if(A == 2 && Z == 1) return G4Deuteron::Deuteron();
412  else if(A == 3 && Z == 1) return G4Triton::Triton();
413  else if(A == 3 && Z == 2) return G4He3::He3();
414  else if(A == 4 && Z == 2) return G4Alpha::Alpha();
415  else if(A > 0 && Z > 0 && A > Z) { // Returns ground state ion definition
416  return G4ParticleTable::GetParticleTable()->GetIon(Z, A, 0.0);
417  } else { // Error, unrecognized particle
418  return 0;
419  }
420 }
421 
422 G4DynamicParticle *G4INCLXXInterface::toG4Particle(G4int A, G4int Z,
423  G4double kinE,
424  G4double px,
425  G4double py, G4double pz) const {
426  const G4ParticleDefinition *def = toG4ParticleDefinition(A, Z);
427  if(def == 0) { // Check if we have a valid particle definition
428  return 0;
429  }
430  const G4double energy = kinE / MeV;
431  const G4ThreeVector momentum(px, py, pz);
432  const G4ThreeVector momentumDirection = momentum.unit();
433  G4DynamicParticle *p = new G4DynamicParticle(def, momentumDirection, energy);
434  return p;
435 }
436 
437 G4double G4INCLXXInterface::remnant4MomentumScaling(G4double mass,
438  G4double kineticE,
439  G4double px, G4double py,
440  G4double pz) const {
441  const G4double p2 = px*px + py*py + pz*pz;
442  if(p2 > 0.0) {
443  const G4double pnew2 = kineticE*kineticE + 2.0*kineticE*mass;
444  return std::sqrt(pnew2)/std::sqrt(p2);
445  } else {
446  return 1.0;
447  }
448 }
449