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