Geant4  9.6.p02
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
G4INCLNucleus.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 /*
38  * G4INCLNucleus.cc
39  *
40  * \date Jun 5, 2009
41  * \author Pekka Kaitaniemi
42  */
43 
44 #ifndef G4INCLNucleus_hh
45 #define G4INCLNucleus_hh 1
46 
47 #include "G4INCLGlobals.hh"
48 #include "G4INCLLogger.hh"
49 #include "G4INCLParticle.hh"
50 #include "G4INCLIAvatar.hh"
51 #include "G4INCLNucleus.hh"
52 #include "G4INCLKinematicsUtils.hh"
53 #include "G4INCLDecayAvatar.hh"
54 #include "G4INCLCluster.hh"
55 #include "G4INCLClusterDecay.hh"
56 #include "G4INCLDeJongSpin.hh"
61 #include <iterator>
62 #include <cstdlib>
63 #include <sstream>
64 // #include <cassert>
65 
66 namespace G4INCL {
67 
68  Nucleus::Nucleus(G4int mass, G4int charge, Config const * const conf, const G4double universeRadius)
69  : Cluster(charge,mass),
70  theInitialZ(charge), theInitialA(mass),
71  theNpInitial(0), theNnInitial(0),
72  initialInternalEnergy(0.),
73  incomingAngularMomentum(0.,0.,0.), incomingMomentum(0.,0.,0.),
74  initialCenterOfMass(0.,0.,0.),
75  remnant(true),
76  blockedDelta(NULL),
77  initialEnergy(0.),
78  tryCN(false),
79  forceTransparent(false),
80  projectileZ(0),
81  projectileA(0),
82  theUniverseRadius(universeRadius),
83  isNucleusNucleus(false),
84  theProjectileRemnant(NULL),
85  theDensity(NULL),
86  thePotential(NULL)
87  {
88  PotentialType potentialType;
89  G4bool pionPotential;
90  if(conf) {
91  potentialType = conf->getPotentialType();
92  pionPotential = conf->getPionPotential();
93  } else { // By default we don't use energy dependent
94  // potential. This is convenient for some tests.
95  potentialType = IsospinPotential;
96  pionPotential = true;
97  }
98  switch(potentialType) {
100  thePotential = new NuclearPotential::NuclearPotentialEnergyIsospinSmooth(theA, theZ, pionPotential);
101  break;
103  thePotential = new NuclearPotential::NuclearPotentialEnergyIsospin(theA, theZ, pionPotential);
104  break;
105  case IsospinPotential:
106  thePotential = new NuclearPotential::NuclearPotentialIsospin(theA, theZ, pionPotential);
107  break;
108  case ConstantPotential:
109  thePotential = new NuclearPotential::NuclearPotentialConstant(theA, theZ, pionPotential);
110  break;
111  default:
112  FATAL("Unrecognized potential type at Nucleus creation." << std::endl);
113  break;
114  }
115 
118 
120 
121  theParticleSampler->setPotential(thePotential);
122  theParticleSampler->setDensity(theDensity);
123 
124  if(theUniverseRadius<0)
125  theUniverseRadius = theDensity->getMaximumRadius();
126  theStore = new Store(conf);
127  toBeUpdated.clear();
128  }
129 
131  delete theStore;
132  delete thePotential;
133  /* We don't delete the density here any more -- the Factory is caching them
134  delete theDensity;*/
135  }
136 
138  // Reset the variables connected with the projectile remnant
139  delete theProjectileRemnant;
140  theProjectileRemnant = NULL;
141 
143  for(ParticleIter i = particles.begin(); i != particles.end(); ++i) {
145  theStore->add(*i);
146  }
147  particles.clear();
148  initialInternalEnergy = computeTotalEnergy();
149  initialCenterOfMass = thePosition;
150  }
151 
152  std::string Nucleus::dump() {
153  std::stringstream ss;
154  ss <<"(list ;; List of participants " << std::endl;
155  ParticleList participants = theStore->getParticipants();
156  for(ParticleIter i = participants.begin(); i != participants.end(); ++i) {
157  ss <<"(make-particle-avatar-map " << std::endl
158  << (*i)->dump()
159  << "(list ;; List of avatars in this particle" << std::endl
160  << ")) ;; Close the list of avatars and the particle-avatar-map" << std::endl;
161  }
162  ss << ")" << std::endl;
163  return ss.str();
164  }
165 
167  justCreated.clear();
168  toBeUpdated.clear(); // Clear the list of particles to be updated by the propagation model.
169  blockedDelta = NULL;
170  G4double totalEnergy = 0.0;
171 
172  FinalStateValidity const validity = finalstate->getValidity();
173  if(validity == ValidFS) {
174 
175  ParticleList const &created = finalstate->getCreatedParticles();
176  for(ParticleIter iter = created.begin(); iter != created.end(); ++iter) {
177  theStore->add((*iter));
178  if(!(*iter)->isOutOfWell()) {
179  totalEnergy += (*iter)->getEnergy() - (*iter)->getPotentialEnergy();
180  justCreated.push_back((*iter)); // New particle, so we must create avatars for it
181  }
182  }
183 
184  ParticleList const &deleted = finalstate->getDestroyedParticles();
185  for(ParticleIter iter = deleted.begin(); iter != deleted.end(); ++iter) {
186  theStore->particleHasBeenDestroyed((*iter)->getID());
187  }
188 
189  ParticleList const &modified = finalstate->getModifiedParticles();
190  for(ParticleIter iter = modified.begin(); iter != modified.end(); ++iter) {
191  theStore->particleHasBeenUpdated((*iter)->getID());
192  totalEnergy += (*iter)->getEnergy() - (*iter)->getPotentialEnergy();
193  toBeUpdated.push_back((*iter)); // Particle is modified so we have to create new avatars for it.
194  }
195 
196  ParticleList const &out = finalstate->getOutgoingParticles();
197  for(ParticleIter iter = out.begin(); iter != out.end(); ++iter) {
198  if((*iter)->isCluster()) {
199  Cluster *clusterOut = dynamic_cast<Cluster*>((*iter));
200  ParticleList const components = clusterOut->getParticles();
201  for(ParticleIter in = components.begin(); in != components.end(); ++in)
202  theStore->particleHasBeenEjected((*in)->getID());
203  } else {
204  theStore->particleHasBeenEjected((*iter)->getID());
205  }
206  totalEnergy += (*iter)->getEnergy(); // No potential here because the particle is gone
207  theA -= (*iter)->getA();
208  theZ -= (*iter)->getZ();
209  theStore->addToOutgoing(*iter);
210  (*iter)->setEmissionTime(theStore->getBook()->getCurrentTime());
211  }
212 
213  ParticleList const &entering = finalstate->getEnteringParticles();
214  for(ParticleIter iter = entering.begin(); iter != entering.end(); ++iter) {
215  insertParticle(*iter);
216  totalEnergy += (*iter)->getEnergy() - (*iter)->getPotentialEnergy();
217  toBeUpdated.push_back((*iter)); // Particle is modified so we have to create new avatars for it.
218  }
219  } else if(validity == PauliBlockedFS) {
220  blockedDelta = finalstate->getBlockedDelta();
221  } else if(validity == ParticleBelowFermiFS) {
222  DEBUG("A Particle is entering below the Fermi sea:" << std::endl << finalstate->print() << std::endl);
223  tryCN = true;
224  ParticleList const &entering = finalstate->getEnteringParticles();
225  for(ParticleIter iter = entering.begin(); iter != entering.end(); ++iter) {
226  insertParticle(*iter);
227  }
228  } else if(validity == ParticleBelowZeroFS) {
229  DEBUG("A Particle is entering below zero energy:" << std::endl << finalstate->print() << std::endl);
230  forceTransparent = true;
231  ParticleList const &entering = finalstate->getEnteringParticles();
232  for(ParticleIter iter = entering.begin(); iter != entering.end(); ++iter) {
233  insertParticle(*iter);
234  }
235  }
236 
237  if(validity==ValidFS &&
238  std::abs(totalEnergy - finalstate->getTotalEnergyBeforeInteraction()) > 0.1) {
239  ERROR("Energy nonconservation! Energy at the beginning of the event = "
240  << finalstate->getTotalEnergyBeforeInteraction()
241  <<" and after interaction = "
242  << totalEnergy << std::endl
243  << finalstate->print());
244  }
245  }
246 
248  WARN("Useless Nucleus::propagateParticles -method called." << std::endl);
249  }
250 
252  G4double totalEnergy = 0.0;
253  ParticleList inside = theStore->getParticles();
254  for(ParticleIter p=inside.begin(); p!=inside.end(); ++p) {
255  if((*p)->isNucleon()) // Ugly: we should calculate everything using total energies!
256  totalEnergy += (*p)->getKineticEnergy() - (*p)->getPotentialEnergy();
257  else if((*p)->isResonance())
258  totalEnergy += (*p)->getEnergy() - (*p)->getPotentialEnergy() - ParticleTable::effectiveNucleonMass;
259  else
260  totalEnergy += (*p)->getEnergy() - (*p)->getPotentialEnergy();
261  }
262  return totalEnergy;
263  }
264 
266  // If the remnant consists of only one nucleon, we need to apply a special
267  // procedure to put it on mass shell.
268  if(theA==1) {
269  emitInsidePions();
270  computeOneNucleonRecoilKinematics();
271  remnant=false;
272  return;
273  }
274 
275  // Compute the recoil momentum and angular momentum
276  theMomentum = incomingMomentum;
277  theSpin = incomingAngularMomentum;
278 
279  ParticleList outgoing = theStore->getOutgoingParticles();
280  for(ParticleIter p=outgoing.begin(); p!=outgoing.end(); ++p)
281  {
282  theMomentum -= (*p)->getMomentum();
283  theSpin -= (*p)->getAngularMomentum();
284  }
285 
286  // Subtract orbital angular momentum
288  theSpin -= (thePosition-initialCenterOfMass).vector(theMomentum);
289 
292  remnant=true;
293  }
294 
296  ThreeVector cm(0.,0.,0.);
297  G4double totalMass = 0.0;
298  ParticleList inside = theStore->getParticles();
299  for(ParticleIter p=inside.begin(); p!=inside.end(); ++p) {
300  const G4double mass = (*p)->getMass();
301  cm += (*p)->getPosition() * mass;
302  totalMass += mass;
303  }
304  cm /= totalMass;
305  return cm;
306  }
307 
309  const G4double totalEnergy = computeTotalEnergy();
310  const G4double separationEnergies = computeSeparationEnergyBalance();
311 
312  return totalEnergy - initialInternalEnergy - separationEnergies;
313  }
314 
315  std::string Nucleus::print()
316  {
317  std::stringstream ss;
318  ss << "Particles in the nucleus:" << std::endl
319  << "Participants:" << std::endl;
320  G4int counter = 1;
321  ParticleList participants = theStore->getParticipants();
322  for(ParticleIter p = participants.begin(); p != participants.end(); ++p) {
323  ss << "index = " << counter << std::endl
324  << (*p)->print();
325  counter++;
326  }
327  ss <<"Spectators:" << std::endl;
328  ParticleList spectators = theStore->getSpectators();
329  for(ParticleIter p = spectators.begin(); p != spectators.end(); ++p)
330  ss << (*p)->print();
331  ss <<"Outgoing:" << std::endl;
332  ParticleList outgoing = theStore->getOutgoingParticles();
333  for(ParticleIter p = outgoing.begin(); p != outgoing.end(); ++p)
334  ss << (*p)->print();
335 
336  return ss.str();
337  }
338 
340  ParticleList out = theStore->getOutgoingParticles();
341  ParticleList deltas;
342  for(ParticleIter i = out.begin(); i != out.end(); ++i) {
343  if((*i)->isDelta()) deltas.push_back((*i));
344  }
345  if(deltas.empty()) return false;
346 
347  for(ParticleIter i = deltas.begin(); i != deltas.end(); ++i) {
348  DEBUG("Decay outgoing delta particle:" << std::endl
349  << (*i)->print() << std::endl);
350  const ThreeVector beta = -(*i)->boostVector();
351  const G4double deltaMass = (*i)->getMass();
352 
353  // Set the delta momentum to zero and sample the decay in the CM frame.
354  // This makes life simpler if we are using real particle masses.
355  (*i)->setMomentum(ThreeVector());
356  (*i)->setEnergy((*i)->getMass());
357 
358  // Use a DecayAvatar
359  IAvatar *decay = new DecayAvatar((*i), 0.0, NULL);
360  FinalState *fs = decay->getFinalState();
361  Particle * const pion = fs->getCreatedParticles().front();
362  Particle * const nucleon = fs->getModifiedParticles().front();
363 
364  // Adjust the decay momentum if we are using the real masses
365  const G4double decayMomentum = KinematicsUtils::momentumInCM(deltaMass,
366  nucleon->getTableMass(),
367  pion->getTableMass());
368  ThreeVector newMomentum = pion->getMomentum();
369  newMomentum *= decayMomentum / newMomentum.mag();
370 
371  pion->setTableMass();
372  pion->setMomentum(newMomentum);
373  pion->adjustEnergyFromMomentum();
374  pion->setEmissionTime(theStore->getBook()->getCurrentTime());
375  pion->boost(beta);
376 
377  nucleon->setTableMass();
378  nucleon->setMomentum(-newMomentum);
379  nucleon->adjustEnergyFromMomentum();
380  nucleon->setEmissionTime(theStore->getBook()->getCurrentTime());
381  nucleon->boost(beta);
382 
383  theStore->addToOutgoing(pion);
384 
385  delete fs;
386  delete decay;
387  }
388 
389  return true;
390  }
391 
393  /* If there is a pion potential, do nothing (deltas will be counted as
394  * excitation energy).
395  * If, however, the remnant is unphysical (Z<0 or Z>A), force the deltas to
396  * decay and get rid of all the pions. In case you're wondering, you can
397  * end up with Z<0 or Z>A if the remnant contains more pi- than protons or
398  * more pi+ than neutrons, respectively.
399  */
400  const G4bool unphysicalRemnant = (theZ<0 || theZ>theA);
401  if(thePotential->hasPionPotential() && !unphysicalRemnant)
402  return false;
403 
404  // Build a list of deltas (avoid modifying the list you are iterating on).
405  ParticleList inside = theStore->getParticles();
406  ParticleList deltas;
407  for(ParticleIter i = inside.begin(); i != inside.end(); ++i)
408  if((*i)->isDelta()) deltas.push_back((*i));
409 
410  // Loop over the deltas, make them decay
411  for(ParticleIter i = deltas.begin(); i != deltas.end(); ++i) {
412  DEBUG("Decay inside delta particle:" << std::endl
413  << (*i)->print() << std::endl);
414  // Create a forced-decay avatar. Note the last boolean parameter. Note
415  // also that if the remnant is unphysical we more or less explicitly give
416  // up energy conservation and CDPP by passing a NULL pointer for the
417  // nucleus.
418  IAvatar *decay;
419  if(unphysicalRemnant)
420  decay = new DecayAvatar((*i), 0.0, NULL, true);
421  else
422  decay = new DecayAvatar((*i), 0.0, this, true);
423  FinalState *fs = decay->getFinalState();
424 
425  // The pion can be ejected only if we managed to satisfy energy
426  // conservation and if pion emission does not lead to negative excitation
427  // energies.
428  if(fs->getValidity()==ValidFS) {
429  // Apply the final state to the nucleus
430  applyFinalState(fs);
431  }
432  delete fs;
433  delete decay;
434  }
435 
436  // If the remnant is unphysical, emit all the pions
437  if(unphysicalRemnant) {
438  DEBUG("Remnant is unphysical: Z=" << theZ << ", A=" << theA << std::endl);
439  emitInsidePions();
440  }
441 
442  return true;
443  }
444 
446  ParticleList out = theStore->getOutgoingParticles();
447  ParticleList clusters;
448  for(ParticleIter i = out.begin(); i != out.end(); ++i) {
449  if((*i)->isCluster()) clusters.push_back((*i));
450  }
451  if(clusters.empty()) return false;
452 
453  for(ParticleIter i = clusters.begin(); i != clusters.end(); ++i) {
454  Cluster *cluster = dynamic_cast<Cluster*>(*i); // Can't avoid using a cast here
455  cluster->deleteParticles(); // Don't need them
456  ParticleList decayProducts = ClusterDecay::decay(cluster);
457  for(ParticleIter j = decayProducts.begin(); j!=decayProducts.end(); ++j)
458  theStore->addToOutgoing(*j);
459  }
460  return true;
461  }
462 
464  // Do the phase-space decay only if Z=0 or Z=A
465  if(theA<=1 || (theZ!=0 && theA!=theZ))
466  return false;
467 
468  ParticleList decayProducts = ClusterDecay::decay(this);
469  for(ParticleIter j = decayProducts.begin(); j!=decayProducts.end(); ++j)
470  theStore->addToOutgoing(*j);
471 
472  return true;
473  }
474 
476  /* Forcing emissions of all pions in the nucleus. This probably violates
477  * energy conservation (although the computation of the recoil kinematics
478  * might sweep this under the carpet).
479  */
480  WARN("Forcing emissions of all pions in the nucleus." << std::endl);
481 
482  // Emit the pions with this kinetic energy
483  const G4double tinyPionEnergy = 0.1; // MeV
484 
485  // Push out the emitted pions
486  ParticleList inside = theStore->getParticles();
487  for(ParticleIter i = inside.begin(); i != inside.end(); ++i) {
488  if((*i)->isPion()) {
489  (*i)->setEmissionTime(theStore->getBook()->getCurrentTime());
490  // Correction for real masses
491  const G4double theQValueCorrection = (*i)->getEmissionQValueCorrection(theA,theZ);
492  const G4double kineticEnergyOutside = (*i)->getKineticEnergy() - (*i)->getPotentialEnergy() + theQValueCorrection;
493  (*i)->setTableMass();
494  if(kineticEnergyOutside > 0.0)
495  (*i)->setEnergy((*i)->getMass()+kineticEnergyOutside);
496  else
497  (*i)->setEnergy((*i)->getMass()+tinyPionEnergy);
498  (*i)->adjustMomentumFromEnergy();
499  (*i)->setPotentialEnergy(0.);
500  theZ -= (*i)->getZ();
501  theStore->particleHasBeenEjected((*i)->getID());
502  theStore->addToOutgoing(*i);
503  }
504  }
505  }
506 
508 
509  // Forced transparent
510  if(forceTransparent)
511  return true;
512 
513  ParticleList const &pL = theStore->getOutgoingParticles();
514  G4int outZ = 0, outA = 0;
515 
516  // If any of the particles has undergone a collision, the event is not a
517  // transparent.
518  for(ParticleIter p = pL.begin(); p != pL.end(); ++p ) {
519  if( (*p)->getNumberOfCollisions() != 0 ) return false;
520  if( (*p)->getNumberOfDecays() != 0 ) return false;
521  outZ += (*p)->getZ();
522  outA += (*p)->getA();
523  }
524 
525  // Add the geometrical spectators to the Z and A count
526  if(theProjectileRemnant) {
527  outZ += theProjectileRemnant->getZ();
528  outA += theProjectileRemnant->getA();
529  }
530 
531  if(outZ!=projectileZ || outA!=projectileA) return false;
532 
533  return true;
534 
535  }
536 
537  void Nucleus::computeOneNucleonRecoilKinematics() {
538  // We should be here only if the nucleus contains only one nucleon
539 // assert(theStore->getParticles().size()==1);
540 
541  ERROR("Computing one-nucleon recoil kinematics. We should never be here nowadays, cascade should stop earlier than this." << std::endl);
542 
543  // No excitation energy!
544  theExcitationEnergy = 0.0;
545 
546  // Move the nucleon to the outgoing list
547  Particle *remN = theStore->getParticles().front();
548  theA -= remN->getA();
549  theZ -= remN->getZ();
550  theStore->particleHasBeenEjected(remN->getID());
551  theStore->addToOutgoing(remN);
552  remN->setEmissionTime(theStore->getBook()->getCurrentTime());
553 
554  // Treat the special case of a remaining delta
555  if(remN->isDelta()) {
556  IAvatar *decay = new DecayAvatar(remN, 0.0, NULL);
557  FinalState *fs = decay->getFinalState();
558  // Eject the pion
559  ParticleList created = fs->getCreatedParticles();
560  for(ParticleIter j = created.begin(); j != created.end(); ++j)
561  theStore->addToOutgoing(*j);
562  delete fs;
563  delete decay;
564  }
565 
566  // Do different things depending on how many outgoing particles we have
567  ParticleList outgoing = theStore->getOutgoingParticles();
568  if(outgoing.size() == 2) {
569 
570  DEBUG("Two particles in the outgoing channel, applying exact two-body kinematics" << std::endl);
571 
572  // Can apply exact 2-body kinematics here. Keep the CM emission angle of
573  // the first particle.
574  Particle *p1 = outgoing.front(), *p2 = outgoing.back();
575  const ThreeVector aBoostVector = incomingMomentum / initialEnergy;
576  // Boost to the initial CM
577  p1->boost(aBoostVector);
578  const G4double sqrts = std::sqrt(initialEnergy*initialEnergy - incomingMomentum.mag2());
579  const G4double pcm = KinematicsUtils::momentumInCM(sqrts, p1->getMass(), p2->getMass());
580  const G4double scale = pcm/(p1->getMomentum().mag());
581  // Reset the momenta
582  p1->setMomentum(p1->getMomentum()*scale);
583  p2->setMomentum(-p1->getMomentum());
584  p1->adjustEnergyFromMomentum();
585  p2->adjustEnergyFromMomentum();
586  // Unboost
587  p1->boost(-aBoostVector);
588  p2->boost(-aBoostVector);
589 
590  } else {
591 
592  DEBUG("Trying to adjust final-state momenta to achieve energy and momentum conservation" << std::endl);
593 
594  const G4int maxIterations=8;
595  G4double totalEnergy, energyScale;
596  G4double val=1.E+100, oldVal=1.E+100, oldOldVal=1.E+100, oldOldOldVal;
597  ThreeVector totalMomentum, deltaP;
598  std::vector<ThreeVector> minMomenta; // use it to store the particle momenta that minimize the merit function
599 
600  // Reserve the vector size
601  minMomenta.reserve(outgoing.size());
602 
603  // Compute the initial total momentum
604  totalMomentum.setX(0.0);
605  totalMomentum.setY(0.0);
606  totalMomentum.setZ(0.0);
607  for(ParticleIter i=outgoing.begin(); i!=outgoing.end(); ++i)
608  totalMomentum += (*i)->getMomentum();
609 
610  // Compute the initial total energy
611  totalEnergy = 0.0;
612  for(ParticleIter i=outgoing.begin(); i!=outgoing.end(); ++i)
613  totalEnergy += (*i)->getEnergy();
614 
615  // Iterative algorithm starts here:
616  for(G4int iterations=0; iterations < maxIterations; ++iterations) {
617 
618  // Save the old merit-function values
619  oldOldOldVal = oldOldVal;
620  oldOldVal = oldVal;
621  oldVal = val;
622 
623  if(iterations%2 == 0) {
624  DEBUG("Momentum step" << std::endl);
625  // Momentum step: modify all the particle momenta
626  deltaP = incomingMomentum - totalMomentum;
627  G4double pOldTot = 0.0;
628  for(ParticleIter i=outgoing.begin(); i!=outgoing.end(); ++i)
629  pOldTot += (*i)->getMomentum().mag();
630  for(ParticleIter i=outgoing.begin(); i!=outgoing.end(); ++i) {
631  const ThreeVector mom = (*i)->getMomentum();
632  (*i)->setMomentum(mom + deltaP*mom.mag()/pOldTot);
633  (*i)->adjustEnergyFromMomentum();
634  }
635  } else {
636  DEBUG("Energy step" << std::endl);
637  // Energy step: modify all the particle momenta
638  energyScale = initialEnergy/totalEnergy;
639  for(ParticleIter i=outgoing.begin(); i!=outgoing.end(); ++i) {
640  const ThreeVector mom = (*i)->getMomentum();
641  G4double pScale = ((*i)->getEnergy()*energyScale - std::pow((*i)->getMass(),2))/mom.mag2();
642  if(pScale>0) {
643  (*i)->setEnergy((*i)->getEnergy()*energyScale);
644  (*i)->adjustMomentumFromEnergy();
645  }
646  }
647  }
648 
649  // Compute the current total momentum and energy
650  totalMomentum.setX(0.0);
651  totalMomentum.setY(0.0);
652  totalMomentum.setZ(0.0);
653  totalEnergy = 0.0;
654  for(ParticleIter i=outgoing.begin(); i!=outgoing.end(); ++i) {
655  totalMomentum += (*i)->getMomentum();
656  totalEnergy += (*i)->getEnergy();
657  }
658 
659  // Merit factor
660  val = std::pow(totalEnergy - initialEnergy,2) +
661  0.25*(totalMomentum - incomingMomentum).mag2();
662  DEBUG("Merit function: val=" << val << ", oldVal=" << oldVal << ", oldOldVal=" << oldOldVal << ", oldOldOldVal=" << oldOldOldVal << std::endl);
663 
664  // Store the minimum
665  if(val < oldVal) {
666  DEBUG("New minimum found, storing the particle momenta" << std::endl);
667  minMomenta.clear();
668  for(ParticleIter i=outgoing.begin(); i!=outgoing.end(); ++i)
669  minMomenta.push_back((*i)->getMomentum());
670  }
671 
672  // Stop the algorithm if the search diverges
673  if(val > oldOldVal && oldVal > oldOldOldVal) {
674  DEBUG("Search is diverging, breaking out of the iteration loop: val=" << val << ", oldVal=" << oldVal << ", oldOldVal=" << oldOldVal << ", oldOldOldVal=" << oldOldOldVal << std::endl);
675  break;
676  }
677  }
678 
679  // We should have made at least one successful iteration here
680 // assert(minMomenta.size()==outgoing.size());
681 
682  // Apply the optimal momenta
683  DEBUG("Applying the solution" << std::endl);
684  std::vector<ThreeVector>::const_iterator v = minMomenta.begin();
685  for(ParticleIter i=outgoing.begin(); i!=outgoing.end(); ++i, ++v) {
686  (*i)->setMomentum(*v);
687  (*i)->adjustEnergyFromMomentum();
688  DATABLOCK((*i)->print());
689  }
690 
691  }
692 
693  }
694 
696  eventInfo->nParticles = 0;
697  G4bool isNucleonAbsorption = false;
698 
699  G4bool isPionAbsorption = false;
700  // It is possible to have pion absorption event only if the
701  // projectile is pion.
702  if(eventInfo->projectileType == PiPlus ||
703  eventInfo->projectileType == PiMinus ||
704  eventInfo->projectileType == PiZero) {
705  isPionAbsorption = true;
706  }
707 
708  // Forced CN
709  eventInfo->forcedCompoundNucleus = tryCN;
710 
711  // Outgoing particles
712  ParticleList outgoingParticles = getStore()->getOutgoingParticles();
713 
714  // Check if we have a nucleon absorption event: nucleon projectile
715  // and no ejected particles.
716  if(outgoingParticles.size() == 0 &&
717  (eventInfo->projectileType == Proton ||
718  eventInfo->projectileType == Neutron)) {
719  isNucleonAbsorption = true;
720  }
721 
722  // Reset the remnant counter
723  eventInfo->nRemnants = 0;
724  eventInfo->history.clear();
725 
726  for( ParticleIter i = outgoingParticles.begin(); i != outgoingParticles.end(); ++i ) {
727  // If the particle is a cluster and has excitation energy, treat it as a cluster
728  if((*i)->isCluster()) {
729  Cluster const * const c = dynamic_cast<Cluster *>(*i);
730 // assert(c);
731 #ifdef INCLXX_IN_GEANT4_MODE
732  if(!c)
733  continue;
734 #endif
735  const G4double eStar = c->getExcitationEnergy();
736  if(std::abs(eStar)>1E-10) {
737  if(eStar<0.) {
738  WARN("Negative excitation energy in outgoing cluster! EStar = " << eStar << std::endl);
739  }
740  eventInfo->ARem[eventInfo->nRemnants] = c->getA();
741  eventInfo->ZRem[eventInfo->nRemnants] = c->getZ();
742  eventInfo->EStarRem[eventInfo->nRemnants] = eStar;
743  ThreeVector remnantSpin = c->getSpin();
744  Float_t remnantSpinMag;
745  if(eventInfo->ARem[eventInfo->nRemnants]%2==0) { // even-A nucleus
746  remnantSpinMag = (G4int) (remnantSpin.mag()/PhysicalConstants::hc + 0.5);
747  } else { // odd-A nucleus
748  remnantSpinMag = ((G4int) (remnantSpin.mag()/PhysicalConstants::hc)) + 0.5;
749  }
750  remnantSpin *= remnantSpinMag/remnantSpin.mag();
751  eventInfo->JRem[eventInfo->nRemnants] = remnantSpinMag;
752  eventInfo->jxRem[eventInfo->nRemnants] = remnantSpin.getX();
753  eventInfo->jyRem[eventInfo->nRemnants] = remnantSpin.getY();
754  eventInfo->jzRem[eventInfo->nRemnants] = remnantSpin.getZ();
755  eventInfo->EKinRem[eventInfo->nRemnants] = c->getKineticEnergy();
756  ThreeVector mom = c->getMomentum();
757  eventInfo->pxRem[eventInfo->nRemnants] = mom.getX();
758  eventInfo->pyRem[eventInfo->nRemnants] = mom.getY();
759  eventInfo->pzRem[eventInfo->nRemnants] = mom.getZ();
760  eventInfo->thetaRem[eventInfo->nRemnants] = Math::toDegrees(mom.theta());
761  eventInfo->phiRem[eventInfo->nRemnants] = Math::toDegrees(mom.phi());
762  eventInfo->nRemnants++;
763  continue; // don't add it as a particle
764  }
765  }
766 
767  // We have a pion absorption event only if the projectile is
768  // pion and there are no ejected pions.
769  if(isPionAbsorption) {
770  if((*i)->isPion()) {
771  isPionAbsorption = false;
772  }
773  }
774  eventInfo->A[eventInfo->nParticles] = (*i)->getA();
775  eventInfo->Z[eventInfo->nParticles] = (*i)->getZ();
776  eventInfo->emissionTime[eventInfo->nParticles] = (*i)->getEmissionTime();
777  eventInfo->EKin[eventInfo->nParticles] = (*i)->getKineticEnergy();
778  ThreeVector mom = (*i)->getMomentum();
779  eventInfo->px[eventInfo->nParticles] = mom.getX();
780  eventInfo->py[eventInfo->nParticles] = mom.getY();
781  eventInfo->pz[eventInfo->nParticles] = mom.getZ();
782  eventInfo->theta[eventInfo->nParticles] = Math::toDegrees(mom.theta());
783  eventInfo->phi[eventInfo->nParticles] = Math::toDegrees(mom.phi());
784  eventInfo->origin[eventInfo->nParticles] = -1;
785  eventInfo->history.push_back("");
786  eventInfo->nParticles++;
787  }
788  eventInfo->nucleonAbsorption = isNucleonAbsorption;
789  eventInfo->pionAbsorption = isPionAbsorption;
790  eventInfo->nCascadeParticles = eventInfo->nParticles;
791 
792  // Remnant characteristics
793  if(hasRemnant()) {
794  eventInfo->ARem[eventInfo->nRemnants] = getA();
795  eventInfo->ZRem[eventInfo->nRemnants] = getZ();
796  eventInfo->EStarRem[eventInfo->nRemnants] = getExcitationEnergy();
797  if(eventInfo->EStarRem[eventInfo->nRemnants]<0.) {
798  WARN("Negative excitation energy! EStarRem = " << eventInfo->EStarRem[eventInfo->nRemnants] << std::endl);
799  }
800  if(eventInfo->ARem[eventInfo->nRemnants]%2==0) { // even-A nucleus
801  eventInfo->JRem[eventInfo->nRemnants] = (G4int) (getSpin().mag()/PhysicalConstants::hc + 0.5);
802  } else { // odd-A nucleus
803  eventInfo->JRem[eventInfo->nRemnants] = ((G4int) (getSpin().mag()/PhysicalConstants::hc)) + 0.5;
804  }
805  eventInfo->EKinRem[eventInfo->nRemnants] = getKineticEnergy();
806  ThreeVector mom = getMomentum();
807  eventInfo->pxRem[eventInfo->nRemnants] = mom.getX();
808  eventInfo->pyRem[eventInfo->nRemnants] = mom.getY();
809  eventInfo->pzRem[eventInfo->nRemnants] = mom.getZ();
810  eventInfo->thetaRem[eventInfo->nRemnants] = Math::toDegrees(mom.theta());
811  eventInfo->phiRem[eventInfo->nRemnants] = Math::toDegrees(mom.phi());
812  eventInfo->nRemnants++;
813  }
814 
815  // Global counters, flags, etc.
816  eventInfo->nCollisions = getStore()->getBook()->getAcceptedCollisions();
818  eventInfo->nDecays = getStore()->getBook()->getAcceptedDecays();
819  eventInfo->nBlockedDecays = getStore()->getBook()->getBlockedDecays();
825  }
826 
827  Nucleus::ConservationBalance Nucleus::getConservationBalance(const EventInfo &theEventInfo, const G4bool afterRecoil) const {
828  ConservationBalance theBalance;
829  // Initialise balance variables with the incoming values
830  theBalance.Z = theEventInfo.Zp + theEventInfo.Zt;
831  theBalance.A = theEventInfo.Ap + theEventInfo.At;
832 
833  theBalance.energy = getInitialEnergy();
834  theBalance.momentum = getIncomingMomentum();
835 
836  // Process outgoing particles
837  ParticleList outgoingParticles = theStore->getOutgoingParticles();
838  for( ParticleIter i = outgoingParticles.begin(); i != outgoingParticles.end(); ++i ) {
839  theBalance.Z -= (*i)->getZ();
840  theBalance.A -= (*i)->getA();
841  // For outgoing clusters, the total energy automatically includes the
842  // excitation energy
843  theBalance.energy -= (*i)->getEnergy(); // Note that outgoing particles should have the real mass
844  theBalance.momentum -= (*i)->getMomentum();
845  }
846 
847  // Remnant contribution, if present
848  if(hasRemnant()) {
849  theBalance.Z -= getZ();
850  theBalance.A -= getA();
851  theBalance.energy -= ParticleTable::getTableMass(getA(),getZ()) +
853  if(afterRecoil)
854  theBalance.energy -= getKineticEnergy();
855  theBalance.momentum -= getMomentum();
856  }
857 
858  return theBalance;
859  }
860 
862  setEnergy(initialEnergy);
863  setMomentum(incomingMomentum);
864  setSpin(incomingAngularMomentum);
867  }
868 
869  void Nucleus::finalizeProjectileRemnant(const G4double anEmissionTime) {
870  // Deal with the projectile remnant
871  if(theProjectileRemnant->getA()>1) {
872  // Set the mass
873  const G4double aMass = theProjectileRemnant->getInvariantMass();
874  theProjectileRemnant->setMass(aMass);
875 
876  // Compute the excitation energy from the invariant mass
877  const G4double anExcitationEnergy = aMass
878  - ParticleTable::getTableMass(theProjectileRemnant->getA(), theProjectileRemnant->getZ());
879 
880  // Set the excitation energy
881  theProjectileRemnant->setExcitationEnergy(anExcitationEnergy);
882 
883  // Set the spin
884  theProjectileRemnant->setSpin(DeJongSpin::shoot(theProjectileRemnant->getNumberStoredComponents(), theProjectileRemnant->getA()));
885 
886  // Set the emission time
887  theProjectileRemnant->setEmissionTime(anEmissionTime);
888 
889  // Put it in the outgoing list
890  theStore->addToOutgoing(theProjectileRemnant);
891 
892  // NULL theProjectileRemnant
893  theProjectileRemnant = NULL;
894  } else if(theProjectileRemnant->getA()==1) {
895  // Put the nucleon in the outgoing list
896  Particle *theNucleon = theProjectileRemnant->getParticles().front();
897  theStore->addToOutgoing(theNucleon);
898  // Delete the remnant
900  } else
902  }
903 
904 }
905 
906 #endif