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