Geant4 11.1.1
Toolkit for the simulation of the passage of particles through matter
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
G4CascadeFinalStateAlgorithm.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// Author: Michael Kelsey (SLAC)
27// Date: 15 April 2013
28//
29// Description: Subclass of models/util G4VHadDecayAlgorithm which uses
30// old INUCL parametrizations for momentum and angular
31// distributions.
32//
33// 20130509 BUG FIX: Two-body momentum vector should be rotated into
34// collision axis; three-body "final" vector needs to be rotated
35// into axis of rest of system. Tweak some diagnostic messages
36// to match old G4EPCollider version.
37// 20130612 BUG FIX: Create separate FillDirThreeBody(), FillDirManyBody()
38// in order to reporoduce old method: N-body states are filled
39// from first to last, while three-body starts with the last.
40// 20130702 M. Kelsey: Copy phase-space algorithm from Kopylov; use if
41// runtime envvar G4CASCADE_USE_PHASESPACE is set
42// 20140627 BUG FIX: Use ".c_str()" in diagnostics to avoid IBM XL error.
43// 20150608 M. Kelsey -- Label all while loops as terminating.
44// 20150619 M. Kelsey -- Replace std::exp with G4Exp
45
48#include "G4Exp.hh"
51#include "G4LorentzConvertor.hh"
53#include "G4Pow.hh"
55#include "G4VMultiBodyMomDst.hh"
56#include "G4VTwoBodyAngDst.hh"
57#include "Randomize.hh"
58#include <vector>
59#include <numeric>
60#include <cmath>
61
62using namespace G4InuclSpecialFunctions;
63
64
65// Cut-offs and iteration limits for generation
66
67const G4double G4CascadeFinalStateAlgorithm::maxCosTheta = 0.9999;
68const G4double G4CascadeFinalStateAlgorithm::oneOverE = 0.3678794;
69const G4double G4CascadeFinalStateAlgorithm::small = 1.e-10;
70const G4int G4CascadeFinalStateAlgorithm::itry_max = 10;
71
72
73// Constructor and destructor
74
76 : G4VHadDecayAlgorithm("G4CascadeFinalStateAlgorithm"),
77 momDist(0), angDist(0), multiplicity(0), bullet_ekin(0.) {;}
78
80
85 toSCM.setVerbose(verbose);
86}
87
88
89// Select distributions to be used for next interaction
90
94 const std::vector<G4int>& particle_kinds) {
95 if (GetVerboseLevel()>1)
96 G4cout << " >>> " << GetName() << "::Configure" << G4endl;
97
98 // Identify initial and final state (if two-body) for algorithm selection
99 multiplicity = (G4int)particle_kinds.size();
100 G4int is = bullet->type() * target->type();
101 G4int fs = (multiplicity==2) ? particle_kinds[0]*particle_kinds[1] : 0;
102
103 ChooseGenerators(is, fs);
104
105 // Save kinematics for use with distributions
106 SaveKinematics(bullet, target);
107
108 // Save particle types for use with distributions
109 kinds = particle_kinds;
110}
111
112// Save kinematics for use with generators
113
117 if (GetVerboseLevel()>1)
118 G4cout << " >>> " << GetName() << "::SaveKinematics" << G4endl;
119
120 if (target->nucleon()) { // Which particle originated in nucleus?
121 toSCM.setBullet(bullet);
122 toSCM.setTarget(target);
123 } else {
124 toSCM.setBullet(target);
125 toSCM.setTarget(bullet);
126 }
127
128 toSCM.toTheCenterOfMass();
129
130 bullet_ekin = toSCM.getKinEnergyInTheTRS();
131}
132
133
134// Select generator based on initial and final state
135
137 if (GetVerboseLevel()>1)
138 G4cout << " >>> " << GetName() << "::ChooseGenerators"
139 << " is " << is << " fs " << fs << G4endl;
140
141 // Get generators for momentum and angle
142 if (G4CascadeParameters::usePhaseSpace()) momDist = 0;
143 else momDist = G4MultiBodyMomentumDist::GetDist(is, multiplicity);
144
145 if (fs > 0 && multiplicity == 2) {
146 G4int kw = (fs==is) ? 1 : 2;
147 angDist = G4TwoBodyAngularDist::GetDist(is, fs, kw);
148 } else if (multiplicity == 3) {
149 angDist = G4TwoBodyAngularDist::GetDist(is);
150 } else {
151 angDist = 0;
152 }
153
154 if (GetVerboseLevel()>1) {
155 G4cout << " " << (momDist?momDist->GetName().c_str():"") << " "
156 << (angDist?angDist->GetName().c_str():"") << G4endl;
157 }
158}
159
160
161// Two-body generation uses angular-distribution function
162
164GenerateTwoBody(G4double initialMass, const std::vector<G4double>& masses,
165 std::vector<G4LorentzVector>& finalState) {
166 if (GetVerboseLevel()>1)
167 G4cout << " >>> " << GetName() << "::GenerateTwoBody" << G4endl;
168
169 finalState.clear(); // Initialization and sanity checks
170
171 if (multiplicity != 2) return;
172
173 // Generate momentum vector in CMS for back-to-back particles
174 G4double pscm = TwoBodyMomentum(initialMass, masses[0], masses[1]);
175
176 G4double costh = angDist ? angDist->GetCosTheta(bullet_ekin, pscm)
177 : (2.*G4UniformRand() - 1.);
178
179 mom.setRThetaPhi(pscm, std::acos(costh), UniformPhi());
180
181 if (GetVerboseLevel()>3) { // Copied from old G4EPCollider
182 G4cout << " Particle kinds = " << kinds[0] << " , " << kinds[1]
183 << "\n pmod " << pscm
184 << "\n before rotation px " << mom.x() << " py " << mom.y()
185 << " pz " << mom.z() << G4endl;
186 }
187
188 finalState.resize(2); // Allows filling by index
189
190 finalState[0].setVectM(mom, masses[0]);
191 finalState[0] = toSCM.rotate(finalState[0]);
192
193 if (GetVerboseLevel()>3) { // Copied from old G4EPCollider
194 G4cout << " after rotation px " << finalState[0].x() << " py "
195 << finalState[0].y() << " pz " << finalState[0].z() << G4endl;
196 }
197
198 finalState[1].setVectM(-finalState[0].vect(), masses[1]);
199}
200
201
202// N-body generation uses momentum-modulus distribution, computed angles
203
205GenerateMultiBody(G4double initialMass, const std::vector<G4double>& masses,
206 std::vector<G4LorentzVector>& finalState) {
207 if (GetVerboseLevel()>1)
208 G4cout << " >>> " << GetName() << "::GenerateMultiBody" << G4endl;
209
211 FillUsingKopylov(initialMass, masses, finalState);
212 return;
213 }
214
215 finalState.clear(); // Initialization and sanity checks
216 if (multiplicity < 3) return;
217 if (!momDist) return;
218
219 G4int itry = -1; /* Loop checking 08.06.2015 MHK */
220 while ((G4int)finalState.size() != multiplicity && ++itry < itry_max) {
221 FillMagnitudes(initialMass, masses);
222 FillDirections(initialMass, masses, finalState);
223 }
224}
225
226
228FillMagnitudes(G4double initialMass, const std::vector<G4double>& masses) {
229 if (GetVerboseLevel()>1)
230 G4cout << " >>> " << GetName() << "::FillMagnitudes" << G4endl;
231
232 modules.clear(); // Initialization and sanity checks
233 if (!momDist) return;
234
235 modules.resize(multiplicity,0.); // Pre-allocate to avoid resizing
236
237 G4double mass_last = masses.back();
238 G4double pmod = 0.;
239
240 if (GetVerboseLevel() > 3){
241 G4cout << " knd_last " << kinds.back() << " mass_last "
242 << mass_last << G4endl;
243 }
244
245 G4int itry = -1;
246 while (++itry < itry_max) { /* Loop checking 08.06.2015 MHK */
247 if (GetVerboseLevel() > 3) {
248 G4cout << " itry in fillMagnitudes " << itry << G4endl;
249 }
250
251 G4double eleft = initialMass;
252
253 G4int i; // For access outside of loop
254 for (i=0; i < multiplicity-1; i++) {
255 pmod = momDist->GetMomentum(kinds[i], bullet_ekin);
256
257 if (pmod < small) break;
258 eleft -= std::sqrt(pmod*pmod + masses[i]*masses[i]);
259
260 if (GetVerboseLevel() > 3) {
261 G4cout << " kp " << kinds[i] << " pmod " << pmod
262 << " mass2 " << masses[i]*masses[i] << " eleft " << eleft
263 << "\n x1 " << eleft - mass_last << G4endl;
264 }
265
266 if (eleft <= mass_last) break;
267
268 modules[i] = pmod;
269 }
270
271 if (i < multiplicity-1) continue; // Failed to generate full kinematics
272
273 G4double plast = eleft * eleft - masses.back()*masses.back();
274 if (GetVerboseLevel() > 2) G4cout << " plast ** 2 " << plast << G4endl;
275
276 if (plast <= small) continue; // Not enough momentum left over
277
278 plast = std::sqrt(plast); // Final momentum is what's left over
279 modules.back() = plast;
280
281 if (multiplicity > 3 || satisfyTriangle(modules)) break; // Successful
282 } // while (itry < itry_max)
283
284 if (itry >= itry_max) { // Too many attempts
285 if (GetVerboseLevel() > 2)
286 G4cerr << " Unable to generate momenta for multiplicity "
287 << multiplicity << G4endl;
288
289 modules.clear(); // Something went wrong, throw away partial
290 }
291}
292
293// For three-body states, check kinematics of momentum moduli
294
296satisfyTriangle(const std::vector<G4double>& pmod) const {
297 if (GetVerboseLevel() > 3)
298 G4cout << " >>> " << GetName() << "::satisfyTriangle" << G4endl;
299
300 return ( (pmod.size() != 3) ||
301 !(pmod[0] < std::fabs(pmod[1] - pmod[2]) ||
302 pmod[0] > pmod[1] + pmod[2] ||
303 pmod[1] < std::fabs(pmod[0] - pmod[2]) ||
304 pmod[1] > pmod[0] + pmod[2] ||
305 pmod[2] < std::fabs(pmod[0] - pmod[1]) ||
306 pmod[2] > pmod[1] + pmod[0])
307 );
308}
309
310// Generate momentum directions into final state
311
313FillDirections(G4double initialMass, const std::vector<G4double>& masses,
314 std::vector<G4LorentzVector>& finalState) {
315 if (GetVerboseLevel()>1)
316 G4cout << " >>> " << GetName() << "::FillDirections" << G4endl;
317
318 finalState.clear(); // Initialization and sanity check
319 if ((G4int)modules.size() != multiplicity) return;
320
321 // Different order of processing for three vs. N body
322 if (multiplicity == 3)
323 FillDirThreeBody(initialMass, masses, finalState);
324 else
325 FillDirManyBody(initialMass, masses, finalState);
326}
327
329FillDirThreeBody(G4double initialMass, const std::vector<G4double>& masses,
330 std::vector<G4LorentzVector>& finalState) {
331 if (GetVerboseLevel()>1)
332 G4cout << " >>> " << GetName() << "::FillDirThreeBody" << G4endl;
333
334 finalState.resize(3);
335
336 G4double costh = GenerateCosTheta(kinds[2], modules[2]);
337 finalState[2] = generateWithFixedTheta(costh, modules[2], masses[2]);
338 finalState[2] = toSCM.rotate(finalState[2]); // Align target axis
339
340 // Generate direction of first particle
341 costh = -0.5 * (modules[2]*modules[2] + modules[0]*modules[0] -
342 modules[1]*modules[1]) / modules[2] / modules[0];
343
344 if (std::fabs(costh) >= maxCosTheta) { // Bad kinematics; abort generation
345 finalState.clear();
346 return;
347 }
348
349 // Report success
350 if (GetVerboseLevel()>2) G4cout << " ok for mult 3" << G4endl;
351
352 // First particle is at fixed angle to recoil system
353 finalState[0] = generateWithFixedTheta(costh, modules[0], masses[0]);
354 finalState[0] = toSCM.rotate(finalState[2], finalState[0]);
355
356 // Remaining particle is constrained to recoil from entire rest of system
357 finalState[1].set(0.,0.,0.,initialMass);
358 finalState[1] -= finalState[0] + finalState[2];
359}
360
362FillDirManyBody(G4double initialMass, const std::vector<G4double>& masses,
363 std::vector<G4LorentzVector>& finalState) {
364 if (GetVerboseLevel()>1)
365 G4cout << " >>> " << GetName() << "::FillDirManyBody" << G4endl;
366
367 // Fill all but the last two particles randomly
368 G4double costh = 0.;
369
370 finalState.resize(multiplicity);
371
372 for (G4int i=0; i<multiplicity-2; i++) {
373 costh = GenerateCosTheta(kinds[i], modules[i]);
374 finalState[i] = generateWithFixedTheta(costh, modules[i], masses[i]);
375 finalState[i] = toSCM.rotate(finalState[i]); // Align target axis
376 }
377
378 // Total momentum so far, to compute recoil of last two particles
379 G4LorentzVector psum =
380 std::accumulate(finalState.begin(), finalState.end()-2, G4LorentzVector());
381 G4double pmod = psum.rho();
382
383 costh = -0.5 * (pmod*pmod +
384 modules[multiplicity-2]*modules[multiplicity-2] -
385 modules[multiplicity-1]*modules[multiplicity-1])
386 / pmod / modules[multiplicity-2];
387
388 if (GetVerboseLevel() > 2) G4cout << " ct last " << costh << G4endl;
389
390 if (std::fabs(costh) >= maxCosTheta) { // Bad kinematics; abort generation
391 finalState.clear();
392 return;
393 }
394
395 // Report success
396 if (GetVerboseLevel()>2) G4cout << " ok for mult " << multiplicity << G4endl;
397
398 // First particle is at fixed angle to recoil system
399 finalState[multiplicity-2] =
400 generateWithFixedTheta(costh, modules[multiplicity-2],
401 masses[multiplicity-2]);
402 finalState[multiplicity-2] = toSCM.rotate(psum, finalState[multiplicity-2]);
403
404 // Remaining particle is constrained to recoil from entire rest of system
405 finalState[multiplicity-1].set(0.,0.,0.,initialMass);
406 finalState[multiplicity-1] -= psum + finalState[multiplicity-2];
407}
408
409
410// Generate polar angle for three- and multi-body systems
411
413GenerateCosTheta(G4int ptype, G4double pmod) const {
414 if (GetVerboseLevel() > 2) {
415 G4cout << " >>> " << GetName() << "::GenerateCosTheta " << ptype
416 << " " << pmod << G4endl;
417 }
418
419 if (multiplicity == 3) { // Use distribution for three-body
420 return angDist->GetCosTheta(bullet_ekin, ptype);
421 }
422
423 // Throw multi-body distribution
424 G4double p0 = ptype<3 ? 0.36 : 0.25; // Nucleon vs. everything else
425 G4double alf = 1.0 / p0 / (p0 - (pmod+p0)*G4Exp(-pmod / p0));
426
427 G4double sinth = 2.0;
428
429 G4int itry1 = -1; /* Loop checking 08.06.2015 MHK */
430 while (std::fabs(sinth) > maxCosTheta && ++itry1 < itry_max) {
431 G4double s1 = pmod * inuclRndm();
432 G4double s2 = alf * oneOverE * p0 * inuclRndm();
433 G4double salf = s1 * alf * G4Exp(-s1 / p0);
434 if (GetVerboseLevel() > 3) {
435 G4cout << " s1 * alf * G4Exp(-s1 / p0) " << salf
436 << " s2 " << s2 << G4endl;
437 }
438
439 if (salf > s2) sinth = s1 / pmod;
440 }
441
442 if (GetVerboseLevel() > 3)
443 G4cout << " itry1 " << itry1 << " sinth " << sinth << G4endl;
444
445 if (itry1 == itry_max) {
446 if (GetVerboseLevel() > 2)
447 G4cout << " high energy angles generation: itry1 " << itry1 << G4endl;
448
449 sinth = 0.5 * inuclRndm();
450 }
451
452 // Convert generated sin(theta) to cos(theta) with random sign
453 G4double costh = std::sqrt(1.0 - sinth * sinth);
454 if (inuclRndm() > 0.5) costh = -costh;
455
456 return costh;
457}
458
459
460// SPECIAL: Generate N-body phase space using Kopylov algorithm
461// ==> Code is copied verbatim from G4HadPhaseSpaceKopylov
462
464FillUsingKopylov(G4double initialMass,
465 const std::vector<G4double>& masses,
466 std::vector<G4LorentzVector>& finalState) {
467 if (GetVerboseLevel()>2)
468 G4cout << " >>> " << GetName() << "::FillUsingKopylov" << G4endl;
469
470 finalState.clear();
471
472 std::size_t N = masses.size();
473 finalState.resize(N);
474
475 G4double mtot = std::accumulate(masses.begin(), masses.end(), 0.0);
476 G4double mu = mtot;
477 G4double Mass = initialMass;
478 G4double T = Mass-mtot;
479 G4double recoilMass = 0.0;
480 G4ThreeVector momV, boostV; // Buffers to reduce memory churn
481 G4LorentzVector recoil(0.0,0.0,0.0,Mass);
482
483 for (std::size_t k=N-1; k>0; --k) {
484 mu -= masses[k];
485 T *= (k>1) ? BetaKopylov((G4int)k) : 0.;
486
487 recoilMass = mu + T;
488
489 boostV = recoil.boostVector(); // Previous system's rest frame
490
491 // Create momentum with a random direction isotropically distributed
492 // FIXME: Should theta distribution should use Bertini fit function?
493 momV.setRThetaPhi(TwoBodyMomentum(Mass,masses[k],recoilMass),
495
496 finalState[k].setVectM(momV,masses[k]);
497 recoil.setVectM(-momV,recoilMass);
498
499 finalState[k].boost(boostV);
500 recoil.boost(boostV);
501 Mass = recoilMass;
502 }
503
504 finalState[0] = recoil;
505}
506
508 G4Pow* g4pow = G4Pow::GetInstance();
509
510 G4int N = 3*K - 5;
511 G4double xN = G4double(N);
512 G4double Fmax = std::sqrt(g4pow->powN(xN/(xN+1.),N)/(xN+1.));
513
514 G4double F, chi;
515 do { /* Loop checking 08.06.2015 MHK */
516 chi = G4UniformRand();
517 F = std::sqrt(g4pow->powN(chi,N)*(1.-chi));
518 } while ( Fmax*G4UniformRand() > F);
519 return chi;
520}
G4double G4Exp(G4double initial_x)
Exponential Function double precision.
Definition: G4Exp.hh:180
double G4double
Definition: G4Types.hh:83
bool G4bool
Definition: G4Types.hh:86
int G4int
Definition: G4Types.hh:85
G4GLOB_DLL std::ostream G4cerr
#define G4endl
Definition: G4ios.hh:57
G4GLOB_DLL std::ostream G4cout
#define G4UniformRand()
Definition: Randomize.hh:52
double z() const
double x() const
double y() const
void setRThetaPhi(double r, double theta, double phi)
Hep3Vector boostVector() const
HepLorentzVector & boost(double, double, double)
void setVectM(const Hep3Vector &spatial, double mass)
void set(double x, double y, double z, double t)
void FillDirThreeBody(G4double initialMass, const std::vector< G4double > &masses, std::vector< G4LorentzVector > &finalState)
virtual void GenerateMultiBody(G4double initialMass, const std::vector< G4double > &masses, std::vector< G4LorentzVector > &finalState)
virtual void SetVerboseLevel(G4int verbose)
void FillUsingKopylov(G4double initialMass, const std::vector< G4double > &masses, std::vector< G4LorentzVector > &finalState)
void SaveKinematics(G4InuclElementaryParticle *bullet, G4InuclElementaryParticle *target)
void Configure(G4InuclElementaryParticle *bullet, G4InuclElementaryParticle *target, const std::vector< G4int > &particle_kinds)
G4double GenerateCosTheta(G4int ptype, G4double pmod) const
void FillDirManyBody(G4double initialMass, const std::vector< G4double > &masses, std::vector< G4LorentzVector > &finalState)
void FillDirections(G4double initialMass, const std::vector< G4double > &masses, std::vector< G4LorentzVector > &finalState)
virtual void GenerateTwoBody(G4double initialMass, const std::vector< G4double > &masses, std::vector< G4LorentzVector > &finalState)
void FillMagnitudes(G4double initialMass, const std::vector< G4double > &masses)
G4bool satisfyTriangle(const std::vector< G4double > &pmod) const
static G4bool usePhaseSpace()
void setVerbose(G4int vb=0)
void setBullet(const G4InuclParticle *bullet)
G4LorentzVector rotate(const G4LorentzVector &mom) const
G4double getKinEnergyInTheTRS() const
void setTarget(const G4InuclParticle *target)
static const G4VMultiBodyMomDst * GetDist(G4int is, G4int mult)
static void setVerboseLevel(G4int vb=0)
Definition: G4Pow.hh:49
static G4Pow * GetInstance()
Definition: G4Pow.cc:41
G4double powN(G4double x, G4int n) const
Definition: G4Pow.cc:162
static void setVerboseLevel(G4int vb=0)
static const G4VTwoBodyAngDst * GetDist(G4int is, G4int fs, G4int kw)
G4double UniformTheta() const
const G4String & GetName() const
G4double TwoBodyMomentum(G4double M0, G4double M1, G4double M2) const
virtual void SetVerboseLevel(G4int verbose)
virtual const G4String & GetName() const
virtual G4double GetMomentum(G4int ptype, const G4double &ekin) const =0
virtual const G4String & GetName() const
virtual G4double GetCosTheta(const G4double &ekin, const G4double &pcm) const =0
#define N
Definition: crc32.c:56
G4LorentzVector generateWithFixedTheta(G4double ct, G4double p, G4double mass=0.)