Geant4  9.6.p02
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
G4INCLClusteringModelIntercomparison.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 
38 #include "G4INCLCluster.hh"
39 #include "G4INCLRandom.hh"
40 #include "G4INCLHashing.hh"
41 #include <algorithm>
42 
43 namespace G4INCL {
44  const G4double ClusteringModelIntercomparison::limitCosEscapeAngle = 0.7;
45 
46  static G4bool cascadingFirstPredicate(Particle *aParticle) {
47  return !aParticle->isTargetSpectator();
48  }
49 
51  // Set the maximum clustering mass dynamically, based on the current nucleus
52  const G4int maxClusterAlgorithmMass = nucleus->getStore()->getConfig()->getClusterMaxMass();
53  runningMaxClusterAlgorithmMass = std::min(maxClusterAlgorithmMass, nucleus->getA()/2);
54 
55  // Nucleus too small?
56  if(runningMaxClusterAlgorithmMass<=1)
57  return NULL;
58 
59  theNucleus = nucleus;
60  Particle *theLeadingParticle = particle;
61 
62  // Initialise sqtot to a large number
63  sqtot = 50000.0;
64  selectedA = 0;
65  selectedZ = 0;
66 
67  // The distance parameter, known as h in publications.
68  // Default value is 1 fm.
69  const G4double transp = 1.0;
70 
71  const G4double rmaxws = theNucleus->getUniverseRadius();
72 
73  // Radius of the sphere where the leading particle is positioned.
74  const G4double Rprime = theNucleus->getDensity()->getNuclearRadius() + transp;
75 
76  // Bring the leading particle back to the coalescence sphere
77  const G4double pk = theLeadingParticle->getMomentum().mag();
78  const G4double cospr = theLeadingParticle->getPosition().dot(theLeadingParticle->getMomentum())/(theNucleus->getUniverseRadius() * pk);
79  const G4double arg = rmaxws*rmaxws - Rprime*Rprime;
80  G4double translat;
81 
82  if(arg > 0.0) {
83  // coalescence sphere smaller than Rmax
84  const G4double cosmin = std::sqrt(arg)/rmaxws;
85  if(cospr <= cosmin) {
86  // there is an intersection with the coalescence sphere
87  translat = rmaxws * cospr;
88  } else {
89  // no intersection with the coalescence sphere
90  translat = rmaxws * (cospr - std::sqrt(cospr*cospr - cosmin*cosmin));
91  }
92  } else {
93  // coalescence sphere larger than Rmax
94  translat = rmaxws * cospr - std::sqrt(Rprime*Rprime - rmaxws*rmaxws*(1.0 - cospr*cospr));
95  }
96 
97  const ThreeVector oldLeadingParticlePosition = theLeadingParticle->getPosition();
98  const ThreeVector leadingParticlePosition = oldLeadingParticlePosition - theLeadingParticle->getMomentum() * (translat/pk);
99  const ThreeVector &leadingParticleMomentum = theLeadingParticle->getMomentum();
100  theLeadingParticle->setPosition(leadingParticlePosition);
101 
102  // Initialise the array of considered nucleons
103  const G4int theNucleusA = theNucleus->getA();
104  if(nConsideredMax < theNucleusA) {
105  delete [] consideredPartners;
106  delete [] isInRunningConfiguration;
107  nConsideredMax = 2*theNucleusA;
108  consideredPartners = new Particle *[nConsideredMax];
109  isInRunningConfiguration = new G4bool [nConsideredMax];
110  std::fill(isInRunningConfiguration,
111  isInRunningConfiguration + nConsideredMax,
112  false);
113  }
114 
115  // Select the subset of nucleons that will be considered in the
116  // cluster production:
117  cascadingEnergyPool = 0.;
118  nConsidered = 0;
119  const ParticleList particles = theNucleus->getStore()->getParticles();
120  for(ParticleIter i = particles.begin(); i != particles.end(); ++i) {
121  if (!(*i)->isNucleon()) continue; // Only nucleons are allowed in clusters
122  if ((*i)->getID() == theLeadingParticle->getID()) continue; // Don't count the leading particle
123 
124  G4double space = ((*i)->getPosition() - leadingParticlePosition).mag2();
125  G4double momentum = ((*i)->getMomentum() - leadingParticleMomentum).mag2();
126  G4double size = space*momentum*ParticleTable::clusterPosFact2[runningMaxClusterAlgorithmMass];
127  // Nucleons are accepted only if they are "close enough" in phase space
128  // to the leading nucleon. The selected phase-space parameter corresponds
129  // to the running maximum cluster mass.
130  if(size < ParticleTable::clusterPhaseSpaceCut[runningMaxClusterAlgorithmMass]) {
131  consideredPartners[nConsidered] = *i;
132  // Keep trace of how much energy is carried by cascading nucleons. This
133  // is used to stop the clustering algorithm as soon as possible.
134  if(!(*i)->isTargetSpectator())
135  cascadingEnergyPool += (*i)->getEnergy() - (*i)->getPotentialEnergy() - 931.3;
136  nConsidered++;
137  // Make sure we don't exceed the array size
138 // assert(nConsidered<=nConsideredMax);
139  }
140  }
141  // Sort the list of considered partners so that we give priority
142  // to participants. As soon as we encounter the first spectator in
143  // the list we know that all the remaining nucleons will be
144  // spectators too.
145  std::partition(consideredPartners, consideredPartners+nConsidered, cascadingFirstPredicate);
146 
147  // Clear the sets of checked configurations
148  // We stop caching two masses short of the max mass -- there seems to be a
149  // performance hit above
150  maxMassConfigurationSkipping = runningMaxClusterAlgorithmMass-2;
151  for(G4int i=0; i<runningMaxClusterAlgorithmMass-2; ++i) // no caching for A=1,2
152  checkedConfigurations[i].clear();
153 
154  // Initialise position, momentum and energy of the running cluster
155  // configuration
156  runningPositions[1] = leadingParticlePosition;
157  runningMomenta[1] = leadingParticleMomentum;
158  runningEnergies[1] = theLeadingParticle->getEnergy();
159  runningPotentials[1] = theLeadingParticle->getPotentialEnergy();
160 
161  // Make sure that all the elements of isInRunningConfiguration are false.
162 // assert(std::count(isInRunningConfiguration, isInRunningConfiguration+nConsidered, true)==0);
163 
164  // Start the cluster search!
165  findClusterStartingFrom(1, theLeadingParticle->getZ());
166 
167  // Again, make sure that all the elements of isInRunningConfiguration have
168  // been reset to false. This is a sanity check.
169 // assert(std::count(isInRunningConfiguration, isInRunningConfiguration+nConsidered, true)==0);
170 
171  Cluster *chosenCluster = 0;
172  if(selectedA!=0) { // A cluster was found!
173  candidateConfiguration[selectedA-1] = theLeadingParticle;
174  chosenCluster = new Cluster(candidateConfiguration,
175  candidateConfiguration + selectedA);
176  }
177 
178  // Restore the original position of the leading particle
179  theLeadingParticle->setPosition(oldLeadingParticlePosition);
180 
181  return chosenCluster;
182  }
183 
184  inline G4double ClusteringModelIntercomparison::getPhaseSpace(const G4int oldA, Particle const * const p) {
185  const G4double psSpace = (p->getPosition() - runningPositions[oldA]).mag2();
186  const G4double psMomentum = (p->getMomentum()*oldA - runningMomenta[oldA]).mag2();
187  return psSpace * psMomentum * ParticleTable::clusterPosFact2[oldA + 1];
188  }
189 
190  void ClusteringModelIntercomparison::findClusterStartingFrom(const G4int oldA, const G4int oldZ) {
191  const G4int newA = oldA + 1;
192  const G4int oldAMinusOne = oldA - 1;
193  G4int newZ;
194  G4int newN;
195 
196  // Look up the phase-space cut
197  const G4double phaseSpaceCut = ParticleTable::clusterPhaseSpaceCut[newA];
198 
199  // Configuration caching enabled only for a certain mass interval
200  const G4bool cachingEnabled = (newA<=maxMassConfigurationSkipping && newA>=3);
201 
202  // Set the pointer to the container of cached configurations
203 #if defined(INCL_CACHING_CLUSTERING_MODEL_INTERCOMPARISON_HashMask)
204  HashContainer *theHashContainer;
205  if(cachingEnabled)
206  theHashContainer = &(checkedConfigurations[oldA-2]);
207  else
208  theHashContainer = NULL;
209 #elif defined(INCL_CACHING_CLUSTERING_MODEL_INTERCOMPARISON_Set)
210  SortedNucleonConfigurationContainer *theConfigContainer;
211  if(cachingEnabled)
212  theConfigContainer = &(checkedConfigurations[oldA-2]);
213  else
214  theConfigContainer = NULL;
215 #else
216 #error Unrecognized INCL_CACHING_CLUSTERING_MODEL_INTERCOMPARISON. Allowed values are: Set, HashMask.
217 #endif
218 
219  // Minimum and maximum Z values for this mass
220  const G4int ZMinForNewA = ParticleTable::clusterZMin[newA];
221  const G4int ZMaxForNewA = ParticleTable::clusterZMax[newA];
222 
223  for(G4int i=0; i<nConsidered; ++i) {
224  // Only accept particles that are not already part of the cluster
225  if(isInRunningConfiguration[i]) continue;
226 
227  Particle * const candidateNucleon = consideredPartners[i];
228 
229  // Z and A of the new cluster
230  newZ = oldZ + candidateNucleon->getZ();
231  newN = newA - newZ;
232 
233  // Skip this nucleon if we already have too many protons or neutrons
234  if(newZ > clusterZMaxAll || newN > clusterNMaxAll)
235  continue;
236 
237  // Compute the phase space factor for a new cluster which
238  // consists of the previous running cluster and the new
239  // candidate nucleon:
240  const G4double phaseSpace = getPhaseSpace(oldA, candidateNucleon);
241  if(phaseSpace > phaseSpaceCut) continue;
242 
243  // Store the candidate nucleon in the running configuration
244  runningConfiguration[oldAMinusOne] = i;
245 #if defined(INCL_CACHING_CLUSTERING_MODEL_INTERCOMPARISON_HashMask)
246  Hashing::HashType configHash;
247  HashIterator aHashIter;
248 #elif defined(INCL_CACHING_CLUSTERING_MODEL_INTERCOMPARISON_Set)
249  SortedNucleonConfiguration thisConfig;
250  SortedNucleonConfigurationIterator thisConfigIter;
251 #endif
252  if(cachingEnabled) {
253 #if defined(INCL_CACHING_CLUSTERING_MODEL_INTERCOMPARISON_HashMask)
254  configHash = Hashing::hashConfig(runningConfiguration, oldA);
255  aHashIter = theHashContainer->lower_bound(configHash);
256  // If we have already checked this configuration, skip it
257  if(aHashIter!=theHashContainer->end()
258  && !(configHash < *aHashIter))
259  continue;
260 #elif defined(INCL_CACHING_CLUSTERING_MODEL_INTERCOMPARISON_Set)
261  thisConfig.fill(runningConfiguration,oldA);
262  thisConfigIter = theConfigContainer->lower_bound(thisConfig);
263  // If we have already checked this configuration, skip it
264  if(thisConfigIter!=theConfigContainer->end()
265  && !(thisConfig < *thisConfigIter))
266  continue;
267 #endif
268  }
269 
270  // Sum of the total energies of the cluster components
271  runningEnergies[newA] = runningEnergies[oldA] + candidateNucleon->getEnergy();
272  // Sum of the potential energies of the cluster components
273  runningPotentials[newA] = runningPotentials[oldA] + candidateNucleon->getPotentialEnergy();
274 
275  // Update the available cascading kinetic energy
276  G4double oldCascadingEnergyPool = cascadingEnergyPool;
277  if(!candidateNucleon->isTargetSpectator())
278  cascadingEnergyPool -= candidateNucleon->getEnergy() - candidateNucleon->getPotentialEnergy() - 931.3;
279 
280  // Check an approximate Coulomb barrier. If the cluster is below
281  // 0.5*barrier and the remaining available energy from cascading nucleons
282  // will not bring it above, reject the cluster.
283  const G4double halfB = 0.72 * newZ *
284  theNucleus->getZ()/(theNucleus->getDensity()->getNuclearRadius()+1.7);
285  const G4double tout = runningEnergies[newA] - runningPotentials[newA] -
286  931.3*newA;
287  if(tout<=halfB && tout+cascadingEnergyPool<=halfB) {
288  cascadingEnergyPool = oldCascadingEnergyPool;
289  continue;
290  }
291 
292  // Here the nucleon has passed all the tests. Accept it in the cluster.
293  runningPositions[newA] = (runningPositions[oldA] * oldA + candidateNucleon->getPosition())*ParticleTable::clusterPosFact[newA];
294  runningMomenta[newA] = runningMomenta[oldA] + candidateNucleon->getMomentum();
295 
296  // Add the config to the container
297  if(cachingEnabled)
298 #if defined(INCL_CACHING_CLUSTERING_MODEL_INTERCOMPARISON_HashMask)
299  theHashContainer->insert(aHashIter, configHash);
300 #elif defined(INCL_CACHING_CLUSTERING_MODEL_INTERCOMPARISON_Set)
301  theConfigContainer->insert(thisConfigIter, thisConfig);
302 #endif
303 
304  // Set the flag that reminds us that this nucleon has already been taken
305  // in the running configuration
306  isInRunningConfiguration[i] = true;
307 
308  // Keep track of the best physical cluster
309  if(newZ >= ZMinForNewA && newZ <= ZMaxForNewA) {
310  // Note: sqc is real kinetic energy, not the square of the kinetic energy!
311  const G4double sqc = KinematicsUtils::invariantMass(runningEnergies[newA],
312  runningMomenta[newA]);
313  const G4double sqct = (sqc - 2.*newZ*protonMass - 2.*(newA-newZ)*neutronMass
314  + ParticleTable::getRealMass(newA, newZ))
316 
317  if(sqct < sqtot) {
318  // This is the best cluster we have found so far. Store its
319  // kinematics.
320  sqtot = sqct;
321  selectedA = newA;
322  selectedZ = newZ;
323 
324  // Store the running configuration in a ParticleList
325  for(G4int j=0; j<oldA; ++j)
326  candidateConfiguration[j] = consideredPartners[runningConfiguration[j]];
327 
328  // Sanity check on number of nucleons in running configuration
329 // assert(std::count(isInRunningConfiguration, isInRunningConfiguration+nConsidered, true)==selectedA-1);
330  }
331  }
332 
333  // The method recursively calls itself for the next mass
334  if(newA < runningMaxClusterAlgorithmMass && newA+1 < theNucleus->getA()) {
335  findClusterStartingFrom(newA, newZ);
336  }
337 
338  // Reset the running configuration flag and the cascading energy pool
339  isInRunningConfiguration[i] = false;
340  cascadingEnergyPool = oldCascadingEnergyPool;
341  }
342  }
343 
345  // Forbid emission of the whole nucleus
346  if(c->getA()>=n->getA())
347  return false;
348 
349  // Check the escape angle of the cluster
350  const ThreeVector &pos = c->getPosition();
351  const ThreeVector &mom = c->getMomentum();
352  const G4double cosEscapeAngle = pos.dot(mom) / std::sqrt(pos.mag2()*mom.mag2());
353  if(cosEscapeAngle < limitCosEscapeAngle)
354  return false;
355 
356  return true;
357  }
358 
359 }