Geant4  9.6.p02
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
G4Fancy3DNucleus.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 // ------------------------------------------------------------
27 // GEANT 4 class implementation file
28 //
29 // ---------------- G4Fancy3DNucleus ----------------
30 // by Gunter Folger, May 1998.
31 // class for a 3D nucleus, arranging nucleons in space and momentum.
32 // ------------------------------------------------------------
33 // 20110805 M. Kelsey -- Remove C-style array (pointer) of G4Nucleons,
34 // make vector a container of objects. Move Helper class
35 // to .hh. Move testSums, places, momentum and fermiM to
36 // class data members for reuse.
37 
38 #include <algorithm>
39 
40 #include "G4Fancy3DNucleus.hh"
42 #include "G4NuclearFermiDensity.hh"
44 #include "G4NucleiProperties.hh"
45 #include "G4Nucleon.hh"
46 #include "G4SystemOfUnits.hh"
47 #include "Randomize.hh"
48 #include "G4ios.hh"
49 #include "G4HadronicException.hh"
50 
51 
53  : myA(0), myZ(0), theNucleons(250), currentNucleon(-1), theDensity(0),
54  nucleondistance(0.8*fermi), places(250), momentum(250), fermiM(250),
55  testSums(250)
56 {
57 //G4cout <<"G4Fancy3DNucleus::G4Fancy3DNucleus()"<<G4endl;
58 }
59 
61 {
62  if(theDensity) delete theDensity;
63 }
64 
65 #if defined(NON_INTEGER_A_Z)
67 {
68  G4int intZ = G4int(theZ);
69  G4int intA= ( G4UniformRand()>theA-G4int(theA) ) ? G4int(theA) : G4int(theA)+1;
70  // forward to integer Init()
71  Init(intA, intZ);
72 
73 }
74 #endif
75 
77 {
78 // G4cout << "G4Fancy3DNucleus::Init(theA, theZ) called"<<G4endl;
79  currentNucleon=-1;
80  theNucleons.clear();
81 
82  myZ = theZ;
83  myA= theA;
84 
85  theNucleons.resize(myA); // Pre-loads vector with empty elements
86 
87 // G4cout << "myA, myZ" << myA << ", " << myZ << G4endl;
88 
89  if(theDensity) delete theDensity;
90  if ( myA < 17 ) {
91  theDensity = new G4NuclearShellModelDensity(myA, myZ);
92  } else {
93  theDensity = new G4NuclearFermiDensity(myA, myZ);
94  }
95 
96  theFermi.Init(myA, myZ);
97 
98  ChooseNucleons();
99 
100  ChoosePositions();
101 
102 // CenterNucleons(); // This would introduce a bias
103 
104  ChooseFermiMomenta();
105 
106  G4double Ebinding= BindingEnergy()/myA;
107 
108  for (G4int aNucleon=0; aNucleon < myA; aNucleon++)
109  {
110  theNucleons[aNucleon].SetBindingEnergy(Ebinding);
111  }
112 
113 
114  return;
115 }
116 
118 {
119  currentNucleon=0;
120  return (theNucleons.size()>0);
121 }
122 
123 // Returns by pointer; null pointer indicates end of loop
125 {
126  return ( (currentNucleon>=0 && currentNucleon<myA) ?
127  &theNucleons[currentNucleon++] : 0 );
128 }
129 
130 const std::vector<G4Nucleon> & G4Fancy3DNucleus::GetNucleons()
131 {
132  return theNucleons;
133 }
134 
135 
136 // Class-scope function to sort nucleons by Z coordinate
138 {
139  return nuc1.GetPosition().z() < nuc2.GetPosition().z();
140 }
141 
142 void G4Fancy3DNucleus::SortNucleonsIncZ() // on increased Z-coordinates Uzhi 29.08.08
143 {
144  if (theNucleons.size() < 2 ) return; // Avoid unnecesary work
145 
146  std::sort(theNucleons.begin(), theNucleons.end(),
148 }
149 
150 void G4Fancy3DNucleus::SortNucleonsDecZ() // on decreased Z-coordinates Uzhi 29.08.08
151 {
152  if (theNucleons.size() < 2 ) return; // Avoid unnecessary work
154 
155  std::reverse(theNucleons.begin(), theNucleons.end());
156 }
157 
158 
159 G4double G4Fancy3DNucleus::BindingEnergy()
160 {
161  return G4NucleiProperties::GetBindingEnergy(myA,myZ);
162 }
163 
164 
166 {
167  return GetNuclearRadius(0.5);
168 }
169 
171 {
172  return theDensity->GetRadius(maxRelativeDensity);
173 }
174 
176 {
177  G4double maxradius2=0;
178 
179  for (int i=0; i<myA; i++)
180  {
181  if ( theNucleons[i].GetPosition().mag2() > maxradius2 )
182  {
183  maxradius2=theNucleons[i].GetPosition().mag2();
184  }
185  }
186  return std::sqrt(maxradius2)+nucleondistance;
187 }
188 
190 {
191  return myZ*G4Proton::Proton()->GetPDGMass() +
192  (myA-myZ)*G4Neutron::Neutron()->GetPDGMass() -
193  BindingEnergy();
194 }
195 
196 
197 
199 {
200  for (G4int i=0; i<myA; i++){
201  theNucleons[i].Boost(theBoost);
202  }
203 }
204 
206 {
207  for (G4int i=0; i<myA; i++){
208  theNucleons[i].Boost(theBeta);
209  }
210 }
211 
213 {
214  G4double factor=(1-std::sqrt(1-theBeta.mag2()))/theBeta.mag2(); // (gamma-1)/gamma/beta**2
215  G4ThreeVector rprime;
216  for (G4int i=0; i< myA; i++) {
217  rprime = theNucleons[i].GetPosition() -
218  factor * (theBeta*theNucleons[i].GetPosition()) * theBeta;
219  theNucleons[i].SetPosition(rprime);
220  }
221 }
222 
224 {
225  G4ThreeVector beta = theBoost.vect()/theBoost.e();
226  // DoLorentzBoost(beta);
227  DoLorentzContraction(beta);
228 }
229 
230 
231 
233 {
234  G4ThreeVector center;
235 
236  for (G4int i=0; i<myA; i++ )
237  {
238  center+=theNucleons[i].GetPosition();
239  }
240  center /= -myA;
241  DoTranslation(center);
242 }
243 
245 {
246  G4ThreeVector tempV;
247  for (G4int i=0; i<myA; i++ )
248  {
249  tempV = theNucleons[i].GetPosition() + theShift;
250  theNucleons[i].SetPosition(tempV);
251  }
252 }
253 
255 {
256  return theDensity;
257 }
258 
259 //----------------------- private Implementation Methods-------------
260 
261 void G4Fancy3DNucleus::ChooseNucleons()
262 {
263  G4int protons=0,nucleons=0;
264 
265  while (nucleons < myA )
266  {
267  if ( protons < myZ && G4UniformRand() < (G4double)(myZ-protons)/(G4double)(myA-nucleons) )
268  {
269  protons++;
270  theNucleons[nucleons++].SetParticleType(G4Proton::Proton());
271  }
272  else if ( (nucleons-protons) < (myA-myZ) )
273  {
274  theNucleons[nucleons++].SetParticleType(G4Neutron::Neutron());
275  }
276  else G4cout << "G4Fancy3DNucleus::ChooseNucleons not efficient" << G4endl;
277  }
278  return;
279 }
280 
281 void G4Fancy3DNucleus::ChoosePositions()
282 {
283  G4int i=0;
284  G4ThreeVector aPos, delta;
285  G4bool freeplace;
286  static G4double nd2 = sqr(nucleondistance);
287  G4double maxR=GetNuclearRadius(0.001); // there are no nucleons at a
288  // relative Density of 0.01
289  G4int jr=0;
290  G4int jx,jy;
291  G4double arand[600];
292  G4double *prand=arand;
293 
294  places.clear(); // Reset data buffer
295 
296  while ( i < myA )
297  {
298  do
299  {
300  if ( jr < 3 )
301  {
302  jr=std::min(600,9*(myA - i));
303  CLHEP::RandFlat::shootArray(jr, prand );
304  }
305  jx=--jr;
306  jy=--jr;
307  aPos.set((2*arand[jx]-1.), (2*arand[jy]-1.), (2*arand[--jr]-1.));
308  } while (aPos.mag2() > 1. );
309  aPos *=maxR;
310  G4double density=theDensity->GetRelativeDensity(aPos);
311  if (G4UniformRand() < density)
312  {
313  freeplace= true;
314  std::vector<G4ThreeVector>::iterator iplace;
315  for( iplace=places.begin(); iplace!=places.end() && freeplace;++iplace)
316  {
317  delta = *iplace - aPos;
318  freeplace= delta.mag2() > nd2;
319  }
320 
321  if ( freeplace )
322  {
323  G4double pFermi=theFermi.GetFermiMomentum(theDensity->GetDensity(aPos));
324  // protons must at least have binding energy of CoulombBarrier, so
325  // assuming the Fermi energy corresponds to a potential, we must place these such
326  // that the Fermi Energy > CoulombBarrier
327  if (theNucleons[i].GetDefinition() == G4Proton::Proton())
328  {
329  G4double nucMass = theNucleons[i].GetDefinition()->GetPDGMass();
330  G4double eFermi= std::sqrt( sqr(pFermi) + sqr(nucMass) )
331  - nucMass;
332  if (eFermi <= CoulombBarrier() ) freeplace=false;
333  }
334  }
335  if ( freeplace )
336  {
337  theNucleons[i].SetPosition(aPos);
338  places.push_back(aPos);
339  ++i;
340  }
341  }
342  }
343 
344 }
345 
346 void G4Fancy3DNucleus::ChooseFermiMomenta()
347 {
348  G4int i;
350 
351  // Pre-allocate buffers for filling by index
352  momentum.resize(myA, G4ThreeVector(0.,0.,0.));
353  fermiM.resize(myA, 0.*GeV);
354 
355  for (G4int ntry=0; ntry<1 ; ntry ++ )
356  {
357  for (i=0; i < myA; i++ ) // momenta for all, including last, in case we swap nucleons
358  {
359  density = theDensity->GetDensity(theNucleons[i].GetPosition());
360  fermiM[i] = theFermi.GetFermiMomentum(density);
361  G4ThreeVector mom=theFermi.GetMomentum(density);
362  if (theNucleons[i].GetDefinition() == G4Proton::Proton())
363  {
364  G4double eMax = std::sqrt(sqr(fermiM[i]) +sqr(theNucleons[i].GetDefinition()->GetPDGMass()) )
365  - CoulombBarrier();
366  if ( eMax > theNucleons[i].GetDefinition()->GetPDGMass() )
367  {
368  G4double pmax2= sqr(eMax) - sqr(theNucleons[i].GetDefinition()->GetPDGMass());
369  fermiM[i] = std::sqrt(pmax2);
370  while ( mom.mag2() > pmax2 )
371  {
372  mom=theFermi.GetMomentum(density, fermiM[i]);
373  }
374  } else
375  {
376  G4cerr << "G4Fancy3DNucleus: difficulty finding proton momentum" << G4endl;
377  mom=G4ThreeVector(0,0,0);
378  }
379 
380  }
381  momentum[i]= mom;
382  }
383 
384  if ( ReduceSum() ) break;
385 // G4cout <<" G4FancyNucleus: iterating to find momenta: "<< ntry<< G4endl;
386  }
387 
388 // G4ThreeVector sum;
389 // for (G4int index=0; index<myA;sum+=momentum[index++])
390 // ;
391 // G4cout << "final sum / mag() " << sum << " / " << sum.mag() << G4endl;
392 
394  for ( i=0; i< myA ; i++ )
395  {
396  energy = theNucleons[i].GetParticleType()->GetPDGMass()
397  - BindingEnergy()/myA;
398  G4LorentzVector tempV(momentum[i],energy);
399  theNucleons[i].SetMomentum(tempV);
400  // GF 11-05-2011: set BindingEnergy to be T of Nucleon with p , ~ p**2/2m
401  //theNucleons[i].SetBindingEnergy(
402  // 0.5*sqr(fermiM[i])/theNucleons[i].GetParticleType()->GetPDGMass());
403  }
404 }
405 
406 
407 G4bool G4Fancy3DNucleus::ReduceSum()
408 {
409  G4ThreeVector sum;
410  G4double PFermi=fermiM[myA-1];
411 
412  for (G4int i=0; i < myA-1 ; i++ )
413  { sum+=momentum[i]; }
414 
415 // check if have to do anything at all..
416  if ( sum.mag() <= PFermi )
417  {
418  momentum[myA-1]=-sum;
419  return true;
420  }
421 
422 // find all possible changes in momentum, changing only the component parallel to sum
423  G4ThreeVector testDir=sum.unit();
424  testSums.clear();
425  testSums.resize(myA-1); // Allocate block for filling below
426 
427  G4ThreeVector delta;
428  for (G4int aNucleon=0; aNucleon < myA-1; aNucleon++) {
429  delta = 2.*((momentum[aNucleon]*testDir)*testDir);
430 
431  testSums[aNucleon].Fill(delta, delta.mag(), aNucleon);
432  }
433 
434  std::sort(testSums.begin(), testSums.end());
435 
436 // reduce Momentum Sum until the next would be allowed.
437  G4int index=testSums.size();
438  while ( (sum-testSums[--index].Vector).mag()>PFermi && index>0)
439  {
440  // Only take one which improve, ie. don't change sign and overshoot...
441  if ( sum.mag() > (sum-testSums[index].Vector).mag() ) {
442  momentum[testSums[index].Index]-=testSums[index].Vector;
443  sum-=testSums[index].Vector;
444  }
445  }
446 
447  if ( (sum-testSums[index].Vector).mag() <= PFermi )
448  {
449  G4int best=-1;
450  G4double pBest=2*PFermi; // anything larger than PFermi
451  for ( G4int aNucleon=0; aNucleon<=index; aNucleon++)
452  {
453  // find the momentum closest to choosen momentum for last Nucleon.
454  G4double pTry=(testSums[aNucleon].Vector-sum).mag();
455  if ( pTry < PFermi
456  && std::abs(momentum[myA-1].mag() - pTry ) < pBest )
457  {
458  pBest=std::abs(momentum[myA-1].mag() - pTry );
459  best=aNucleon;
460  }
461  }
462  if ( best < 0 )
463  {
464  G4String text = "G4Fancy3DNucleus.cc: Logic error in ReduceSum()";
465  throw G4HadronicException(__FILE__, __LINE__, text);
466  }
467  momentum[testSums[best].Index]-=testSums[best].Vector;
468  momentum[myA-1]=testSums[best].Vector-sum;
469 
470  return true;
471 
472  }
473 
474  // try to compensate momentum using another Nucleon....
475  G4int swapit=-1;
476  while (swapit<myA-1)
477  {
478  if ( fermiM[++swapit] > PFermi ) break;
479  }
480  if (swapit == myA-1 ) return false;
481 
482  // Now we have a nucleon with a bigger Fermi Momentum.
483  // Exchange with last nucleon.. and iterate.
484  std::swap(theNucleons[swapit], theNucleons[myA-1]);
485  std::swap(momentum[swapit], momentum[myA-1]);
486  std::swap(fermiM[swapit], fermiM[myA-1]);
487  return ReduceSum();
488 }
489 
491 {
492  G4double coulombBarrier = (1.44/1.14) * MeV * myZ / (1.0 + std::pow(G4double(myA),1./3.));
493  return coulombBarrier;
494 }