ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
G4CascadeFinalStateAlgorithm.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file G4CascadeFinalStateAlgorithm.cc
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 
47 #include "G4CascadeParameters.hh"
48 #include "G4Exp.hh"
51 #include "G4LorentzConvertor.hh"
53 #include "G4Pow.hh"
54 #include "G4TwoBodyAngularDist.hh"
55 #include "G4VMultiBodyMomDst.hh"
56 #include "G4VTwoBodyAngDst.hh"
57 #include "Randomize.hh"
58 #include <vector>
59 #include <numeric>
60 #include <cmath>
61 
62 using namespace G4InuclSpecialFunctions;
63 
64 
65 // Cut-offs and iteration limits for generation
66 
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 = 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 
129 
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
144 
145  if (fs > 0 && multiplicity == 2) {
146  G4int kw = (fs==is) ? 1 : 2;
148  } else if (multiplicity == 3) {
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 
164 GenerateTwoBody(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 
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 
205 GenerateMultiBody(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 
228 FillMagnitudes(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 
296 satisfyTriangle(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 
313 FillDirections(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 
329 FillDirThreeBody(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 
362 FillDirManyBody(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 
413 GenerateCosTheta(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 
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  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 (size_t k=N-1; k>0; --k) {
484  mu -= masses[k];
485  T *= (k>1) ? BetaKopylov(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),
494  UniformTheta(), UniformPhi());
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 }