Geant4 9.6.0
Toolkit for the simulation of the passage of particles through matter
Loading...
Searching...
No Matches
G4StatMFChannel.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// $Id$
28//
29// Hadronic Process: Nuclear De-excitations
30// by V. Lara
31//
32// Modified:
33// 25.07.08 I.Pshenichnov (in collaboration with Alexander Botvina and Igor
34// Mishustin (FIAS, Frankfurt, INR, Moscow and Kurchatov Institute,
35// Moscow, [email protected]) fixed semi-infinite loop
36
37#include <numeric>
38
39#include "G4StatMFChannel.hh"
42#include "G4Pow.hh"
43
44class SumCoulombEnergy : public std::binary_function<G4double,G4double,G4double>
45{
46public:
49 {
50 total += frag->GetCoulombEnergy();
51 return total;
52 }
53
54 G4double GetTotal() { return total; }
55public:
57};
58
60 _NumOfNeutralFragments(0),
61 _NumOfChargedFragments(0)
62{}
63
65{
66 if (!_theFragments.empty()) {
67 std::for_each(_theFragments.begin(),_theFragments.end(),
68 DeleteFragment());
69 }
70}
71
73{
74 std::deque<G4StatMFFragment*>::iterator i;
75 for (i = _theFragments.begin();
76 i != _theFragments.end(); ++i)
77 {
78 G4int A = (*i)->GetA();
79 G4int Z = (*i)->GetZ();
80 if ( (A > 1 && (Z > A || Z <= 0)) || (A==1 && Z > A) || A <= 0 ) return false;
81 }
82 return true;
83}
84
86 // Create a new fragment.
87 // Fragments are automatically sorted: first charged fragments,
88 // then neutral ones.
89{
90 if (Z <= 0.5) {
91 _theFragments.push_back(new G4StatMFFragment(A,Z));
92 _NumOfNeutralFragments++;
93 } else {
94 _theFragments.push_front(new G4StatMFFragment(A,Z));
95 _NumOfChargedFragments++;
96 }
97
98 return;
99}
100
102{
103 G4double Coulomb = std::accumulate(_theFragments.begin(),_theFragments.end(),
104 0.0,SumCoulombEnergy());
105// G4double Coulomb = 0.0;
106// for (unsigned int i = 0;i < _theFragments.size(); i++)
107// Coulomb += _theFragments[i]->GetCoulombEnergy();
108 return Coulomb;
109}
110
111
112
114{
115 G4double Energy = 0.0;
116
117 G4double TranslationalEnergy = (3./2.)*T*static_cast<G4double>(_theFragments.size());
118
119 std::deque<G4StatMFFragment*>::const_iterator i;
120 for (i = _theFragments.begin(); i != _theFragments.end(); ++i)
121 {
122 Energy += (*i)->GetEnergy(T);
123 }
124 return Energy + TranslationalEnergy;
125}
126
128 G4int anZ,
129 G4double T)
130 //
131{
132 // calculate momenta of charged fragments
133 CoulombImpulse(anA,anZ,T);
134
135 // calculate momenta of neutral fragments
136 FragmentsMomenta(_NumOfNeutralFragments, _NumOfChargedFragments, T);
137
138 G4FragmentVector * theResult = new G4FragmentVector;
139 std::deque<G4StatMFFragment*>::iterator i;
140 for (i = _theFragments.begin(); i != _theFragments.end(); ++i)
141 theResult->push_back((*i)->GetFragment(T));
142
143 return theResult;
144}
145
146void G4StatMFChannel::CoulombImpulse(G4int anA, G4int anZ, G4double T)
147 // Aafter breakup, fragments fly away under Coulomb field.
148 // This method calculates asymptotic fragments momenta.
149{
150 // First, we have to place the fragments inside of the original nucleus volume
151 PlaceFragments(anA);
152
153 // Second, we sample initial charged fragments momenta. There are
154 // _NumOfChargedFragments charged fragments and they start at the begining
155 // of the vector _theFragments (i.e. 0)
156 FragmentsMomenta(_NumOfChargedFragments, 0, T);
157
158 // Third, we have to figure out the asymptotic momenta of charged fragments
159 // For taht we have to solve equations of motion for fragments
160 SolveEqOfMotion(anA,anZ,T);
161
162 return;
163}
164
165void G4StatMFChannel::PlaceFragments(G4int anA)
166 // This gives the position of fragments at the breakup instant.
167 // Fragments positions are sampled inside prolongated ellipsoid.
168{
169 G4Pow* g4pow = G4Pow::GetInstance();
171 const G4double Rsys = 2.0*R0*g4pow->Z13(anA);
172
173 G4bool TooMuchIterations;
174 do
175 {
176 TooMuchIterations = false;
177
178 // Sample the position of the first fragment
179 G4double R = (Rsys - R0*g4pow->Z13(_theFragments[0]->GetA()))*
180 std::pow(G4UniformRand(),1./3.);
181 _theFragments[0]->SetPosition(IsotropicVector(R));
182
183
184 // Sample the position of the remaining fragments
185 G4bool ThereAreOverlaps = false;
186 std::deque<G4StatMFFragment*>::iterator i;
187 for (i = _theFragments.begin()+1; i != _theFragments.end(); ++i)
188 {
189 G4int counter = 0;
190 do
191 {
192 R = (Rsys - R0*g4pow->Z13((*i)->GetA()))*std::pow(G4UniformRand(),1./3.);
193 (*i)->SetPosition(IsotropicVector(R));
194
195 // Check that there are not overlapping fragments
196 std::deque<G4StatMFFragment*>::iterator j;
197 for (j = _theFragments.begin(); j != i; ++j)
198 {
199 G4ThreeVector FragToFragVector = (*i)->GetPosition() - (*j)->GetPosition();
200 G4double Rmin = R0*(g4pow->Z13((*i)->GetA()) +
201 g4pow->Z13((*j)->GetA()));
202 if ( (ThereAreOverlaps = (FragToFragVector.mag2() < Rmin*Rmin)) ) break;
203 }
204 counter++;
205 } while (ThereAreOverlaps && counter < 1000);
206
207 if (counter >= 1000)
208 {
209 TooMuchIterations = true;
210 break;
211 }
212 }
213 } while (TooMuchIterations);
214
215 return;
216}
217
218void G4StatMFChannel::FragmentsMomenta(G4int NF, G4int idx,
219 G4double T)
220 // Calculate fragments momenta at the breakup instant
221 // Fragment kinetic energies are calculated according to the
222 // Boltzmann distribution at given temperature.
223 // NF is number of fragments
224 // idx is index of first fragment
225{
226 G4double KinE = (3./2.)*T*static_cast<G4double>(NF);
227
229
230 if (NF <= 0) return;
231 else if (NF == 1)
232 {
233 // We have only one fragment to deal with
234 p = IsotropicVector(std::sqrt(2.0*_theFragments[idx]->GetNuclearMass()*KinE));
235 _theFragments[idx]->SetMomentum(p);
236 }
237 else if (NF == 2)
238 {
239 // We have only two fragment to deal with
240 G4double M1 = _theFragments[idx]->GetNuclearMass();
241 G4double M2 = _theFragments[idx+1]->GetNuclearMass();
242 p = IsotropicVector(std::sqrt(2.0*KinE*(M1*M2)/(M1+M2)));
243 _theFragments[idx]->SetMomentum(p);
244 _theFragments[idx+1]->SetMomentum(-p);
245 }
246 else
247 {
248 // We have more than two fragments
249 G4double AvailableE;
250 G4int i1,i2;
251 G4double SummedE;
252 G4ThreeVector SummedP;
253 do
254 {
255 // Fisrt sample momenta of NF-2 fragments
256 // according to Boltzmann distribution
257 AvailableE = 0.0;
258 SummedE = 0.0;
259 SummedP.setX(0.0);SummedP.setY(0.0);SummedP.setZ(0.0);
260 for (G4int i = idx; i < idx+NF-2; i++)
261 {
262 G4double E;
263 G4double RandE;
264 G4double Boltzmann;
265 do
266 {
267 E = 9.0*T*G4UniformRand();
268 Boltzmann = std::sqrt(E)*std::exp(-E/T);
269 RandE = std::sqrt(T/2.)*std::exp(-0.5)*G4UniformRand();
270 }
271 while (RandE > Boltzmann);
272 p = IsotropicVector(std::sqrt(2.0*E*_theFragments[i]->GetNuclearMass()));
273 _theFragments[i]->SetMomentum(p);
274 SummedE += E;
275 SummedP += p;
276 }
277 // Calculate momenta of last two fragments in such a way
278 // that constraints are satisfied
279 i1 = idx+NF-2; // before last fragment index
280 i2 = idx+NF-1; // last fragment index
281 p = -SummedP;
282 AvailableE = KinE - SummedE;
283 // Available Kinetic Energy should be shared between two last fragments
284 }
285 while (AvailableE <= p.mag2()/(2.0*(_theFragments[i1]->GetNuclearMass()+
286 _theFragments[i2]->GetNuclearMass())));
287
288 G4double H = 1.0 + _theFragments[i2]->GetNuclearMass()/_theFragments[i1]->GetNuclearMass();
289 G4double CTM12 = H*(1.0 - 2.0*_theFragments[i2]->GetNuclearMass()*AvailableE/p.mag2());
290 G4double CosTheta1;
291 G4double Sign;
292
293 if (CTM12 > 0.9999) {CosTheta1 = 1.;}
294 else {
295 do
296 {
297 do
298 {
299 CosTheta1 = 1.0 - 2.0*G4UniformRand();
300 }
301 while (CosTheta1*CosTheta1 < CTM12);
302 }
303 while (CTM12 >= 0.0 && CosTheta1 < 0.0);
304 }
305
306 if (CTM12 < 0.0) Sign = 1.0;
307 else if (G4UniformRand() <= 0.5) Sign = -1.0;
308 else Sign = 1.0;
309
310
311 G4double P1 = (p.mag()*CosTheta1+Sign*std::sqrt(p.mag2()*(CosTheta1*CosTheta1-CTM12)))/H;
312 G4double P2 = std::sqrt(P1*P1+p.mag2() - 2.0*P1*p.mag()*CosTheta1);
313 G4double Phi = twopi*G4UniformRand();
314 G4double SinTheta1 = std::sqrt(1.0 - CosTheta1*CosTheta1);
315 G4double CosPhi1 = std::cos(Phi);
316 G4double SinPhi1 = std::sin(Phi);
317 G4double CosPhi2 = -CosPhi1;
318 G4double SinPhi2 = -SinPhi1;
319 G4double CosTheta2 = (p.mag2() + P2*P2 - P1*P1)/(2.0*p.mag()*P2);
320 G4double SinTheta2 = 0.0;
321 if (CosTheta2 > -1.0 && CosTheta2 < 1.0) SinTheta2 = std::sqrt(1.0 - CosTheta2*CosTheta2);
322
323 G4ThreeVector p1(P1*SinTheta1*CosPhi1,P1*SinTheta1*SinPhi1,P1*CosTheta1);
324 G4ThreeVector p2(P2*SinTheta2*CosPhi2,P2*SinTheta2*SinPhi2,P2*CosTheta2);
325 G4ThreeVector b(1.0,0.0,0.0);
326
327 p1 = RotateMomentum(p,b,p1);
328 p2 = RotateMomentum(p,b,p2);
329
330 SummedP += p1 + p2;
331 SummedE += p1.mag2()/(2.0*_theFragments[i1]->GetNuclearMass()) +
332 p2.mag2()/(2.0*_theFragments[i2]->GetNuclearMass());
333
334 _theFragments[i1]->SetMomentum(p1);
335 _theFragments[i2]->SetMomentum(p2);
336
337 }
338
339 return;
340}
341
342
343void G4StatMFChannel::SolveEqOfMotion(G4int anA, G4int anZ, G4double T)
344 // This method will find a solution of Newton's equation of motion
345 // for fragments in the self-consistent time-dependent Coulomb field
346{
347 G4double CoulombEnergy = (3./5.)*(elm_coupling*anZ*anZ)*
348 std::pow(1.0+G4StatMFParameters::GetKappaCoulomb(),1./3.)/
351 if (CoulombEnergy <= 0.0) return;
352
353 G4int Iterations = 0;
354 G4double TimeN = 0.0;
355 G4double TimeS = 0.0;
356 G4double DeltaTime = 10.0;
357
358 G4ThreeVector * Pos = new G4ThreeVector[_NumOfChargedFragments];
359 G4ThreeVector * Vel = new G4ThreeVector[_NumOfChargedFragments];
360 G4ThreeVector * Accel = new G4ThreeVector[_NumOfChargedFragments];
361
362 G4int i;
363 for (i = 0; i < _NumOfChargedFragments; i++)
364 {
365 Vel[i] = (1.0/(_theFragments[i]->GetNuclearMass()))*
366 _theFragments[i]->GetMomentum();
367 Pos[i] = _theFragments[i]->GetPosition();
368 }
369
370 do
371 {
372
373 G4ThreeVector distance;
374 G4ThreeVector force;
375
376 for (i = 0; i < _NumOfChargedFragments; i++)
377 {
378 force.setX(0.0); force.setY(0.0); force.setZ(0.0);
379 for (G4int j = 0; j < _NumOfChargedFragments; j++)
380 {
381 if (i != j)
382 {
383 distance = Pos[i] - Pos[j];
384 force += (elm_coupling*_theFragments[i]->GetZ()
385 *_theFragments[j]->GetZ()/
386 (distance.mag2()*distance.mag()))*distance;
387 }
388 }
389 Accel[i] = (1./(_theFragments[i]->GetNuclearMass()))*force;
390 }
391
392 TimeN = TimeS + DeltaTime;
393
394 G4ThreeVector SavedVel;
395 for ( i = 0; i < _NumOfChargedFragments; i++)
396 {
397 SavedVel = Vel[i];
398 Vel[i] += Accel[i]*(TimeN-TimeS);
399 Pos[i] += (SavedVel+Vel[i])*(TimeN-TimeS)*0.5;
400 }
401
402 // if (Iterations >= 50 && Iterations < 75) DeltaTime = 4.;
403 // else if (Iterations >= 75) DeltaTime = 10.;
404
405 TimeS = TimeN;
406
407 }
408 while (Iterations++ < 100);
409
410 // Summed fragment kinetic energy
411 G4double TotalKineticEnergy = 0.0;
412 for (i = 0; i < _NumOfChargedFragments; i++)
413 {
414 TotalKineticEnergy += _theFragments[i]->GetNuclearMass()*
415 0.5*Vel[i].mag2();
416 }
417 // Scaling of fragment velocities
418 G4double KineticEnergy = (3./2.)*_theFragments.size()*T;
419 G4double Eta = ( CoulombEnergy + KineticEnergy ) / TotalKineticEnergy;
420 for (i = 0; i < _NumOfChargedFragments; i++)
421 {
422 Vel[i] *= Eta;
423 }
424
425 // Finally calculate fragments momenta
426 for (i = 0; i < _NumOfChargedFragments; i++)
427 {
428 _theFragments[i]->SetMomentum(_theFragments[i]->GetNuclearMass()*Vel[i]);
429 }
430
431 // garbage collection
432 delete [] Pos;
433 delete [] Vel;
434 delete [] Accel;
435
436 return;
437}
438
439
440
441G4ThreeVector G4StatMFChannel::RotateMomentum(G4ThreeVector Pa,
443 // Rotates a 3-vector P to close momentum triangle Pa + V + P = 0
444{
445 G4ThreeVector U = Pa.unit();
446
447 G4double Alpha1 = U * V;
448
449 G4double Alpha2 = std::sqrt(V.mag2() - Alpha1*Alpha1);
450
451 G4ThreeVector N = (1./Alpha2)*U.cross(V);
452
453 G4ThreeVector RotatedMomentum(
454 ( (V.x() - Alpha1*U.x())/Alpha2 ) * P.x() + N.x() * P.y() + U.x() * P.z(),
455 ( (V.y() - Alpha1*U.y())/Alpha2 ) * P.x() + N.y() * P.y() + U.y() * P.z(),
456 ( (V.z() - Alpha1*U.z())/Alpha2 ) * P.x() + N.z() * P.y() + U.z() * P.z()
457 );
458 return RotatedMomentum;
459}
460
461
462
463
464
465G4ThreeVector G4StatMFChannel::IsotropicVector(const G4double Magnitude)
466 // Samples a isotropic random vector with a magnitud given by Magnitude.
467 // By default Magnitude = 1
468{
469 G4double CosTheta = 1.0 - 2.0*G4UniformRand();
470 G4double SinTheta = std::sqrt(1.0 - CosTheta*CosTheta);
471 G4double Phi = twopi*G4UniformRand();
472 G4ThreeVector Vector(Magnitude*std::cos(Phi)*SinTheta,
473 Magnitude*std::cos(Phi)*CosTheta,
474 Magnitude*std::sin(Phi));
475 return Vector;
476}
std::vector< G4Fragment * > G4FragmentVector
Definition: G4Fragment.hh:65
double G4double
Definition: G4Types.hh:64
int G4int
Definition: G4Types.hh:66
bool G4bool
Definition: G4Types.hh:67
#define G4UniformRand()
Definition: Randomize.hh:53
double z() const
Hep3Vector unit() const
double x() const
void setY(double)
double mag2() const
double y() const
Hep3Vector cross(const Hep3Vector &) const
void setZ(double)
double mag() const
void setX(double)
Definition: G4Pow.hh:54
static G4Pow * GetInstance()
Definition: G4Pow.cc:50
G4double Z13(G4int Z)
Definition: G4Pow.hh:110
G4double GetFragmentsCoulombEnergy(void)
G4bool CheckFragments(void)
G4double GetFragmentsEnergy(G4double T) const
void CreateFragment(G4int A, G4int Z)
G4FragmentVector * GetFragments(G4int anA, G4int anZ, G4double T)
G4double GetCoulombEnergy(void) const
static G4double Getr0()
static G4double GetKappaCoulomb()
G4double operator()(G4double &, G4StatMFFragment *&frag)
ush Pos
Definition: deflate.h:82