INukeUtils2014.cxx
Go to the documentation of this file.
1 //____________________________________________________________________________
2 /*
3  Copyright (c) 2003-2017, GENIE Neutrino MC Generator Collaboration
4  For the full text of the license visit http://copyright.genie-mc.org
5  or see $GENIE/LICENSE
6 
7  Author: Jim Dobson <j.dobson07 \at imperial.ac.uk>
8  Imperial College London
9 
10  Costas Andreopoulos <costas.andreopoulos \at stfc.ac.uk>
11  University of Liverpool & STFC Rutherford Appleton Lab
12 
13  Aaron Meyer <asm58 \at pitt.edu>
14  Pittsburgh University
15 
16  For documentation see the corresponding header file.
17 
18  Important revisions after version 2.0.0 :
19  @ Mar 04, 2009 - JD
20  Was first added in v2.5.1. Adapted from the T2K GENIE reweighting tool.
21  @ Mar 05, 2009 - CA
22  Modified ReconstructHadronFateHA() to work with hadron+A event files in
23  addition to neutrino event files.
24  @ Sep 10, 2009 - CA
25  Added MeanFreePath(), Dist2Exit(), Dist2ExitMFP()
26  @ Sep 30, 2009 - CA
27  Added StepParticle() from Intranuke.cxx
28  @ Oct 02, 2009 - CA
29  Added test MeanFreePath_Delta().
30  @ Jul 15, 2010 - AM
31  Added common utility functions used by both hA and hN mode. Updated
32  MeanFreePath to separate proton and neutron cross sections. Added general
33  utility functions.
34  @ Jan 9, 2015 - SD, NG, TG
35  Added 2014 version of INTRANUKE codes for v2.9.0. Uses INukeHadroData2014,
36  but no changes to mean free path.
37 */
38 //____________________________________________________________________________
39 
40 #include <TLorentzVector.h>
41 #include <TMath.h>
42 
44 #include "Conventions/Constants.h"
45 #include "Conventions/Controls.h"
46 #include "Conventions/Units.h"
47 #include "Conventions/GBuild.h"
49 #include "GHEP/GHepRecord.h"
50 #include "GHEP/GHepParticle.h"
54 #include "Messenger/Messenger.h"
55 #include "Numerical/RandomGen.h"
56 #include "Numerical/Spline.h"
57 #include "PDG/PDGLibrary.h"
58 #include "PDG/PDGUtils.h"
59 #include "PDG/PDGCodes.h"
60 #include "PDG/PDGCodeList.h"
61 #include "PDG/PDGUtils.h"
62 #include "Registry/Registry.h"
63 #include "Utils/NuclearUtils.h"
64 #include "Utils/PrintUtils.h"
65 
66 using std::ostringstream;
67 using namespace genie;
68 using namespace genie::utils;
69 using namespace genie::constants;
70 using namespace genie::controls;
71 
72 //____________________________________________________________________________
74  int pdgc, const TLorentzVector & x4, const TLorentzVector & p4,
75  double A, double Z, double nRpi, double nRnuc)
76 {
77 // Calculate the mean free path (in fm) for a pions and nucleons in a nucleus
78 //
79 // Inputs
80 // pdgc : Hadron PDG code
81 // x4 : Hadron 4-position in the nucleus coordinate system (units: fm)
82 // p4 : Hadron 4-momentum (units: GeV)
83 // A : Nucleus atomic mass number
84 // nRpi : Controls the pion ring size in terms of de-Broglie wavelengths
85 // nRnuc: Controls the nuclepn ring size in terms of de-Broglie wavelengths
86 //
87  bool is_pion = pdgc == kPdgPiP || pdgc == kPdgPi0 || pdgc == kPdgPiM;
88  bool is_nucleon = pdgc == kPdgProton || pdgc == kPdgNeutron;
89  bool is_kaon = pdgc == kPdgKP;
90  bool is_gamma = pdgc == kPdgGamma;
91 
92  if(!is_pion && !is_nucleon && !is_kaon && !is_gamma) return 0.;
93 
94  // before getting the nuclear density at the current position
95  // check whether the nucleus has to become larger by const times the
96  // de Broglie wavelength -- that is somewhat empirical, but this
97  // is what is needed to get piA total cross sections right.
98  // The ring size is different for light nuclei (using gaus density) /
99  // heavy nuclei (using woods-saxon density).
100  // The ring size is different for pions / nucleons.
101  //
102  double momentum = p4.Vect().Mag(); // hadron momentum in GeV
103  double ring = (momentum>0) ? 1.240/momentum : 0; // de-Broglie wavelength
104 
105  if(A<=20) { ring /= 2.; }
106 
107  if (is_pion || is_kaon ) { ring *= nRpi; }
108  else if (is_nucleon ) { ring *= nRnuc; }
109  else if (is_gamma ) { ring = 0.; }
110 
111  // get the nuclear density at the current position
112  double rnow = x4.Vect().Mag();
113  double rho = A * utils::nuclear::Density(rnow,(int) A,ring);
114 
115  // the hadron+nucleon cross section will be evaluated within the range
116  // of the input spline and assumed to be const outside that range
117  //
118  double ke = (p4.Energy() - p4.M()) / units::MeV; // kinetic energy in MeV
119  ke = TMath::Max(INukeHadroData2014::fMinKinEnergy, ke);
120  ke = TMath::Min(INukeHadroData2014::fMaxKinEnergyHN, ke);
121 
122  // get total xsection for the incident hadron at its current
123  // kinetic energy
124  double sigtot = 0;
125  double ppcnt = (double) Z/ (double) A; // % of protons remaining
127 
128  if (pdgc == kPdgPiP)
129  { sigtot = fHadroData2014 -> XSecPipp_Tot() -> Evaluate(ke)*ppcnt;
130  sigtot+= fHadroData2014 -> XSecPipn_Tot() -> Evaluate(ke)*(1-ppcnt);}
131  else if (pdgc == kPdgPi0)
132  { sigtot = fHadroData2014 -> XSecPi0p_Tot() -> Evaluate(ke)*ppcnt;
133  sigtot+= fHadroData2014 -> XSecPi0n_Tot() -> Evaluate(ke)*(1-ppcnt);}
134  else if (pdgc == kPdgPiM)
135  { sigtot = fHadroData2014 -> XSecPipn_Tot() -> Evaluate(ke)*ppcnt;
136  sigtot+= fHadroData2014 -> XSecPipp_Tot() -> Evaluate(ke)*(1-ppcnt);}
137  else if (pdgc == kPdgProton)
138  { sigtot = fHadroData2014 -> XSecPp_Tot() -> Evaluate(ke)*ppcnt;
139  sigtot+= fHadroData2014 -> XSecPn_Tot() -> Evaluate(ke)*(1-ppcnt);}
140  else if (pdgc == kPdgNeutron)
141  { sigtot = fHadroData2014 -> XSecPn_Tot() -> Evaluate(ke)*ppcnt;
142  sigtot+= fHadroData2014 -> XSecNn_Tot() -> Evaluate(ke)*(1-ppcnt);}
143  else if (pdgc == kPdgKP)
144  { sigtot = fHadroData2014 -> XSecKpN_Tot() -> Evaluate(ke);
145  sigtot*=1.2;}
146  else if (pdgc == kPdgGamma)
147  { sigtot = fHadroData2014 -> XSecGamp_fs() -> Evaluate(ke)*ppcnt;
148  sigtot+= fHadroData2014 -> XSecGamn_fs() -> Evaluate(ke)*(1-ppcnt);}
149  else {
150  return 0;
151  }
152 
153  // the xsection splines in INukeHadroData return the hadron x-section in
154  // mb -> convert to fm^2
155  sigtot *= (units::mb / units::fm2);
156 
157  // compute the mean free path
158  double lamda = 1. / (rho * sigtot);
159 
160  // exits if lamda is InF (if cross section is 0)
161  if( ! TMath::Finite(lamda) ) {
162  return -1;
163  }
164 
165 /*
166  LOG("INukeUtils", pDEBUG)
167  << "sig_total = " << sigtot << " fm^2, rho = " << rho
168  << " fm^-3 => mfp = " << lamda << " fm.";
169 */
170  return lamda;
171 }
172 //____________________________________________________________________________
174  int pdgc, const TLorentzVector & x4, const TLorentzVector & p4, double A)
175 {
176 //
177 // **test**
178 //
179 
180 // Calculate the mean free path (in fm) for Delta's in a nucleus
181 //
182 // Inputs
183 // pdgc : Hadron PDG code
184 // x4 : Hadron 4-position in the nucleus coordinate system (units: fm)
185 // p4 : Hadron 4-momentum (units: GeV)
186 // A : Nucleus atomic mass number
187 //
188  bool is_deltapp = (pdgc==kPdgP33m1232_DeltaPP);
189  if(!is_deltapp) return 0.;
190 
191  // get the nuclear density at the current position
192  double rnow = x4.Vect().Mag();
193  double rho = A * utils::nuclear::Density(rnow,(int) A);
194 
195  // the Delta+N->N+N cross section will be evaluated within the range
196  // of the input spline and assumed to be const outside that range
197  double ke = (p4.Energy() - p4.M()) / units::MeV; // kinetic energy in MeV
198  ke = TMath::Max(1500., ke);
199  ke = TMath::Min( 0., ke);
200 
201  // get the Delta+N->N+N cross section
202  double sig = 0;
203  if (ke< 500) sig=20;
204  else if (ke<1000) sig=40;
205  else sig=30;
206 
207  // value is in mb -> convert to fm^2
208  sig *= (units::mb / units::fm2);
209 
210  // compute the mean free path
211  double lamda = 1. / (rho * sig);
212 
213  // exits if lamda is InF (if cross section is 0)
214  if( ! TMath::Finite(lamda) ) {
215  return -1;
216  }
217 
218  return lamda;
219 }
220 //____________________________________________________________________________
222  int pdgc, const TLorentzVector & x4, const TLorentzVector & p4, double A, double Z,
223  double mfp_scale_factor, double nRpi, double nRnuc, double NR, double R0)
224 {
225 // Calculate the survival probability for a hadron inside a nucleus
226 //
227 // Inputs
228 // pdgc : Hadron PDG code
229 // x4 : Hadron 4-position in the nucleus coordinate system (units: fm)
230 // p4 : Hadron 4-momentum (units: GeV)
231 // A : Nucleus atomic mass number
232 // mfp_scale_factor: Tweaks the mean free path (mfp -> mfp*scale). Def: 1.0
233 // nRpi: Controls the pion ring size in terms of de-Broglie wavelengths
234 // nRnuc: Controls the nuclepn ring size in terms of de-Broglie wavelengths
235 // NR: How far away to track the hadron, in terms of the corresponding
236 // nuclear radius. Def: 3
237 // R0: R0 in R=R0*A^1/3 (units:fm). Def. 1.4
238 
239  double prob = 1.0;
240 
241  double step = 0.05; // fermi
242  double R = NR * R0 * TMath::Power(A, 1./3.);
243 
244  TVector3 dr3 = p4.Vect().Unit(); // unit vector along its direction
245  TLorentzVector dr4(dr3,0);
246 
247  LOG("INukeUtils", pDEBUG)
248  << "Calculating survival probability for hadron with PDG code = " << pdgc
249  << " and momentum = " << p4.P() << " GeV";
250  LOG("INukeUtils", pDEBUG)
251  << "mfp scale = " << mfp_scale_factor
252  << ", nRpi = " << nRpi << ", nRnuc = " << nRnuc << ", NR = " << NR
253  << ", R0 = " << R0 << " fm";
254 
255  TLorentzVector x4_curr(x4); // current position
256 
257  while(1) {
258  double rnow = x4_curr.Vect().Mag();
259  if (rnow > (R+step)) break;
260 
261  x4_curr += (step*dr4);
262  rnow = x4_curr.Vect().Mag();
263  double mfp =
264  genie::utils::intranuke2014::MeanFreePath(pdgc,x4_curr,p4,A,Z,nRpi,nRnuc);
265  double mfp_twk = mfp * mfp_scale_factor;
266 
267  double dprob = (mfp_twk>0) ? TMath::Exp(-step/mfp_twk) : 0.;
268  prob*=dprob;
269 /*
270  LOG("INukeUtils", pDEBUG)
271  << "+ step size = " << step << " fm, |r| = " << rnow << " fm, "
272  << "mfp = " << mfp_twk << "fm (nominal mfp = " << mfp << " fm): "
273  << "dPsurv = " << dprob << ", current Psurv = " << prob;
274 */
275  }
276 
277  LOG("INukeUtils", pDEBUG) << "Psurv = " << prob;
278 
279  return prob;
280 }
281 //____________________________________________________________________________
283  const TLorentzVector & x4, const TLorentzVector & p4,
284  double A, double NR, double R0)
285 {
286 // Calculate distance within a nucleus (units: fm) before we stop tracking
287 // the hadron.
288 // See previous functions for a description of inputs.
289 //
290  double R = NR * R0 * TMath::Power(A, 1./3.);
291  double step = 0.05; // fermi
292 
293  TVector3 dr3 = p4.Vect().Unit(); // unit vector along its direction
294  TLorentzVector dr4(dr3,0);
295 
296  TLorentzVector x4_curr(x4); // current position
297 
298  double d=0;
299  while(1) {
300  double rnow = x4_curr.Vect().Mag();
301  x4_curr += (step*dr4);
302  d+=step;
303  rnow = x4_curr.Vect().Mag();
304  if (rnow > R) break;
305  }
306  return d;
307 }
308 //____________________________________________________________________________
310  int pdgc, const TLorentzVector & x4, const TLorentzVector & p4,
311  double A, double Z, double NR, double R0)
312 {
313 // Calculate distance within a nucleus (expressed in terms of 'mean free
314 // paths') before we stop tracking the hadron.
315 // See previous functions for a description of inputs.
316 //
317 
318 // distance before exiting in mean free path lengths
319 //
320  double R = NR * R0 * TMath::Power(A, 1./3.);
321  double step = 0.05; // fermi
322 
323  TVector3 dr3 = p4.Vect().Unit(); // unit vector along its direction
324  TLorentzVector dr4(dr3,0);
325 
326  TLorentzVector x4_curr(x4); // current position
327 
328  double d=0;
329  double d_mfp=0;
330  while(1) {
331  double rnow = x4_curr.Vect().Mag();
332  x4_curr += (step*dr4);
333  d+=step;
334  rnow = x4_curr.Vect().Mag();
335 
336  double lambda = genie::utils::intranuke2014::MeanFreePath(pdgc,x4_curr,p4,A,Z);
337  d_mfp += (step/lambda);
338 
339  if (rnow > R) break;
340  }
341  return d_mfp;
342 }
343 //____________________________________________________________________________
345  GHepParticle * p, double step, double nuclear_radius)
346 {
347 // Steps a particle starting from its current position (in fm) and moving
348 // along the direction of its current momentum by the input step (in fm).
349 // The particle is stepped in a straight line.
350 // If a nuclear radius is set then the following check is performed:
351 // If the step is too large and takes the the particle far away from the
352 // nucleus then its position is scaled back so that the escaped particles are
353 // always within a ~1fm from the "outer nucleus surface"
354 
355 #ifdef __GENIE_LOW_LEVEL_MESG_ENABLED__
356  LOG("INukeUtils", pDEBUG)
357  << "Stepping particle [" << p->Name() << "] by dr = " << step << " fm";
358 #endif
359 
360  // Step particle
361  TVector3 dr = p->P4()->Vect().Unit(); // unit vector along its direction
362  dr.SetMag(step); // spatial step size
363  double dt = 0; // temporal step:
364  TLorentzVector dx4(dr,dt); // 4-vector step
365  TLorentzVector x4new = *(p->X4()) + dx4; // new position
366 
367  if(nuclear_radius > 0.) {
368  // Check position against nuclear boundary. If the particle was stepped
369  // too far away outside the nuclear boundary bring it back to within
370  // 1fm from that boundary
371  double epsilon = 1; // fm
372  double r = x4new.Vect().Mag(); // fm
373  double rmax = nuclear_radius+epsilon;
374  if(r > rmax) {
375  LOG("INukeUtils", pINFO)
376  << "Particle was stepped too far away (r = " << r << " fm)";
377  LOG("INukeUtils", pINFO)
378  << "Placing it " << epsilon
379  << " fm outside the nucleus (r' = " << rmax << " fm)";
380  double scale = rmax/r;
381  x4new *= scale;
382  }//r>rmax
383  }//nucl radius set
384 
385 #ifdef __GENIE_LOW_LEVEL_MESG_ENABLED__
386  LOG("INukeUtils", pDEBUG)
387  << "\n Init direction = " << print::Vec3AsString(&dr)
388  << "\n Init position (in fm,nsec) = " << print::X4AsString(p->X4())
389  << "\n Fin position (in fm,nsec) = " << print::X4AsString(&x4new);
390 #endif
391 
392  p->SetPosition(x4new);
393 }
394 //___________________________________________________________________________
395 // Method to handle compound nucleus considerations, preequilibrium
396 // and equilibrium
397 // Alex Bell -- 6/17/2008
399  GHepRecord * ev, GHepParticle * p, int &RemnA, int &RemnZ, TLorentzVector &RemnP4,
400  bool DoFermi, double FermiFac, const NuclearModelI* Nuclmodel, double NucRmvE, EINukeMode mode)
401 {
402 
403 #ifdef __GENIE_LOW_LEVEL_MESG_ENABLED__
404  LOG("INukeUtils", pDEBUG)
405  << "PreEquilibrium() is invoked for a : " << p->Name()
406  << " whose kinetic energy is : " << p->KinE();
407 #endif
408 
409  // Random number generator
410  RandomGen * rnd = RandomGen::Instance();
411  PDGLibrary * pLib = PDGLibrary::Instance();
412 
413  bool allow_dup = true;
414  PDGCodeList list(allow_dup); // list of final state particles
415 
416  double ppcnt = (double) RemnZ / (double) RemnA; // % of protons left
417 
418  // figure out the final state conditions
419 
420  if(p->Pdg()==kPdgProton) list.push_back(kPdgProton);
421  else if(p->Pdg()==kPdgNeutron) list.push_back(kPdgNeutron);
422 
423  for(int i=0;i<3;i++)
424  {
425  if(rnd->RndFsi().Rndm()<ppcnt)
426  {
427  list.push_back(kPdgProton);
428  RemnZ--;
429  }
430  else list.push_back(kPdgNeutron);
431 
432  RemnA--;
433 
434  ppcnt = (double) RemnZ / (double) RemnA;
435  }
436 
437  // Add the fermi energy of the three nucleons to the phase space
438  if(DoFermi)
439  {
440  Target target(ev->TargetNucleus()->Pdg());
441  TVector3 pBuf = p->P4()->Vect();
442  double mBuf = p->Mass();
443  double eBuf = TMath::Sqrt(pBuf.Mag2() + mBuf*mBuf);
444  TLorentzVector tSum(pBuf,eBuf);
445  double mSum = 0.0;
447  for(pdg_iter=++(list.begin());pdg_iter!=list.end();++pdg_iter)
448  {
449  target.SetHitNucPdg(*pdg_iter);
450  Nuclmodel->GenerateNucleon(target);
451  mBuf = pLib->Find(*pdg_iter)->Mass();
452  mSum += mBuf;
453  pBuf = FermiFac * Nuclmodel->Momentum3();
454  eBuf = TMath::Sqrt(pBuf.Mag2() + mBuf*mBuf);
455  tSum += TLorentzVector(pBuf,eBuf);
456  RemnP4 -= TLorentzVector(pBuf,eBuf-mBuf);
457  }
458  TLorentzVector dP4 = tSum + TLorentzVector(TVector3(0,0,0),-mSum);
459  p->SetMomentum(dP4);
460  }
461 
462  // do the phase space decay & save all f/s particles to the event record
463  bool success = genie::utils::intranuke2014::PhaseSpaceDecay(ev,p,list,RemnP4,NucRmvE,mode);
464  if(success) LOG("INukeUtils",pINFO) << "Successful phase space decay for pre-equilibrium nucleus FSI event";
465  else
466  {
468  exception.SetReason("Phase space generation of pre-equilibrium nucleus final state failed, details above");
469  throw exception;
470  }
471 
472  int p_loc = 0;
473  while(p_loc<ev->GetEntries())
474  {
475  GHepParticle * p_ref = ev->Particle(p_loc);
476  if(!p->ComparePdgCodes(p_ref)) p_loc++;
477  else
478  {
479  if(!p->CompareStatusCodes(p_ref)) p_loc++;
480  else
481  {
482  if(!p->CompareMomentum(p_ref)) p_loc++;
483  else break;
484  }
485  }
486  }
487 
488 #ifdef __GENIE_LOW_LEVEL_MESG_ENABLED__
489  LOG("INukeUtils", pDEBUG)
490  << "Particle at: " << p_loc;
491 #endif
492 
493  // find the appropriate daughter
494  vector<int> * descendants = ev->GetStableDescendants(p_loc);
495 
496  int loc = p_loc + 1;
497  int f_loc = p_loc + 1;
498  double energy = ev->Particle(loc)->E();
499 
500 /* // (1) least energetic
501  double min_en = energy;
502 
503  for(unsigned int j=0;j<descendants->size();j++)
504  {
505  loc = (*descendants)[j];
506  energy = ev->Particle(loc)->E();
507  if(energy<min_en)
508  {
509  f_loc = loc;
510  min_en = energy;
511  }
512  }
513 */
514  // (2) most energetic
515  double max_en = energy;
516 
517  for(unsigned int j=0;j<descendants->size();j++)
518  {
519  loc = (*descendants)[j];
520  energy = ev->Particle(loc)->E();
521  if(energy>max_en)
522  {
523  f_loc = loc;
524  max_en = energy;
525  }
526  }
527 
528  // (3) 1st particle
529  // ...just use the defaulted f_loc
530 
531  delete descendants;
532 
533  // change particle status for decaying particle
535  // decay a clone particle
536  GHepParticle * t = new GHepParticle(*(ev->Particle(f_loc)));
537  t->SetFirstMother(f_loc);
538  genie::utils::intranuke2014::Equilibrium(ev,t,RemnA,RemnZ,RemnP4,DoFermi,FermiFac,Nuclmodel,NucRmvE,mode);
539 
540  delete t;
541 }
542 //___________________________________________________________________________
543 // Method to handle Equilibrium reaction
544 // Alex Bell -- 6/17/2008
546  GHepRecord * ev, GHepParticle * p, int &RemnA, int &RemnZ, TLorentzVector &RemnP4,
547  bool DoFermi, double FermiFac, const NuclearModelI* Nuclmodel, double NucRmvE, EINukeMode mode)
548 {
549 
550 #ifdef __GENIE_LOW_LEVEL_MESG_ENABLED__
551  LOG("INukeUtils", pDEBUG)
552  << "Equilibrium() is invoked for a : " << p->Name()
553  << " whose kinetic energy is : " << p->KinE();
554 #endif
555 
556  // Random number generator
557  RandomGen * rnd = RandomGen::Instance();
558  PDGLibrary * pLib = PDGLibrary::Instance();
559 
560  bool allow_dup = true;
561  PDGCodeList list(allow_dup); // list of final state particles
562 
563  // % of protons left
564  double ppcnt = (double) RemnZ / (double) RemnA;
565 
566  // figure out the final state conditions
567 
568  if(p->Pdg()==kPdgProton) list.push_back(kPdgProton);
569  else if(p->Pdg()==kPdgNeutron) list.push_back(kPdgNeutron);
570 
571  //add additonal particles to stack
572  for(int i=0;i<2;i++)
573  {
574  if(rnd->RndFsi().Rndm()<ppcnt)
575  {
576  list.push_back(kPdgProton);
577  RemnZ--;
578  }
579  else list.push_back(kPdgNeutron);
580 
581  RemnA--;
582 
583  ppcnt = (double) RemnZ / (double) RemnA;
584  }
585 
586 #ifdef __GENIE_LOW_LEVEL_MESG_ENABLED__
587  LOG("INukeUtils", pDEBUG)
588  << "Remnant nucleus (A,Z) = (" << RemnA << ", " << RemnZ << ")";
589 #endif
590 
591  // Add the fermi energy of the three nucleons to the phase space
592  if(DoFermi)
593  {
594  Target target(ev->TargetNucleus()->Pdg());
595  TVector3 pBuf = p->P4()->Vect();
596  double mBuf = p->Mass();
597  double eBuf = TMath::Sqrt(pBuf.Mag2() + mBuf*mBuf);
598  TLorentzVector tSum(pBuf,eBuf);
599  double mSum = 0.0;
601  for(pdg_iter=++(list.begin());pdg_iter!=list.end();++pdg_iter)
602  {
603  target.SetHitNucPdg(*pdg_iter);
604  Nuclmodel->GenerateNucleon(target);
605  mBuf = pLib->Find(*pdg_iter)->Mass();
606  mSum += mBuf;
607  pBuf = FermiFac * Nuclmodel->Momentum3();
608  eBuf = TMath::Sqrt(pBuf.Mag2() + mBuf*mBuf);
609  tSum += TLorentzVector(pBuf,eBuf);
610  RemnP4 -= TLorentzVector(pBuf,eBuf-mBuf);
611  }
612  TLorentzVector dP4 = tSum + TLorentzVector(TVector3(0,0,0),-mSum);
613  p->SetMomentum(dP4);
614  }
615 
616  // do the phase space decay & save all f/s particles to the record
617  bool success = genie::utils::intranuke2014::PhaseSpaceDecay(ev,p,list,RemnP4,NucRmvE,mode);
618  if (success) LOG("INukeUtils",pINFO) << "successful pre-equilibrium interaction";
619  else
620  {
622  exception.SetReason("Phase space generation of compound nucleus final state failed, details above");
623  throw exception;
624  }
625 
626 }
627 //___________________________________________________________________________
629  GHepRecord* ev, int pcode, int tcode, int scode, int s2code, double C3CM,
630  GHepParticle* p, GHepParticle* t, int &RemnA, int &RemnZ, TLorentzVector &RemnP4, EINukeMode mode)
631 {
632  // Aaron Meyer (10/29/09)
633  // Adapted from kinematics in other function calls
634  //
635  // C3CM is the cosine of the scattering angle, calculated before calling
636  // p and t are the output particles, must be allocated before calling
637  // pcode,tcode,scode,s2code are initial and final particle PDG codes in scattering
638  // return value used for error checking
639 
640  // Kinematic variables
641 
642  double M3, M4; // rest energies, in GeV
643  double E3L, P3L, E4L, P4L;
644  TVector3 tP1L, tPtot, tbeta, tbetadir, tTrans, tVect;
645  TVector3 tP1zCM, tP2zCM, tP3L, tP4L;
646 
647  // Library instance for reference
648  PDGLibrary * pLib = PDGLibrary::Instance();
649 
650  // random number generator
651  // RandomGen * rnd = RandomGen::Instance();
652 
653  // handle fermi target
654  Target target(ev->TargetNucleus()->Pdg());
655 
656  // get mass for particles
657  M3 = pLib->Find(scode)->Mass();
658  M4 = pLib->Find(s2code)->Mass();
659 
660  // get lab energy and momenta and assign to 4 vectors
661  TLorentzVector t4P1L = *p->P4();
662  TLorentzVector t4P2L = *t->P4();
663 
664  // binding energy
665  double bindE = 0.025; // empirical choice, might need to be improved
666  //double bindE = 0.0;
667 
668  // carry out scattering
669  TLorentzVector t4P3L, t4P4L;
670  if (!TwoBodyKinematics(M3,M4,t4P1L,t4P2L,t4P3L,t4P4L,C3CM,RemnP4,bindE))
671  {
672  P3L = t4P3L.Vect().Mag();
673  P4L = t4P4L.Vect().Mag();
674  E3L = t4P3L.E();
675  E4L = t4P4L.E();
676 
677  LOG("TwoBodyCollision",pNOTICE)
678  << "TwoBodyKinematics fails: C3CM, P3 = " << C3CM << " "
679  << P3L << " " << E3L << "\n" << " P4 = "
680  << P4L << " " << E4L ;
681  return false; //covers all possiblities for now
682  }
683 
684  // error checking
685  P3L = t4P3L.Vect().Mag();
686  P4L = t4P4L.Vect().Mag();
687  E3L = t4P3L.E();
688  E4L = t4P4L.E();
689  LOG("INukeUtils",pINFO)
690  << "C3CM, P3 = " << C3CM << " "
691  << P3L << " " << E3L << "\n" << " P4 = "
692  << P4L << " " << E4L ;
693 
694  // handle very low momentum particles
695  if(!(TMath::Finite(P3L)) || P3L<.001)
696  {
697  LOG("INukeUtils",pINFO)
698  << "Particle 3 momentum small or non-finite: " << P3L
699  << "\n" << "--> Assigning .001 as new momentum";
700  P3L = .001;
701  E3L = TMath::Sqrt(P3L*P3L + M3*M3);
702  }
703  if(!(TMath::Finite(P4L)) || P4L<.001)
704  {
705  LOG("INukeUtils",pINFO)
706  << "Particle 4 momentum small or non-finite: " << P4L
707  << "\n" << "--> Assigning .001 as new momentum";
708  P4L = .001;
709  E4L = TMath::Sqrt(P4L*P4L + M4*M4);
710  }
711 
712  /*// pauli blocking turn off for now to better match data
713  // if(P3L<fFermiMomentum && pdg::IsNeutronOrProton(scode) ||
714  // P4L<fFermiMomentum && pdg::IsNeutronOrProton(s2code) )
715  if(P3L<.25 && pdg::IsNeutronOrProton(scode) ||
716  P4L<.25 && pdg::IsNeutronOrProton(s2code) )
717  {
718  LOG("INukeUtils",pNOTICE)<< "TwoBodyCollision failed: Pauli blocking";
719  p->SetStatus(kIStHadronInTheNucleus);
720  RemnP4 -= TLorentzVector(0,0,0,bindE);
721  return false;
722  }*/
723 
724  // update remnant nucleus
725  RemnP4 -= t4P2L;
726  if (tcode==kPdgProton) {RemnZ--;RemnA--;}
727  else if(tcode==kPdgNeutron) RemnA--;
728 
729  // create t particle w/ appropriate momenta, code, and status
730  // Set target's mom to be the mom of the hadron that was cloned
731  t->SetFirstMother(p->FirstMother());
732  t->SetLastMother(p->LastMother());
733 
734  // adjust p to reflect scattering
735  p->SetPdgCode(scode);
736  p->SetMomentum(t4P3L);
737 
738  t->SetPdgCode(s2code);
739  t->SetMomentum(t4P4L);
740 
741  if (mode==kIMdHN)
742  {
745  }
746  else
747  {
750  }
751  LOG("INukeUtils",pINFO) << "Successful 2 body collision";
752  return true;
753 
754 }
755 //___________________________________________________________________________
757  double M3, double M4, TLorentzVector t4P1L, TLorentzVector t4P2L,
758  TLorentzVector &t4P3L, TLorentzVector &t4P4L, double C3CM, TLorentzVector &RemnP4, double bindE)
759 {
760  // Aaron Meyer (05/17/10)
761  // Adapted from kinematics in other function calls
762  //
763  // Outgoing particle masses M3,M4
764  // Scatters particles according to normal two body collisions
765  //
766  // bindE is the binding energy (GeV) of a particle removed from the nucleus (default 0)
767  // For nonzero binding energy, remove the binding energy from the total energy,
768  // then put both of the particles back on mass shell by shifting momentum/energy
769  // of target
770  // Momentum only shifted in the direction parallel to the probe's motion
771  //
772  // Rotates final transverse component of momentum by a random angle from 0->2pi
773  // Return value for error checking
774  // Gives outgoing 4-momenta of particles 3 and 4 (t4P3L, t4P4L respectively)
775  //
776  // All 4-momenta should be on mass shell
777 
778  double E1L, E2L, P1L, P2L, E3L, P3L;
779  double beta, gm; // speed and gamma for CM frame in Lab
780  double S3CM; // sin of scattering angle
781  double PHI3;
782  double E1CM, E2CM, E3CM, P3CM;//, E4CM, P4CM;
783  double P3zL, P3tL;//, P4zL, P4tL;
784  double Et;
785  double theta1, theta2, theta5, P1zL, P2zL, P1tL, P2tL;
786  TVector3 tbeta, tbetadir, tTrans, tVect;
787  TVector3 tP1zCM, tP2zCM, vP3L;
788  TLorentzVector t4P1buf, t4P2buf, t4Ptot;
789 
790  // Library instance for reference
791  //PDGLibrary * pLib = PDGLibrary::Instance();
792 
793  // random number generator
794  RandomGen * rnd = RandomGen::Instance();
795 
796  // error checking
797  if (C3CM < -1. || C3CM > 1.) return false;
798 
799  // calculate sine from scattering angle
800  S3CM = TMath::Sqrt(1.0 - C3CM*C3CM);
801 
802  // fill buffers
803  t4P1buf = t4P1L;
804  t4P2buf = t4P2L;
805 
806  // get lab energy and momenta
807  E1L = t4P1buf.E();
808  P1L = t4P1buf.P();
809  E2L = t4P2buf.E();
810  P2L = t4P2buf.P();
811  t4Ptot = t4P1buf + t4P2buf;
812 
813  // binding energy
814  if (bindE!=0)
815  {
816 
817  E1L -= bindE;
818 
819  if (E1L+E2L < M3+M4)
820  {
821  LOG("INukeUtils",pNOTICE) <<"TwoBodyKinematics Failed: Forbidden by binding energy";
822  LOG("INukeUtils",pNOTICE) <<"E1L, E2L, M3, M4 : "<< E1L <<", "<< E2L <<", "<< M3 <<", "<< M4;
823  t4P3L.SetPxPyPzE(0,0,0,0);
824  t4P4L.SetPxPyPzE(0,0,0,0);
825  return false;
826  }
827  }
828 
829  // calculate beta and gamma
830  tbeta = t4Ptot.Vect() * (1.0 / (E1L + E2L));
831  tbetadir = tbeta.Unit();
832  beta = tbeta.Mag();
833  gm = 1.0 / TMath::Sqrt(1.0 - beta*beta);
834 
835  // get angle and component info
836  theta1 = t4P1buf.Angle(tbeta);
837  theta2 = t4P2buf.Angle(tbeta);
838  P1zL = P1L*TMath::Cos(theta1);
839  P2zL = P2L*TMath::Cos(theta2);
840  P1tL = TMath::Sqrt(P1L*P1L - P1zL*P1zL);
841  P2tL = -TMath::Sqrt(P2L*P2L - P2zL*P2zL);
842  tVect.SetXYZ(1,0,0);
843  if(TMath::Abs((tVect - tbetadir).Mag())<.01) tVect.SetXYZ(0,1,0);
844  theta5 = tVect.Angle(tbetadir);
845  tTrans = (tVect - TMath::Cos(theta5)*tbetadir).Unit();
846 
847  // boost to CM frame to get scattered particle momenta
848  E1CM = gm*E1L - gm*beta*P1zL;
849  tP1zCM = gm*P1zL*tbetadir - gm*tbeta*E1L;
850  E2CM = gm*E2L - gm*beta*P2zL;
851  tP2zCM = gm*P2zL*tbetadir - gm*tbeta*E2L;
852  Et = E1CM + E2CM;
853 //-------
854 
855  LOG("INukeUtils",pINFO) <<"M1 "<<t4P1L.M()<< ", M2 "<<t4P2L.M();
856  LOG("INukeUtils",pINFO) <<"E1L "<<E1L<< ", E1CM "<<E1CM;
857  LOG("INukeUtils",pINFO) <<"P1zL "<<P1zL<<", P1zCM "<<tP1zCM.Mag()<<", P1tL "<<P1tL;
858  LOG("INukeUtils",pINFO) <<"E2L "<<E2L<< ", E2CM "<<E2CM;
859  LOG("INukeUtils",pINFO) <<"P2zL "<<P2zL<<", P2zCM "<<tP2zCM.Mag()<<", P2tL "<<P2tL;
860  LOG("INukeUtils",pINFO) <<"C3CM "<<C3CM;
861 
862 //-------
863  E3CM = (Et*Et + M3*M3 - M4*M4) / (2.0 * Et);
864 
865  // check to see if decay is viable
866  if(E3CM*E3CM - M3*M3<0 || E3CM<0 || Et<0)
867  {
868  if (Et<0) LOG("INukeUtils",pNOTICE) <<"TwoBodyKinematics Failed: Total energy is negative";
869  if (E3CM<M3) LOG("INukeUtils",pNOTICE) <<"TwoBodyKinematics Failed: Scattered Particle 3 CM energy is too small";
870  if (E3CM*E3CM - M3*M3<0) LOG("INukeUtils",pNOTICE) <<"TwoBodyKinematics Failed: Scattered Particle 3 CM momentum is nonreal";
871  t4P3L.SetPxPyPzE(0,0,0,0);
872  t4P4L.SetPxPyPzE(0,0,0,0);
873  return false;
874  }
875  P3CM = TMath::Sqrt(E3CM*E3CM - M3*M3);
876 
877  // boost back to lab
878  P3zL = gm*beta*E3CM + gm*P3CM*C3CM;
879  P3tL = P3CM*S3CM;
880 
881  P3L = TMath::Sqrt(P3zL*P3zL + P3tL*P3tL);
882  E3L = TMath::Sqrt(P3L*P3L + M3*M3);
883 
884  //-------
885 
886  double E4CM = Et-E3CM;
887  double P4zL = gm*beta*E4CM - gm*P3CM*C3CM;
888  double P4tL = -1.*P3tL;
889  double P4L = TMath::Sqrt(P4zL*P4zL + P4tL*P4tL);
890  double E4L = TMath::Sqrt(P4L*P4L + M4*M4);
891 
892  LOG("INukeUtils",pINFO) <<"M3 "<< M3 << ", M4 "<< M4;
893  LOG("INukeUtils",pINFO) <<"E3L "<<E3L<< ", E3CM "<<E3CM;
894  LOG("INukeUtils",pINFO) <<"P3zL "<<P3zL<<", P3tL "<<P3tL;
895  LOG("INukeUtils",pINFO) <<"C3L "<<P3zL/P3L;
896  LOG("INukeUtils",pINFO) <<"Check:";
897  LOG("INukeUtils",pINFO) <<"E4L "<<E4L<< ", E4CM "<<E4CM;
898  LOG("INukeUtils",pINFO) <<"P4zL "<<P4zL<<", P4tL "<<P4tL;
899  LOG("INukeUtils",pINFO) <<"P4L "<<P4L;
900  LOG("INukeUtils",pINFO) <<"C4L "<<P4zL/P4L;
901 
902  double echeck = E1L + E2L - (E3L + E4L);
903  double pzcheck = P1zL+ P2zL - (P3zL + P4zL);
904  double ptcheck = P1tL+ P2tL - (P3tL + P4tL);
905 
906  LOG("INukeUtils",pINFO) <<"Check 4-momentum conservation - Energy "<<echeck<<", z momentum "<<pzcheck << ", transverse momentum " << ptcheck ;
907 
908  // -------
909 
910  // handle very low momentum particles
911  if(!(TMath::Finite(P3L)) || P3L<.001)
912  {
913  LOG("INukeUtils",pINFO)
914  << "Particle 3 momentum small or non-finite: " << P3L
915  << "\n" << "--> Assigning .001 as new momentum";
916  P3tL = 0;
917  P3zL = .001;
918  P3L = .001;
919  E3L = TMath::Sqrt(P3L*P3L + M3*M3);
920  }
921 
922  // get random phi angle, distributed uniformally in 360 deg
923  PHI3 = 2 * kPi * rnd->RndFsi().Rndm();
924 
925  vP3L = P3zL*tbetadir + P3tL*tTrans;
926  vP3L.Rotate(PHI3,tbetadir);
927 
928  t4P3L.SetVect(vP3L);
929  t4P3L.SetE(E3L);
930 
931  t4P4L = t4P1buf + t4P2buf - t4P3L;
932  t4P4L-= TLorentzVector(0,0,0,bindE);
933  /*LOG("INukeUtils",pINFO) <<"GENIE:";
934  LOG("INukeUtils",pINFO) <<"E4L "<<t4P4L.E();
935  LOG("INukeUtils",pINFO) <<"P4zL "<<t4P4L.Vect()*tbetadir<<", P4tL "<<-1.*TMath::Sqrt(t4P4L.Vect().Mag2()-TMath::Power(t4P4L.Vect()*tbetadir,2.));
936  LOG("INukeUtils",pINFO) <<"P4L "<<t4P4L.Vect().Mag();
937  LOG("INukeUtils",pINFO) <<"C4L "<<t4P4L.Vect()*tbetadir/t4P4L.Vect().Mag(); */
938 
939  if(t4P4L.Mag2()<0 || t4P4L.E()<0)
940  {
941  LOG("INukeUtils",pNOTICE)<<"TwoBodyKinematics Failed: Target mass or energy is negative";
942  t4P3L.SetPxPyPzE(0,0,0,0);
943  t4P4L.SetPxPyPzE(0,0,0,0);
944  return false;
945  }
946 
947  if (bindE!=0) RemnP4 += TLorentzVector(0,0,0,bindE);
948  return true;
949 }
950 //___________________________________________________________________________
952  GHepRecord* ev, GHepParticle* p, int tcode, GHepParticle* s1, GHepParticle* s2, GHepParticle* s3,
953  bool DoFermi, double FermiFac, double FermiMomentum, const NuclearModelI* Nuclmodel)
954 {
955 
956  // Aaron Meyer (7/15/10)
957  //
958  // Kinematics used in utils::intranuke2014::PionProduction
959  // Handles the kinematics of three body scattering
960  //
961  // s1,s2,and s3 are pointers to particles with the PDG code that needs to be scattered
962  // the last four variables are for Fermi momentum and pauli blocking, default will use none of them
963 
964  // kinematic variables
965  double M1, M2, M3, M4, M5; // rest energies, in GeV
966  double P1L, P2L, P3L, P4L, P5L;
967  double E1L, E2L, E3L, E4L, E5L;
968  double E1CM, E2CM, P3tL;
969  double PizL, PitL, PiL, EiL;
970  double EiCM, P4CM2, E4CM2, E5CM2, P3CM, E3CM;
971  double beta, gm, beta2, gm2;
972  double P3zL, P4zL, P4tL, P5zL, P5tL;
973  double Et, M, theta1, theta2;
974  double P1zL, P2zL;
975  double theta3, theta4, phi3, phi4, theta5;
976  TVector3 tP2L, tP1L, tPtot, tbeta, tbetadir, tTrans, tP4L, tP5L;
977  TVector3 tP1zCM, tP2zCM, tP3L, tPiL, tbeta2, tbetadir2, tVect, tTrans2;
978 
979  // Library instance for reference
980  PDGLibrary * pLib = PDGLibrary::Instance();
981 
982  // random number generator
983  RandomGen * rnd = RandomGen::Instance();
984 
985  M1 = pLib->Find(p->Pdg())->Mass();
986  M2 = pLib->Find(tcode)->Mass();
987  M3 = pLib->Find(s1->Pdg())->Mass();
988  M4 = pLib->Find(s2->Pdg())->Mass();
989  M5 = pLib->Find(s3->Pdg())->Mass();
990 
991  // set up fermi target
992  Target target(ev->TargetNucleus()->Pdg());
993 
994  // handle fermi momentum
995  if(DoFermi)
996  {
997  target.SetHitNucPdg(tcode);
998  Nuclmodel->GenerateNucleon(target);
999  tP2L = FermiFac * Nuclmodel->Momentum3();
1000  P2L = tP2L.Mag();
1001  E2L = TMath::Sqrt(tP2L.Mag2() + M2*M2);
1002  }
1003  else
1004  {
1005  tP2L.SetXYZ(0.0, 0.0, 0.0);
1006  P2L = 0;
1007  E2L = M2;
1008  }
1009 
1010  // first sequence, handle 4th and 5th products as composite
1011  E1L = p->E();
1012 
1013  P1L = TMath::Sqrt(E1L*E1L - M1*M1);
1014  tP1L = p->P4()->Vect();
1015  tPtot = tP1L + tP2L;
1016 
1017  tbeta = tPtot * (1.0 / (E1L + E2L));
1018  tbetadir = tbeta.Unit();
1019  beta = tbeta.Mag();
1020  gm = 1.0 / TMath::Sqrt(1.0 - beta*beta);
1021 
1022  theta1 = tP1L.Angle(tbeta);
1023  theta2 = tP2L.Angle(tbeta);
1024  P1zL = P1L*TMath::Cos(theta1);
1025  P2zL = P2L*TMath::Cos(theta2);
1026  tVect.SetXYZ(1,0,0);
1027  if(TMath::Abs((tVect - tbetadir).Mag())<.01) tVect.SetXYZ(0,1,0);
1028  theta5 = tVect.Angle(tbetadir);
1029  tTrans = (tVect - TMath::Cos(theta5)*tbetadir).Unit();
1030 
1031  E1CM = gm*E1L - gm*beta*P1zL;
1032  tP1zCM = gm*P1zL*tbetadir - gm*tbeta*E1L;
1033  E2CM = gm*E2L - gm*beta*P2zL;
1034  tP2zCM = gm*P2zL*tbetadir - gm*tbeta*E2L;
1035  Et = E1CM + E2CM;
1036  M = (rnd->RndFsi().Rndm()*(Et - M3 - M4 - M5)) + (M4 + M5);
1037  E3CM = (Et*Et + M3*M3 - M*M)/(2*Et);
1038  EiCM = Et - E3CM;
1039  if(E3CM*E3CM - M3*M3<0)
1040  {
1041  LOG("INukeUtils",pNOTICE)
1042  << "PionProduction P3 has non-real momentum - retry kinematics";
1043  LOG("INukeUtils",pNOTICE) << "Energy, masses of 3 fs particales:"
1044  << E3CM << " " << M3 << " " << " " << M4 << " " << M5;
1046  exception.SetReason("PionProduction particle 3 has non-real momentum");
1047  throw exception;
1048  return false;
1049  }
1050  P3CM = TMath::Sqrt(E3CM*E3CM - M3*M3);
1051 
1052  theta3 = kPi * rnd->RndFsi().Rndm();
1053  theta4 = kPi * rnd->RndFsi().Rndm();
1054  phi3 = 2*kPi * rnd->RndFsi().Rndm();
1055  phi4 = 2*kPi * rnd->RndFsi().Rndm();
1056 
1057  P3zL = gm*beta*E3CM + gm*P3CM*TMath::Cos(theta3);
1058  P3tL = P3CM*TMath::Sin(theta3);
1059  PizL = gm*beta*EiCM - gm*P3CM*TMath::Cos(theta3);
1060  PitL = -P3CM*TMath::Sin(theta3);
1061 
1062  P3L = TMath::Sqrt(P3zL*P3zL + P3tL*P3tL);
1063  PiL = TMath::Sqrt(PizL*PizL + PitL*PitL);
1064  E3L = TMath::Sqrt(P3L*P3L + M3*M3);
1065  EiL = TMath::Sqrt(PiL*PiL + M*M);
1066 
1067  // handle very low momentum particles
1068  if(!(TMath::Finite(P3L)) || P3L < .001)
1069  {
1070  LOG("INukeUtils",pINFO)
1071  << "Particle 3 " << M3 << " momentum small or non-finite: " << P3L
1072  << "\n" << "--> Assigning .001 as new momentum";
1073  P3tL = 0;
1074  P3zL = .001;
1075  P3L = .001;
1076  E3L = TMath::Sqrt(P3L*P3L + M3*M3);
1077  }
1078 
1079  tP3L = P3zL*tbetadir + P3tL*tTrans;
1080  tPiL = PizL*tbetadir + PitL*tTrans;
1081  tP3L.Rotate(phi3,tbetadir);
1082  tPiL.Rotate(phi3,tbetadir);
1083 
1084  // second sequence, handle formally composite particles 4 and 5
1085  tbeta2 = tPiL * (1.0 / EiL);
1086  tbetadir2 = tbeta2.Unit();
1087  beta2 = tbeta2.Mag();
1088  gm2 = 1.0 / TMath::Sqrt(1.0 - beta2*beta2);
1089 
1090  E4CM2 = (M*M + M4*M4 - M5*M5) / (2*M);
1091  E5CM2 = M - E4CM2;
1092  P4CM2 = TMath::Sqrt(E4CM2*E4CM2 - M4*M4);
1093 
1094  tVect.SetXYZ(1,0,0);
1095  if(TMath::Abs((tVect - tbetadir2).Mag())<.01) tVect.SetXYZ(0,1,0);
1096  theta5 = tVect.Angle(tbetadir2);
1097  tTrans2 = (tVect - TMath::Cos(theta5)*tbetadir2).Unit();
1098 
1099  P4zL = gm2*beta2*E4CM2 + gm2*P4CM2*TMath::Cos(theta4);
1100  P4tL = P4CM2*TMath::Sin(theta4);
1101  P5zL = gm2*beta2*E5CM2 - gm2*P4CM2*TMath::Cos(theta4);
1102  P5tL = - P4tL;
1103 
1104  P4L = TMath::Sqrt(P4zL*P4zL + P4tL*P4tL);
1105  P5L = TMath::Sqrt(P5zL*P5zL + P5tL*P5tL);
1106  E4L = TMath::Sqrt(P4L*P4L + M4*M4);
1107  E5L = TMath::Sqrt(P5L*P5L + M5*M5);
1108 
1109  // handle very low momentum particles
1110  if(!(TMath::Finite(P4L)) || P4L < .001)
1111  {
1112  LOG("INukeUtils",pINFO)
1113  << "Particle 4 " << M4 << " momentum small or non-finite: " << P4L
1114  << "\n" << "--> Assigning .001 as new momentum";
1115  P4tL = 0;
1116  P4zL = .001;
1117  P4L = .001;
1118  E4L = TMath::Sqrt(P4L*P4L + M4*M4);
1119  }
1120  if(!(TMath::Finite(P5L)) || P5L < .001)
1121  {
1122  LOG("INukeUtils",pINFO)
1123  << "Particle 5 " << M5 << " momentum small or non-finite: " << P5L
1124  << "\n" << "--> Assigning .001 as new momentum";
1125  P5tL = 0;
1126  P5zL = .001;
1127  P5L = .001;
1128  E5L = TMath::Sqrt(P5L*P5L + M5*M5);
1129  }
1130 
1131  tP4L = P4zL*tbetadir2 + P4tL*tTrans2;
1132  tP5L = P5zL*tbetadir2 + P5tL*tTrans2;
1133  tP4L.Rotate(phi4,tbetadir2);
1134  tP5L.Rotate(phi4,tbetadir2);
1135 
1136  // pauli blocking
1137  if(P3L < FermiMomentum || ( pdg::IsNeutronOrProton(s2->Pdg()) && P4L < FermiMomentum ) )
1138  {
1139  LOG("INukeUtils",pNOTICE)
1140  << "PionProduction fails because of Pauli blocking - retry kinematics";
1142  exception.SetReason("PionProduction final state not determined");
1143  throw exception;
1144  return false;
1145  }
1146 
1147  // create scattered particles w/ appropriate momenta, code, and status
1148  // Set moms to be the moms of the hadron that was cloned
1149  s1->SetFirstMother(p->FirstMother());
1150  s2->SetFirstMother(p->FirstMother());
1151  s3->SetFirstMother(p->FirstMother());
1152  s1->SetLastMother(p->LastMother());
1153  s2->SetLastMother(p->LastMother());
1154  s3->SetLastMother(p->LastMother());
1155 
1156  TLorentzVector(tP3L,E3L);
1157  TLorentzVector(tP4L,E4L);
1158  TLorentzVector(tP5L,E5L);
1159 
1160  s1->SetMomentum(TLorentzVector(tP3L,E3L));
1161  s2->SetMomentum(TLorentzVector(tP4L,E4L));
1162  s3->SetMomentum(TLorentzVector(tP5L,E5L));
1163  int mode = kIMdHA;
1164  LOG ("INukeUtils",pDEBUG) << "in Pi Prod, mode = " << mode;
1165  if (mode==kIMdHN)
1166  {
1170  }
1171  else
1172  {
1176  }
1177  return true;
1178 }
1179 //___________________________________________________________________________
1181  GHepRecord* ev, GHepParticle* p, GHepParticle* s1, GHepParticle* s2, GHepParticle* s3, int &RemnA, int &RemnZ,
1182  TLorentzVector &RemnP4, bool DoFermi, double FermiFac, double FermiMomentum, const NuclearModelI* Nuclmodel)
1183 {
1184  // Aaron Meyer (7/15/2010)
1185  //
1186  // Handles pion production reactions in both hA and hN mode
1187  // Calculates fundamental cross sections from fit functions
1188  // Uses isospin relations to determine the rest of cross sections
1189  //
1190  // p is the probe particle
1191  // s1, s2, and s3 are the particles produced in the reaction
1192  // must set the status and add particles to the event record after returning from this method
1193  // return value for error checking
1194 
1195 
1196  // random number generator
1197  RandomGen * rnd = RandomGen::Instance();
1198 
1199  // library reference
1200  PDGLibrary * pLib = PDGLibrary::Instance();
1201 
1202  bool ptarg = false;
1203  int pcode = p->Pdg();
1204 
1205  int p1code = p->Pdg();
1206  // Randomly determine target and 1st product baryons
1207  int p3code = 0, p4code = 0, p5code = 0;
1208 
1209  //
1210  // Uses a fit curve log(sigma) = a - b/(T_pi - c) for pions
1211  // Fit parameters determined by Roman Tacik (4/3/09)
1212  // pi- & p cross sections are assumed to be the same as pi+ & n
1213  //
1214  // Fit curve for nucleons:
1215  // sigma = a*(1+b*e^(-c*(eta-d)^2))*(1-e^(-(f*eta)^g))*(1-e^(-h/eta^2))
1216  // 7 parameters (a,b,c,d,f,g,h)
1217  // eta is maximum kinematically allowed momentum of the pion, normalized by the mass
1218  // Uses isotopic spin decomposition of total cross sections
1219  //
1220 
1221  if ((p1code==kPdgPi0)||(p1code==kPdgPiP)||(p1code==kPdgPiM)) {
1222 
1223  double kine = 1000*p->KinE();
1224 
1225  // Determine cross sections
1226 
1227  // pion
1228  // pi- & p
1229  // -> pi0 & pi0 & n
1230  // a = 8.82; b = 573.2; c = 107.3;
1231  double xsec2pi0n = TMath::Max(0.,TMath::Exp(8.82 - (573.2/(kine-107.3))));
1232  // -> pi- & pi+ & n
1233  // a = 11.06; b = 985.9; c = 88.2;
1234  double xsecpippimn = TMath::Max(0.,TMath::Exp(11.06 - (985.9/(kine-88.2))));
1235  // -> pi- & pi0 & p
1236  // a = 9.58; b = 1229.4; c = 60.5;
1237  double xsecpimpi0p = TMath::Max(0.,TMath::Exp(9.58 - (1229.4/(kine-60.5))));
1238  double totpimp = xsec2pi0n + xsecpippimn + xsecpimpi0p;
1239 
1240 
1241  // pi+ & p
1242  // -> pi+ & pi+ & n
1243  // a = 5.64; b = 222.6; c = 150.0;
1244  double xsec2pipn = TMath::Max(0.,TMath::Exp(5.64 - (222.6/(kine-150.))));
1245  // -> pi+ & pi0 & p
1246  // a = 7.95; b = 852.6; c = 77.8;
1247  double xsecpippi0p = TMath::Max(0.,TMath::Exp(7.95 - (852.6/(kine-77.8))));
1248  double totpipp = xsec2pipn + xsecpippi0p;
1249 
1250  if (totpimp<=0 && totpipp<=0) {
1251  LOG("INukeUtils",pNOTICE) << "InelasticHN called below threshold energy";
1253  ev->AddParticle(*p);
1254  return false;
1255  }
1256 
1257  double xsecp, xsecn;
1258  switch (p1code) {
1259  case kPdgPi0: xsecp = 0.5 * (totpimp + totpipp); xsecn = xsecp; break;
1260  case kPdgPiP: xsecp = totpipp; xsecn = totpimp; break;
1261  case kPdgPiM: xsecp = totpimp; xsecn = totpipp; break;
1262  default:
1263  LOG("INukeUtils",pWARN) << "InelasticHN cannot handle probe: "
1264  << PDGLibrary::Instance()->Find(p1code)->GetName();
1266  exception.SetReason("PionProduction final state not determined");
1267  throw exception;
1268  return false;
1269  break;
1270  }
1271 
1272  // Normalize cross sections by Z or A-Z
1273 
1274  xsecp *= RemnZ;
1275  xsecn *= RemnA-RemnZ;
1276 
1277  // determine target
1278 
1279  double rand = rnd->RndFsi().Rndm() * (xsecp + xsecn);
1280  if (rand < xsecp) // proton target
1281  { rand /= RemnZ; ptarg = true;}
1282  else // neutron target
1283  { rand -= xsecp; rand /= RemnA-RemnZ; ptarg = false;}
1284 
1285 
1286  // determine final state
1287 
1288  if (((ptarg==true)&&(p1code==kPdgPiP))
1289  || ((ptarg==false)&&(p1code==kPdgPiM)))
1290  {
1291  if (rand < xsec2pipn) // pi+ & pi+ & n final state
1292  {
1293  p3code = (ptarg ? kPdgNeutron : kPdgProton);
1294  p4code = p1code;
1295  p5code = p4code;
1296  }
1297  else { // pi+ & pi0 & p final state
1298  p3code = (ptarg ? kPdgProton : kPdgNeutron);
1299  p4code = p1code;
1300  p5code = kPdgPi0;
1301  }
1302  }
1303  else if (((ptarg==false)&&(p1code==kPdgPiP))
1304  || ((ptarg==true)&&(p1code==kPdgPiM)))
1305  {
1306  if (rand < xsec2pi0n) // pi0 & pi0 & n final state
1307  {
1308  p3code = (ptarg ? kPdgNeutron : kPdgProton);
1309  p4code = kPdgPi0;
1310  p5code = p4code;
1311  }
1312  else if (rand < (xsec2pi0n + xsecpippimn)) // pi+ & pi- & n final state
1313  {
1314  p3code = (ptarg ? kPdgNeutron : kPdgProton);
1315  p4code = p1code;
1316  p5code = ((p1code==kPdgPiP) ? kPdgPiM : kPdgPiP);
1317  }
1318  else // pi0 & pi- & p final state
1319  {
1320  p3code = (ptarg ? kPdgProton : kPdgNeutron);
1321  p4code = p1code;
1322  p5code = kPdgPi0;
1323  }
1324  }
1325  else if (p1code==kPdgPi0)
1326  {
1327  rand = rnd->RndFsi().Rndm();
1328  if (rand < 191./270.)
1329  { // pi+ & pi- & p final state
1330  p3code = (ptarg ? kPdgProton : kPdgNeutron);
1331  p4code = kPdgPiP;
1332  p5code = kPdgPiM;
1333  }
1334  else if (rand < 7./135.)
1335  { // pi0 & pi0 & p final state
1336  p3code = (ptarg ? kPdgProton : kPdgNeutron);
1337  p4code = kPdgPi0;
1338  p5code = p4code;
1339  }
1340  else
1341  { // pi+ & pi0 & n final state
1342  p3code = (ptarg ? kPdgNeutron : kPdgProton);
1343  p4code = (ptarg ? kPdgPiP : kPdgPiM);
1344  p5code = kPdgPi0;
1345  }
1346  }
1347  else // unhandled
1348  {
1349  LOG("INukeUtils",pNOTICE) << "Pi production final state unable to be determined, picode, ptarg = " <<PDGLibrary::Instance()->Find(p1code)->GetName() << " " << PDGLibrary::Instance()->Find(ptarg)->GetName();
1351  exception.SetReason("PionProduction final state not determined");
1352  throw exception;
1353  return false;
1354  }
1355 
1356  } else if(p1code==kPdgProton||p1code==kPdgNeutron) //nucleon probes
1357  {
1358 
1359  double tote = p->Energy();
1360  double pMass = pLib->Find(2212)->Mass();
1361  double nMass = pLib->Find(2112)->Mass();
1362  double etapp2ppPi0 =
1363  utils::intranuke2014::CalculateEta(pMass,tote,pMass,pMass+pMass,pLib->Find(111)->Mass());
1364  double etapp2pnPip =
1365  utils::intranuke2014::CalculateEta(pLib->Find(p1code)->Mass(),tote,((p1code==kPdgProton)?pMass:nMass),
1366  pMass+nMass,pLib->Find(211)->Mass());
1367  double etapn2nnPip =
1368  utils::intranuke2014::CalculateEta(pMass,tote,nMass,nMass+nMass,pLib->Find(211)->Mass());
1369  double etapn2ppPim =
1370  utils::intranuke2014::CalculateEta(pMass,tote,nMass,pMass+pMass,pLib->Find(211)->Mass());
1371 
1372  if ((etapp2ppPi0<=0.)&&(etapp2pnPip<=0.)&&(etapn2nnPip<=0.)&&(etapn2ppPim<=0.)) { // below threshold
1373  LOG("INukeUtils",pNOTICE) << "PionProduction() called below threshold energy";
1375  exception.SetReason("PionProduction final state not possible - below threshold");
1376  throw exception;
1377  return false;
1378  }
1379 
1380  // calculate cross sections
1381  double xsecppPi0=0,xsecpnPiP=0,xsecnnPiP=0,xsecppPiM=0;
1382  if (etapp2ppPi0>0){
1383  xsecppPi0 = 4511*(1-.91*TMath::Exp(-TMath::Power((etapp2ppPi0-.705),2)));
1384  xsecppPi0 *= (1-TMath::Exp(-TMath::Power((.556*etapp2ppPi0),3.5)));
1385  xsecppPi0 *= (1-TMath::Exp(-56.897/(etapp2ppPi0*etapp2ppPi0)));
1386  xsecppPi0 = TMath::Max(0.,xsecppPi0);}
1387 
1388  if (etapp2pnPip>0){
1389  xsecpnPiP = 18840*(1-.808*TMath::Exp(-TMath::Power((etapp2pnPip-.371),2)));
1390  xsecpnPiP *= (1-TMath::Exp(-TMath::Power((.568*etapp2pnPip),3.2)));
1391  xsecpnPiP *= (1-TMath::Exp(-39.818/(etapp2pnPip*etapp2pnPip)));
1392  xsecpnPiP = TMath::Max(0.,xsecpnPiP);}
1393 
1394  if (etapn2nnPip>0){
1395  xsecnnPiP = 7670*(1-.479*TMath::Exp(-TMath::Power((etapn2nnPip-.947),2)));
1396  xsecnnPiP *= (1-TMath::Exp(-TMath::Power((.35*etapn2nnPip),3.2)));
1397  xsecnnPiP *= (1-TMath::Exp(-71.53/(etapn2nnPip*etapn2nnPip)));
1398  xsecnnPiP = TMath::Max(0.,xsecnnPiP);}
1399 
1400  if (etapn2ppPim>0){
1401  xsecppPiM = 7670*(1-.479*TMath::Exp(-TMath::Power((etapn2ppPim-.947),2)));
1402  xsecppPiM *= (1-TMath::Exp(-TMath::Power((.35*etapn2ppPim),3.2)));
1403  xsecppPiM *= (1-TMath::Exp(-71.53/(etapn2ppPim*etapn2ppPim)));
1404  xsecppPiM = TMath::Max(0.,xsecppPiM);}
1405 
1406  // double sigma11 = xsecppPi0;
1407  double sigma10 = TMath::Max(0.,xsecpnPiP - xsecppPi0); // Fundamental cross sections
1408  double sigma01 = TMath::Max(0.,xsecppPiM + xsecnnPiP - xsecppPi0);
1409 
1410  double xsecpnPi0 = .5*(sigma10 + sigma01);
1411  xsecpnPi0 = TMath::Max(xsecpnPi0,0.);
1412 
1413  LOG("INukeUtils",pDEBUG) << '\n' << "Cross section values: "<<'\n'
1414  << xsecppPi0 << " PP pi0" <<'\n'
1415  << xsecpnPiP << " PN pi+" <<'\n'
1416  << xsecnnPiP << " NN pi+" <<'\n'
1417  << xsecpnPi0 << " PN pi0";
1418 
1419  double xsecp=0,xsecn=0;
1420  switch (p1code) {
1421  case kPdgProton: xsecp=xsecppPi0+xsecpnPiP; xsecn=xsecppPiM+xsecnnPiP+xsecpnPi0; break;
1422  case kPdgNeutron: xsecp=xsecppPiM+xsecnnPiP+xsecpnPi0; xsecn=xsecppPi0+xsecpnPiP; break;
1423  default:
1424  LOG("INukeUtils",pWARN) << "InelasticHN cannot handle probe: "
1425  << PDGLibrary::Instance()->Find(p1code)->GetName();
1426  return false;
1427  break;
1428  }
1429 
1430  // Normalize cross sections by Z or (A-Z)
1431 
1432  xsecp *= RemnZ;
1433  xsecn *= RemnA-RemnZ;
1434 
1435  // determine target
1436 
1437  double rand = rnd->RndFsi().Rndm() * (xsecp + xsecn);
1438  if (rand < xsecp) // proton target
1439  { rand /= RemnZ; ptarg = true;}
1440  else // neutron target
1441  { rand -= xsecp; rand /= RemnA-RemnZ; ptarg = false;}
1442 
1443  if(p1code==kPdgProton) // Cross sections not explicitly given are calculated from isospin relations
1444  {
1445  if(ptarg)
1446  {
1447  if (rand<xsecppPi0) {p3code=kPdgProton; p4code=kPdgProton; p5code=kPdgPi0;}
1448  else {p3code=kPdgProton; p4code=kPdgNeutron; p5code=kPdgPiP;}
1449  }
1450  else
1451  {
1452  if (rand<xsecnnPiP) {p3code=kPdgNeutron; p4code=kPdgNeutron; p5code=kPdgPiP;}
1453  else if (rand<xsecppPiM+xsecnnPiP) {p3code=kPdgProton; p4code=kPdgProton; p5code=kPdgPiM;}
1454  else {p3code=kPdgProton; p4code=kPdgNeutron; p5code=kPdgPi0;}
1455  }
1456  }
1457  else if(p1code==kPdgNeutron)
1458  {
1459  if(ptarg)
1460  {
1461  if (rand<xsecnnPiP) {p3code=kPdgNeutron; p4code=kPdgNeutron; p5code=kPdgPiP;}
1462  else if (rand<xsecppPiM+xsecnnPiP) {p3code=kPdgProton; p4code=kPdgProton; p5code=kPdgPiM;}
1463  else {p3code=kPdgProton; p4code=kPdgNeutron; p5code=kPdgPi0;}
1464  }
1465  else
1466  {
1467  if (rand<xsecpnPiP) {p3code=kPdgNeutron; p4code=kPdgProton; p5code=kPdgPiM;}
1468  else {p3code=kPdgNeutron; p4code=kPdgNeutron; p5code=kPdgPi0;}
1469  }
1470  }
1471  }
1472  else
1473  {
1474  LOG("INukeUtils",pWARN)
1475  << "Unable to handle probe (=" << p1code << ") in InelasticHN()";
1476  return false;
1477  }
1478 
1479  // determine if reaction is allowed
1480  if ( RemnA < 1 )
1481  {
1482  LOG("INukeUtils",pNOTICE) << "PionProduction() failed : no nucleons to produce pions";
1483  return false;
1484  }
1485  else if ( RemnZ + ((pcode==kPdgProton || pcode==kPdgPiP)?1:0) - ((pcode==kPdgPiM)?1:0)
1486  < ((p3code==kPdgProton || p3code==kPdgPiP)?1:0) - ((p3code==kPdgPiM)?1:0)
1487  + ((p4code==kPdgProton || p4code==kPdgPiP)?1:0) - ((p4code==kPdgPiM)?1:0)
1488  + ((p5code==kPdgProton || p5code==kPdgPiP)?1:0) - ((p5code==kPdgPiM)?1:0) )
1489  {
1490  LOG("INukeUtils",pNOTICE) << "PionProduction() failed : too few protons in nucleus";
1492  exception.SetReason("PionProduction fails - too few protons available");
1493  throw exception;
1494  return false;
1495  }
1496 
1497  s1->SetPdgCode(p3code);
1498  s2->SetPdgCode(p4code);
1499  s3->SetPdgCode(p5code);
1500 
1502  ev,p,(ptarg?kPdgProton:kPdgNeutron),s1,s2,s3,DoFermi,FermiFac,FermiMomentum,Nuclmodel))
1503  {
1504  // okay, handle remnants and return true
1505  // assumes first particle is always the nucleon,
1506  // second can be either nucleon or pion
1507  // last always pion
1508  if (pcode==kPdgProton || pcode==kPdgPiP) RemnZ++;
1509  if (pcode==kPdgPiM) RemnZ--;
1510  if (pdg::IsPion(pcode)) RemnA--;
1511  if (pdg::IsProton(p3code)) RemnZ--;
1512  if (pdg::IsNeutronOrProton(p4code)) RemnA--;
1513  if (p4code==kPdgPiP || p4code==kPdgProton) RemnZ--;
1514  if (p4code==kPdgPiM) RemnZ++;
1515  if (p5code==kPdgPiP) RemnZ--;
1516  if (p5code==kPdgPiM) RemnZ++;
1517 
1518  LOG("INukeUtils",pDEBUG) << "Remnant (A,Z) = (" <<RemnA<<','<<RemnZ<<')';
1519 
1520  RemnP4 -= *s1->P4() + *s2->P4() + *s3->P4() - *p->P4();
1521  return true;
1522  }
1523  else {
1525  exception.SetReason("PionProduction final state not determined");
1526  throw exception;
1527  return false;
1528  }
1529 }
1530 //___________________________________________________________________________
1531 double genie::utils::intranuke2014::CalculateEta(double Minc, double nrg, double Mtarg,
1532  double Mtwopart, double Mpi)
1533 {
1534  //Aaron Meyer (1/20/2010)
1535 
1536  //Used to calculate the maximum kinematically allowed CM frame pion momentum
1537  // ke in MeV, eta normalized by pion mass
1538  // approximated by taking two ejected nucleons to be one particle of the same mass
1539  //For pion cross sections, in utils::intranuke2014::PionProduction
1540 
1541  //LOG("INukeUtils",pDEBUG) << "Input values: "<<Minc<<' '<<nrg<<' '<<Mtarg<<' '<<Mtwopart<<' '<<Mpi;
1542  double Scm = Minc*Minc + Mtarg*Mtarg + 2*Mtarg*nrg;
1543  double eta = 0;
1544  eta= TMath::Power(Scm,2) + TMath::Power(Mtwopart,4) + TMath::Power(Mpi,4);
1545  eta-= 2*TMath::Power(Mtwopart*Mpi,2);
1546  eta-= 2*Scm*TMath::Power(Mtwopart,2);
1547  eta-= 2*Scm*TMath::Power(Mpi,2);
1548  eta = TMath::Power(eta/Scm,1./2.);
1549  eta/= (2*Mpi);
1550 
1551  eta=TMath::Max(eta,0.);
1552  return eta;
1553 }
1554 //___________________________________________________________________________
1555 // Generic Phase Space Decay methods
1556 //___________________________________________________________________________
1558  GHepRecord* ev, GHepParticle* p, const PDGCodeList & pdgv,
1559  TLorentzVector &RemnP4, double NucRmvE, EINukeMode mode)
1560 {
1561 // General method decaying the input particle system 'pdgv' with available 4-p
1562 // given by 'pd'. The decayed system is used to populate the input TMCParticle
1563 // array starting from the slot 'offset'.
1564 //
1565  LOG("INukeUtils", pINFO) << "*** Performing a Phase Space Decay";
1566  assert(pdgv.size() > 1);
1567 
1568  // Get the decay product masses & names
1569 
1570  ostringstream state_sstream;
1571  state_sstream << "( ";
1572  vector<int>::const_iterator pdg_iter;
1573  int i = 0;
1574  double * mass = new double[pdgv.size()];
1575  double mass_sum = 0;
1576  for(pdg_iter = pdgv.begin(); pdg_iter != pdgv.end(); ++pdg_iter) {
1577  int pdgc = *pdg_iter;
1578  double m = PDGLibrary::Instance()->Find(pdgc)->Mass();
1579  string nm = PDGLibrary::Instance()->Find(pdgc)->GetName();
1580  mass[i++] = m;
1581  mass_sum += m;
1582  state_sstream << nm << " ";
1583  }
1584  state_sstream << ")";
1585 
1586  TLorentzVector * pd = p->GetP4(); // incident particle 4p
1587 
1588  bool is_nuc = pdg::IsNeutronOrProton(p->Pdg());
1589  bool is_kaon = p->Pdg()==kPdgKP || p->Pdg()==kPdgKM;
1590  // update available energy -> init (mass + kinetic) + sum of f/s masses
1591  // for pion only. Probe mass not available for nucleon, kaon
1592  double availE = pd->Energy() + mass_sum;
1593  if(is_nuc||is_kaon) availE -= p->Mass();
1594  pd->SetE(availE);
1595 
1596  LOG("INukeUtils",pNOTICE)
1597  << "size, mass_sum, availE, pd mass, energy = " << pdgv.size() << " "
1598  << mass_sum << " " << availE << " " << p->Mass() << " " << p->Energy() ;
1599 
1600  // compute the 4p transfer to the hadronic blob
1601  double dE = mass_sum;
1602  if(is_nuc||is_kaon) dE -= p->Mass();
1603  TLorentzVector premnsub(0,0,0,dE);
1604  RemnP4 -= premnsub;
1605 
1606  LOG("INukeUtils", pINFO)
1607  << "Final state = " << state_sstream.str() << " has N = " << pdgv.size()
1608  << " particles / total mass = " << mass_sum;
1609  LOG("INukeUtils", pINFO)
1610  << "Composite system p4 = " << utils::print::P4AsString(pd);
1611 
1612  // Set the decay
1613  TGenPhaseSpace GenPhaseSpace;
1614  bool permitted = GenPhaseSpace.SetDecay(*pd, pdgv.size(), mass);
1615  if(!permitted) {
1616  LOG("INukeUtils", pERROR)
1617  << " *** Phase space decay is not permitted \n"
1618  << " Total particle mass = " << mass_sum << "\n"
1619  << " Decaying system p4 = " << utils::print::P4AsString(pd);
1620 
1621  // clean-up and return
1622  RemnP4 += premnsub;
1623  delete [] mass;
1624  delete pd;
1625  return false;
1626  }
1627 
1628  // The decay is permitted - add the incident particle at the event record
1629  // and mark is as 'Nucleon Cluster Target' (used to be confusing 'Decayed State')
1630  p->SetStatus(kIStNucleonClusterTarget); //kIStDecayedState);
1632  ev->AddParticle(*p);
1633 
1634  // Get the maximum weight
1635  double wmax = -1;
1636  for(int idec=0; idec<200; idec++) {
1637  double w = GenPhaseSpace.Generate();
1638  wmax = TMath::Max(wmax,w);
1639  }
1640  assert(wmax>0);
1641 
1642  LOG("INukeUtils", pINFO)
1643  << "Max phase space gen. weight @ current hadronic interaction: " << wmax;
1644 
1645  // Generate an unweighted decay
1646 
1647  RandomGen * rnd = RandomGen::Instance();
1648  wmax *= 1.2;
1649 
1650  bool accept_decay=false;
1651  unsigned int itry=0;
1652 
1653  while(!accept_decay)
1654  {
1655  itry++;
1656 
1657  if(itry>kMaxUnweightDecayIterations) {
1658  // report, clean-up and return
1659  LOG("INukeUtils", pNOTICE)
1660  << "Couldn't generate an unweighted phase space decay after "
1661  << itry << " attempts";
1662  delete [] mass;
1663  delete pd;
1664  return false;
1665  }
1666 
1667  double w = GenPhaseSpace.Generate();
1668  double gw = wmax * rnd->RndFsi().Rndm();
1669 
1670  if(w > wmax) {
1671  LOG("INukeUtils", pNOTICE)
1672  << "Decay weight = " << w << " > max decay weight = " << wmax;
1673  }
1674 
1675  LOG("INukeUtils", pNOTICE) << "Decay weight = " << w << " / R = " << gw;
1676  accept_decay = (gw<=w);
1677  }
1678 
1679  // Insert final state products into the event record
1680  // - the particles are added as daughters of the decayed state
1681  // - the particles are marked as final stable state (in hA mode)
1682  i=0;
1683  int mom = ev->ParticlePosition(p);
1684  LOG("INukeUtils", pNOTICE) << "mother index = " << mom;
1687 
1688  TLorentzVector * v4 = p->GetX4();
1689 
1690  double checkpx = p->Px();
1691  double checkpy = p->Py();
1692  double checkpz = p->Pz();
1693  double checkE = p->E();
1694 
1695  for(pdg_iter = pdgv.begin(); pdg_iter != pdgv.end(); ++pdg_iter) {
1696 
1697  //-- current PDG code
1698  int pdgc = *pdg_iter;
1699  bool isnuc = pdg::IsNeutronOrProton(pdgc);
1700 
1701  //-- get the 4-momentum of the i-th final state particle
1702  TLorentzVector * p4fin = GenPhaseSpace.GetDecay(i++);
1703 
1704  //-- intranuke no longer throws "bindinos" but adds all the energy
1705  // not going at a simulated f/s particle at a "hadronic blob"
1706  // representing the remnant system: do the binding energy subtraction
1707  // here & update the remnant hadronic system 4p
1708  double M = PDGLibrary::Instance()->Find(pdgc)->Mass();
1709  double En = p4fin->Energy();
1710  double KE = En-M;
1711  double dE_leftover = TMath::Min(NucRmvE, KE);
1712  KE -= dE_leftover;
1713  En = KE+M;
1714  double pmag_old = p4fin->P();
1715  double pmag_new = TMath::Sqrt(TMath::Max(0.,En*En-M*M));
1716  double scale = pmag_new / pmag_old;
1717  double pxn = scale * p4fin->Px();
1718  double pyn = scale * p4fin->Py();
1719  double pzn = scale * p4fin->Pz();
1720 
1721  TLorentzVector p4n(pxn,pyn,pzn,En);
1722  // LOG("INukeUtils", pNOTICE) << "Px = " << pxn << " Py = " << pyn
1723  // << " Pz = " << pzn << " E = " << KE;
1724  checkpx -= pxn;
1725  checkpy -= pyn;
1726  checkpz -= pzn;
1727  checkE -= KE;
1728 
1729  if (mode==kIMdHA &&
1730  (pdgc==kPdgPiP || pdgc==kPdgPi0 || pdgc==kPdgPiM) )
1731  {
1732  if (p4n.Vect().Mag()>=0.001)
1733  {
1734  GHepParticle new_particle(pdgc, ist_pi, mom,-1,-1,-1, p4n, *v4);
1735  ev->AddParticle(new_particle);
1736  }
1737  else
1738  {
1739  // Momentum too small, assign a non-zero momentum to the particle
1740  // Conserve momentum with the remnant nucleus
1741 
1742  LOG("INukeUtils", pINFO)<<"Momentum too small; assigning 0.001 as new momentum";
1743 
1744  double phi = 2*kPi*rnd->RndFsi().Rndm();
1745  double omega = 2*rnd->RndFsi().Rndm();
1746  // throw number against solid angle for uniform distribution
1747 
1748  double E4n = TMath::Sqrt(0.001*0.001+M*M);
1749  p4n.SetPxPyPzE(0.001,0,0,E4n);
1750  p4n.Rotate(TMath::ACos(1-omega),TVector3(0,0,1));
1751  p4n.Rotate(phi,TVector3(1,0,0));
1752 
1753  RemnP4 -= (p4n - TLorentzVector(0,0,0,M));
1754 
1755  GHepParticle new_particle(pdgc, ist, mom,-1,-1,-1, p4n, *v4);
1756  ev->AddParticle(new_particle);
1757  }
1758  }
1759  else
1760  {
1761  GHepParticle new_particle(pdgc, ist, mom,-1,-1,-1, p4n, *v4);
1762 
1763  if(isnuc) new_particle.SetRemovalEnergy(0.);
1764  ev->AddParticle(new_particle);
1765  }
1766 
1767  double dpx = (1-scale)*p4fin->Px();
1768  double dpy = (1-scale)*p4fin->Py();
1769  double dpz = (1-scale)*p4fin->Pz();
1770  TLorentzVector premnadd(dpx,dpy,dpz,dE_leftover);
1771  RemnP4 += premnadd;
1772  }
1773  LOG("INukeUtils", pNOTICE) << "check conservation: Px = " << checkpx << " Py = " << checkpy
1774  << " Pz = " << checkpz << " E = " << checkE;
1775 
1776  // Clean-up
1777  delete [] mass;
1778  delete pd;
1779  delete v4;
1780 
1781  return true;
1782 }
1783 
const int kPdgP33m1232_DeltaPP
Definition: PDGCodes.h:88
static const double m
Definition: Units.h:79
An exception thrown by SimulateHadronState for kinematics problems. TwoBodyCollision/Kinematics used ...
void SetFirstMother(int m)
Definition: GHepParticle.h:131
bool IsPion(int pdgc)
Definition: PDGUtils.cxx:296
Basic constants.
virtual GHepParticle * Particle(int position) const
Definition: GHepRecord.cxx:148
virtual int ParticlePosition(int pdg, GHepStatus_t i, int start=0) const
Definition: GHepRecord.cxx:181
TLorentzVector * GetX4(void) const
TRandom3 & RndFsi(void) const
rnd number generator used by intranuclear cascade monte carlos
Definition: RandomGen.h:60
#include "Numerical/GSFunc.h"
Definition: AlgCmp.h:26
#define pERROR
Definition: Messenger.h:50
bool TwoBodyCollision(GHepRecord *ev, int pcode, int tcode, int scode, int s2code, double C3CM, GHepParticle *p, GHepParticle *t, int &RemnA, int &RemnZ, TLorentzVector &RemnP4, EINukeMode mode=kIMdHA)
Intranuke utility functions.
double E(void) const
Get energy.
Definition: GHepParticle.h:90
static RandomGen * Instance()
Access instance.
Definition: RandomGen.cxx:79
double Density(double r, int A, double ring=0.)
enum genie::EGHepStatus GHepStatus_t
static const double MeV
Definition: Units.h:130
string P4AsString(const TLorentzVector *p)
Definition: PrintUtils.cxx:34
static const unsigned int kMaxUnweightDecayIterations
Definition: Controls.h:61
Pure abstract base class. Defines the NuclearModelI interface to be implemented by any physics model ...
Definition: NuclearModelI.h:42
double Mass(Resonance_t res)
resonance mass (GeV)
EINukeMode
Definition: INukeMode.h:29
void SetMomentum(const TLorentzVector &p4)
double Mass(void) const
Mass that corresponds to the PDG code.
A singleton holding random number generator classes. All random number generation in GENIE should tak...
Definition: RandomGen.h:30
double CalculateEta(double Minc, double ke, double Mtarg, double Mtwopart, double Mpi)
double Pz(void) const
Get Pz.
Definition: GHepParticle.h:89
void StepParticle(GHepParticle *p, double step, double nuclear_radius=-1.)
Step particle.
double Energy(void) const
Get energy.
Definition: GHepParticle.h:91
void Equilibrium(GHepRecord *ev, GHepParticle *p, int &RemnA, int &RemnZ, TLorentzVector &RemnP4, bool DoFermi, double FermiFac, const NuclearModelI *Nuclmodel, double NucRmvE, EINukeMode mode=kIMdHN)
A list of PDG codes.
Definition: PDGCodeList.h:33
double Px(void) const
Get Px.
Definition: GHepParticle.h:87
bool ThreeBodyKinematics(GHepRecord *ev, GHepParticle *p, int tcode, GHepParticle *s1, GHepParticle *s2, GHepParticle *s3, bool DoFermi=false, double FermiFac=0, double FermiMomentum=0, const NuclearModelI *Nuclmodel=(const NuclearModelI *) 0)
bool CompareMomentum(const GHepParticle *p) const
bool TwoBodyKinematics(double M3, double M4, TLorentzVector tP1L, TLorentzVector tP2L, TLorentzVector &tP3L, TLorentzVector &tP4L, double C3CM, TLorentzVector &RemnP4, double bindE=0)
int LastMother(void) const
Definition: GHepParticle.h:68
int Pdg(void) const
Definition: GHepParticle.h:64
int FirstMother(void) const
Definition: GHepParticle.h:67
string Name(void) const
Name that corresponds to the PDG code.
const int kPdgCompNuclCluster
Definition: PDGCodes.h:188
void SetPosition(const TLorentzVector &v4)
bool IsProton(int pdgc)
Definition: PDGUtils.cxx:306
#define LOG(stream, priority)
A macro that returns the requested log4cpp::Category appending a string (using the FILE...
Definition: Messenger.h:87
intermediate_table::const_iterator const_iterator
double MeanFreePath(int pdgc, const TLorentzVector &x4, const TLorentzVector &p4, double A, double Z, double nRpi=0.5, double nRnuc=1.0)
Mean free path (pions, nucleons)
const int kPdgKM
Definition: PDGCodes.h:147
double MeanFreePath_Delta(int pdgc, const TLorentzVector &x4, const TLorentzVector &p4, double A)
Mean free path (Delta++ test)
void PreEquilibrium(GHepRecord *ev, GHepParticle *p, int &RemnA, int &RemnZ, TLorentzVector &RemnP4, bool DoFermi, double FermiFac, const NuclearModelI *Nuclmodel, double NucRmvE, EINukeMode mode=kIMdHN)
const int kPdgGamma
Definition: PDGCodes.h:163
TLorentzVector * GetP4(void) const
bool ComparePdgCodes(const GHepParticle *p) const
A Neutrino Interaction Target. Is a transparent encapsulation of quite different physical systems suc...
Definition: Target.h:41
const int kPdgKP
Definition: PDGCodes.h:146
bool TwoBodyKinematics(double M3, double M4, TLorentzVector tP1L, TLorentzVector tP2L, TLorentzVector &tP3L, TLorentzVector &tP4L, double C3CM, TLorentzVector &RemnP4, double bindE=0)
Definition: INukeUtils.cxx:753
const int kPdgPiP
Definition: PDGCodes.h:132
const int kPdgPi0
Definition: PDGCodes.h:134
virtual GHepParticle * TargetNucleus(void) const
Definition: GHepRecord.cxx:323
virtual vector< int > * GetStableDescendants(int position) const
Definition: GHepRecord.cxx:218
#define pINFO
Definition: Messenger.h:53
bool PionProduction(GHepRecord *ev, GHepParticle *p, GHepParticle *s1, GHepParticle *s2, GHepParticle *s3, int &RemnA, int &RemnZ, TLorentzVector &RemnP4, bool DoFermi, double FermiFac, double FermiMomentum, const NuclearModelI *Nuclmodel)
Misc GENIE control constants.
#define pWARN
Definition: Messenger.h:51
p
Definition: test.py:228
bool CompareStatusCodes(const GHepParticle *p) const
void SetRemovalEnergy(double Erm)
static const double A
Definition: Units.h:82
double Dist2Exit(const TLorentzVector &x4, const TLorentzVector &p4, double A, double NR=3, double R0=1.4)
Distance to exit.
double KinE(bool mass_from_pdg=false) const
Get kinetic energy.
static PDGLibrary * Instance(void)
Definition: PDGLibrary.cxx:45
bool PhaseSpaceDecay(GHepRecord *ev, GHepParticle *p, const PDGCodeList &pdgv, TLorentzVector &RemnP4, double NucRmvE, EINukeMode mode=kIMdHA)
general phase space decay method
void SetLastMother(int m)
Definition: GHepParticle.h:132
Singleton class to load & serve a TDatabasePDG.
Definition: PDGLibrary.h:27
TLorentzVector * X4(void) const
Definition: GHepParticle.h:80
void SetStatus(GHepStatus_t s)
Definition: GHepParticle.h:125
virtual TVector3 Momentum3(void) const
Definition: NuclearModelI.h:59
bool IsNeutronOrProton(int pdgc)
Definition: PDGUtils.cxx:321
TParticlePDG * Find(int pdgc)
Definition: PDGLibrary.cxx:63
const int kPdgPiM
Definition: PDGCodes.h:133
virtual bool GenerateNucleon(const Target &) const =0
static const double fm2
Definition: Units.h:84
virtual void AddParticle(const GHepParticle &p)
Definition: GHepRecord.cxx:526
double Dist2ExitMFP(int pdgc, const TLorentzVector &x4, const TLorentzVector &p4, double A, double Z, double NR=3, double R0=1.4)
Distance to exit.
const int kPdgProton
Definition: PDGCodes.h:62
#define pNOTICE
Definition: Messenger.h:52
string X4AsString(const TLorentzVector *x)
Definition: PrintUtils.cxx:64
TLorentzVector * P4(void) const
Definition: GHepParticle.h:79
static const double mb
Definition: Units.h:87
const int kPdgNeutron
Definition: PDGCodes.h:64
GENIE&#39;s GHEP MC event record.
Definition: GHepRecord.h:46
static const double kPi
Definition: Constants.h:38
Most commonly used PDG codes. A set of utility functions to handle PDG codes is provided in PDGUtils...
STDHEP-like event record entry that can fit a particle or a nucleus.
Definition: GHepParticle.h:40
string Vec3AsString(const TVector3 *vec)
Definition: PrintUtils.cxx:87
double ProbSurvival(int pdgc, const TLorentzVector &x4, const TLorentzVector &p4, double A, double Z, double mfp_scale_factor=1.0, double nRpi=0.5, double nRnuc=1.0, double NR=3, double R0=1.4)
Hadron survival probability.
Root of GENIE utility namespaces.
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
void push_back(int pdg_code)
Definition: PDGCodeList.cxx:67
static INukeHadroData2014 * Instance(void)
#define pDEBUG
Definition: Messenger.h:54
void SetPdgCode(int c)
double Py(void) const
Get Py.
Definition: GHepParticle.h:88