Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
epemGun.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file epemGun.cc
1 /*******************************************************************************
2  * Copyright (c) The JETSCAPE Collaboration, 2018
3  *
4  * Modular, task-based framework for simulating all aspects of heavy-ion collisions
5  *
6  * For the list of contributors see AUTHORS.
7  *
8  * Report issues at https://github.com/JETSCAPE/JETSCAPE/issues
9  *
10  * or via email to bugs.jetscape@gmail.com
11  *
12  * Distributed under the GNU General Public License 3.0 (GPLv3 or later).
13  * See COPYING for details.
14  ******************************************************************************/
15 
16 // Create e+e- -> qqbar processes with Pythia and return the two inital partons with set virtualities
17 
18 #include "epemGun.h"
19 #include <sstream>
20 
21 #define MAGENTA "\033[35m"
22 
23 using namespace std;
24 
25 // Register the module with the base class
27 
29 
31 
32  JSDEBUG << "Initialize epemGun";
33  VERBOSE(8);
34 
35  // Show initialization at INFO level
36  readString("Init:showProcesses = off");
37  readString("Init:showChangedSettings = off");
38  readString("Init:showMultipartonInteractions = off");
39  readString("Init:showChangedParticleData = off");
40  if (JetScapeLogger::Instance()->GetInfo()) {
41  readString("Init:showProcesses = on");
42  readString("Init:showChangedSettings = on");
43  readString("Init:showMultipartonInteractions = on");
44  readString("Init:showChangedParticleData = on");
45  }
46 
47  // No event record printout.
48  readString("Next:numberShowInfo = 0");
49  readString("Next:numberShowProcess = 0");
50  readString("Next:numberShowEvent = 0");
51 
52  // Standard settings
53  readString("HadronLevel:all = off");
54  //readString("PartonLevel:ISR = on");
55  //readString("PartonLevel:MPI = on");
56  readString("PartonLevel:FSR = off");
57  //readString("PromptPhoton:all=on");
58  readString("PDF:lepton = off");
59  readString("WeakSingleBoson:ffbar2gmz=on"); //Scattering f fbar → gamma^*/Z^0, with full interference between the gamma^* and Z^0
60  readString("WeakDoubleBoson:all=on"); //Common switch for the group of pair production of gamma^*/Z^0 and W^+-
61  readString("WeakBosonExchange:all=on"); //Common switch for the group of gamma^*/Z^0 or W^+- exchange between two fermions
62 
63  //Stuff I added. Ask if I'm allowed to just do this.
64  readString("23:onMode = off");
65  readString("23:onIfAny = 1 2 3 4 5");
66 
67  // For parsing text
69  numbf.setf(ios::fixed, ios::floatfield);
70  numbf.setf(ios::showpoint);
71  numbf.precision(1);
73 
74  std::string s = GetXMLElementText({"Hard", "epemGun", "name"});
75  SetId(s);
76  // cout << s << endl;
77 
78  // SC: read flag for FSR
79  //FSR_on = GetXMLElementInt({"Hard", "epemGun", "FSR_on"});
80  //if (FSR_on)
81  // readString("PartonLevel:FSR = on");
82  //else
83  // readString("PartonLevel:FSR = off");
84 
85  //pTHatMin = GetXMLElementDouble({"Hard", "epemGun", "pTHatMin"});
86  //pTHatMax = GetXMLElementDouble({"Hard", "epemGun", "pTHatMax"});
87 
88  //JSINFO << MAGENTA << "epem Gun with FSR_on: " << FSR_on;
89  //JSINFO << MAGENTA << "epem Gun with " << pTHatMin << " < pTHat < "
90  // << pTHatMax;
91 
92  //numbf.str("PhaseSpace:pTHatMin = ");
93  //numbf << pTHatMin;
94  //readString(numbf.str());
95  //numbf.str("PhaseSpace:pTHatMax = ");
96  //numbf << pTHatMax;
97  //readString(numbf.str());
98 
99  // random seed
100  // xml limits us to unsigned int :-/ -- but so does 32 bits Mersenne Twist
101  tinyxml2::XMLElement *RandomXmlDescription = GetXMLElement({"Random"});
102  readString("Random:setSeed = on");
103  numbi.str("Random:seed = ");
104  unsigned int seed = 0;
105  if (RandomXmlDescription) {
106  tinyxml2::XMLElement *xmle =
107  RandomXmlDescription->FirstChildElement("seed");
108  if (!xmle)
109  throw std::runtime_error("Cannot parse xml");
110  xmle->QueryUnsignedText(&seed);
111  } else {
112  JSWARN << "No <Random> element found in xml, seeding to 0";
113  }
114  VERBOSE(7) << "Seeding pythia to " << seed;
115  numbi << seed;
116  readString(numbi.str());
117 
118  // Species
119  readString("Beams:idA = 11");
120  readString("Beams:idB = -11");
121 
122  // Energy
123  eCM = GetXMLElementDouble({"Hard", "epemGun", "eCM"});
124  numbf.str("Beams:eCM = ");
125  numbf << eCM;
126  readString(numbf.str());
127 
128  std::stringstream lines;
129  lines << GetXMLElementText({"Hard", "epemGun", "LinesToRead"}, false);
130  int i = 0;
131  while (std::getline(lines, s, '\n')) {
132  if (s.find_first_not_of(" \t\v\f\r") == s.npos)
133  continue; // skip empty lines
134  VERBOSE(7) << "Also reading in: " << s;
135  readString(s);
136  }
137 
138  // And initialize
139  if (!init()) { // Pythia>8.1
140  throw std::runtime_error("Pythia init() failed.");
141  }
142 
143  // Initialize random number distribution
144  ZeroOneDistribution = uniform_real_distribution<double>{0.0, 1.0};
145 
146 }
147 
149  VERBOSE(1) << "Run Hard Process : " << GetId() << " ...";
150  VERBOSE(8) << "Current Event #" << GetCurrentEvent();
151  //Reading vir_factor from xml for MATTER
152  double vir_factor = GetXMLElementDouble({"Eloss", "Matter", "vir_factor"});
153 
154  bool flag62 = false;
155  vector<Pythia8::Particle> p62;
156 
157  // sort by pt
158  struct greater_than_pt {
159  inline bool operator()(const Pythia8::Particle &p1,
160  const Pythia8::Particle &p2) {
161  return (p1.pT() > p2.pT());
162  }
163  };
164 
165  do {
166  next();
167  //event.list();
168  p62.clear();
169 
170  for (int parid = 0; parid < event.size(); parid++) {
171  if (parid < 3)
172  continue; // 0, 1, 2: total event and beams
173  Pythia8::Particle &particle = event[parid];
174 
175  //replacing diquarks with antiquarks (and anti-dq's with quarks)
176  //the id is set to the heaviest quark in the diquark (except down quark)
177  //this technically violates baryon number conservation over the entire event
178  //also can violate electric charge conservation
179  if( (std::abs(particle.id()) > 1100) && (std::abs(particle.id()) < 6000) && ((std::abs(particle.id())/10)%10 == 0) ){
180  //if(particle.id() > 0){particle.id( -1*particle.id()/1000 );}
181  //else{particle.id( particle.id()/1000 );}
182  particle.id( -1*particle.id()/1000 ); //Changed from previous two lines.
183  }
184 
185  //if (!FSR_on) {
186  // only accept particles after MPI
187  //if (particle.status() != 62) {continue;}
188  if ( particle.status()<0 ) {continue;}
189  // only accept gluons and quarks
190  // Also accept Gammas to put into the hadron's list
191  if (fabs(particle.id()) > 6 && (particle.id() != 21 && particle.id() != 22)) {
192  continue;
193  }
194 
195  //if(particle.id()==22) cout<<"########this is a photon!######" <<endl;
196  // accept
197  /*} else { // FSR_on true: use Pythia vacuum shower instead of MATTER
198  if (!particle.isFinal())
199  continue;
200  // only accept gluons and quarks
201  // Also accept Gammas to put into the hadron's list
202  if (fabs(particle.id()) > 5 &&
203  (particle.id() != 21 && particle.id() != 22))
204  continue;
205  }*/
206  p62.push_back(particle);
207  }
208 
209  // if you want at least 2
210  if (p62.size() < 2)
211  continue;
212  //if ( p62.size() < 1 ) continue;
213 
214  // Now have all candidates, sort them
215  // sort by pt
216  std::sort(p62.begin(), p62.end(), greater_than_pt());
217  // // check...
218  // for (auto& p : p62 ) cout << p.pT() << endl;
219 
220  flag62 = true;
221 
222  } while (!flag62);
223 
224  double p[4], xLoc[4];
225 
226  // This location should come from an initial state
227  for (int i = 0; i <= 3; i++) {
228  xLoc[i] = 0.0;
229  };
230 
231  // Now determine virtualities and rebalance
232  // Need back-to-back kinematics with a q-qbar pair
233  // This seems always to be the case for the epem gun but check nevertheless
234  if(p62.size() == 2 && std::abs(p62[0].e() - 0.5*eCM) < 0.001 && std::abs(p62[1].e() - 0.5*eCM) < 0.001 && std::abs(p62[0].id()) < 6 && std::abs(p62[1].id()) < 6){
235 
236  // Virtualities of the two partons
237  double q1 = 0.;
238  double q2 = 0.;
239  const double QS = 0.9;
240 
241  //Find initial virtuality one parton at a time
242  for(int pass=0; pass<2; ++pass){
243 
244  double mass = p62[pass].m0();
245  // this part is for the case that light quarks are considered massless
246  /*if(std::abs(p62[pass].id()) < 4){
247  mass = 0.;
248  }*/
249  double max_vir = (0.25*eCM*eCM - mass*mass) * std::abs(vir_factor);
250  double min_vir = (0.5 * QS * QS ) * (1.0 + std::sqrt(1.0 + 4.0 * mass*mass / (QS*QS)));
251 
252  double tQ2 = 0.;
253 
254  if (max_vir <= QS * QS){
255  tQ2 = 0.0;
256  }else if(max_vir < min_vir){
257  tQ2 = QS * QS;
258  }else{
259 
260  double numer = 1.0;
261  double random = ZeroOneDistribution(*GetMt19937Generator());
262  double ratio = 1.0;
263  double diff = ratio;
264  if(random > rounding_error){
265  diff = (ratio - random) / random;
266  }
267 
268  if(max_vir >= (QS*QS / 2.) * (1.0 + std::sqrt(1.0 + 2.0 * mass * mass / (QS*QS / 2.)))){
269  double g = (QS*QS / 2.) * (1.0 + std::sqrt(1.0 + 2.0 * mass * mass / (QS*QS / 2.)));
270  numer = exp(-1.0 * (Cf / 2.0 / pi) * sud_val_QG_w_M(mass,(QS*QS / 2.), g, max_vir, 0.5*eCM));
271  }
272 
273  if (numer > random){tQ2 = min_vir;}
274  else{
275 
276  double t_hi = max_vir;
277  double t_low = min_vir;
278  double t_mid = t_low;
279 
280  double denom = 1.0;
281 
282  do{
283  t_mid = 0.5*(t_low + t_hi);
284 
285  if (t_mid < (QS*QS / 2.) * (1.0 + std::sqrt(1.0 + 2.0 * mass * mass / (QS*QS / 2.)))){
286  denom = 1.0;
287  }else{
288  double g = (QS*QS / 2.) * (1.0 + std::sqrt(1.0 + 2.0 * mass * mass / (QS*QS / 2.)));
289  denom = exp(-1.0 * (Cf / 2.0 / pi) * sud_val_QG_w_M(mass,(QS*QS / 2.), g, t_mid, 0.5*eCM));
290  }
291 
292  ratio = numer / denom;
293 
294  diff = (ratio - random) / random;
295 
296  if (diff < 0.0){t_low = t_mid;}
297  else{t_hi = t_mid;}
298 
299  }while((abs(diff) > s_approx) && (abs(t_hi - t_low) / t_hi > s_error));
300 
301  tQ2 = t_mid;
302 
303  }
304  }
305 
306  if(pass==0){q1=sqrt(tQ2);}
307  else if(pass==1){q2=sqrt(tQ2);}
308  }
309 
310  double modm_sq1 = q1*q1 + p62[0].m0()*p62[0].m0();
311  double modm_sq2 = q2*q2 + p62[1].m0()*p62[1].m0();
312 
313  if(eCM > sqrt(modm_sq1)+sqrt(modm_sq2)){
314  // Check viability condition; should always be satisfied in the massless case if virfactor < 1
315  double pnew = 0.5*sqrt((eCM*eCM-modm_sq1-modm_sq2)*(eCM*eCM-modm_sq1-modm_sq2)-4.*modm_sq1*modm_sq2)/eCM;
316 
317  auto magnitude = [](const Pythia8::Particle &p) {
318  return std::sqrt(p.px() * p.px() + p.py() * p.py() + p.pz() * p.pz());
319  };
320 
321  double scale1 = pnew/magnitude(p62[0]);
322  double scale2 = pnew/magnitude(p62[1]);
323  double e1new = sqrt(pnew*pnew + modm_sq1);
324  double e2new = sqrt(pnew*pnew + modm_sq2);
325  p62[0].e(e1new);
326  p62[0].px(p62[0].px()*scale1);
327  p62[0].py(p62[0].py()*scale1);
328  p62[0].pz(p62[0].pz()*scale1);
329  p62[1].e(e2new);
330  p62[1].px(p62[1].px()*scale2);
331  p62[1].py(p62[1].py()*scale2);
332  p62[1].pz(p62[1].pz()*scale2);
333  }
334  }
335 
336  //give partons to the framework
337  for (int np = 0; np < p62.size(); ++np) {
338  Pythia8::Particle &particle = p62.at(np);
339 
340  VERBOSE(7) << "Adding particle with pid = " << particle.id()
341  << " at x=" << xLoc[1] << ", y=" << xLoc[2] << ", z=" << xLoc[3];
342 
343  VERBOSE(7) << "Adding particle with pid = " << particle.id()
344  << ", pT = " << particle.pT() << ", eta = " << particle.eta()
345  << ", phi = " << particle.phi() << ", e = " << particle.e();
346 
347  VERBOSE(7) << " at x=" << xLoc[1] << ", y=" << xLoc[2] << ", z=" << xLoc[3];
348 
349  auto ptn = make_shared<Parton>(0, particle.id(), 0, particle.pT(), particle.eta(), particle.phi(), particle.e(), xLoc);
350  ptn->set_color(particle.col());
351  ptn->set_anti_color(particle.acol());
352  ptn->set_max_color(1000 * (np + 1));
353 
354  if(p62.size() == 2 && std::abs(particle.id()) < 6){
355  double mean_form_time = (2.*ptn->e()) / (ptn->e()*ptn->e()
356  - ptn->px()*ptn->px() - ptn->py()*ptn->py()
357  - ptn->pz()*ptn->pz() - ptn->restmass()*ptn->restmass()
359  ptn->set_form_time(mean_form_time);
360  ptn->set_mean_form_time();
361 
362  double velocity[4];
363  velocity[0] = 1.0;
364  for (int j = 1; j <= 3; j++) {
365  velocity[j] = ptn->p(j) / ptn->e();
366  }
367  ptn->set_jet_v(velocity);
368  }
369  AddParton(ptn);
370  }
371 
372  //event.list();
373 
374  VERBOSE(8) << GetNHardPartons();
375 }
376 
377 double epemGun::sud_val_QG_w_M(double M, double h0, double h1, double h2, double E1) {
378  double val, h, intg, hL, hR, diff, intg_L, intg_R, span;
379 
380  val = 0.0;
381 
382  h = (h1 + h2) / 2.0;
383 
384  span = (h2 - h1) / h2;
385 
386  val = alpha_s(h) * sud_z_QG_w_M(M, h0, h, E1);
387 
388  intg = val * (h2 - h1);
389 
390  hL = (h1 + h) / 2.0;
391 
392  intg_L = alpha_s(hL) * sud_z_QG_w_M(M, h0, hL, E1) * (h - h1);
393 
394  hR = (h + h2) / 2.0;
395 
396  intg_R = alpha_s(hR) * sud_z_QG_w_M(M, h0, hR, E1) * (h2 - h);
397 
398  diff = std::abs((intg_L + intg_R - intg) / intg);
399 
400  if ((diff > approx) || (span > error)) {
401  intg = sud_val_QG_w_M(M, h0, h1, h, E1) +
402  sud_val_QG_w_M(M, h0, h, h2, E1);
403  }
404 
405  return intg;
406 }
407 
408 double epemGun::sud_z_QG_w_M(double M, double cg, double cg1, double E2){
409  // this part is for the case that light quarks are considered massless
410  /*if(M < 1.){
411  if (cg1 < 2.0 * cg) {
412  return 0.0;
413  };
414 
415  double t2 = std::pow(cg1, 2);
416  double t6 = std::log(cg);
417  double t10 = std::abs(cg - cg1);
418  double t11 = std::log(t10);
419  double t17 = -1.0 / t2 * (3.0 * cg1 - 6.0 * cg + 4.0 * t6 * cg1 - 4.0 * t11 * cg1) /
420  2.0;
421  if (t17 < 0.0) {
422  cerr << "ERROR: t17 negative in sud_z_QG_w_M = " << t17 << endl;
423  throw std::runtime_error("ERROR: medium contribution negative in sud_z_QG_w_M");
424  }
425  return t17;
426  }*/
427 
428  if (cg1 < 2.0 * cg + M * M / (1.0 + M * M / cg1)) {
429  JSINFO << MAGENTA << " returning with cg, cg1 = " << cg << " " << cg1
430  << " " << E_minimum << " " << E2;
431  return M * M;
432  };
433 
434  double t1 = 1.0 / cg1;
435  double t2 = t1 * cg;
436  double t4 = std::pow(1.0 - t2, 2.0);
437  double t7 = std::log(t2);
438  double t9 = M * M;
439  double t10 = t1 * t9;
440  double t13 = 1.0 / (t10 + 1.0) * t10;
441  double t15 = std::pow(t2 + t13, 2.0);
442  double t18 = std::log(1.0 - t2 - t13);
443  double t21 = t1 * (-t4 / 2.0 - 1.0 + 2.0 * t2 - 2.0 * t7 + t15 / 2.0 + t13 +
444  2.0 * t18);
445 
446  if (t21 < 0.0) {
447  cerr << "ERROR: t21 negative in sud_z_QG_w_M = " << t21 << endl;
448  throw std::runtime_error("ERROR: medium contribution negative in sud_z_QG_w_M");
449  }
450 
451  return t21;
452 }
453 
454 double epemGun::alpha_s(double q2) {
455  double a, L2, q24, c_nf;
456 
457  L2 = std::pow(Lambda_QCD, 2);
458 
459  q24 = q2 / 4.0;
460 
461  c_nf = nf;
462 
463  if (q24 > 4.0) {
464  c_nf = 4;
465  }
466 
467  if (q24 > 64.0) {
468  c_nf = 5;
469  }
470 
471  if (q24 > L2) {
472  a = 12.0 * pi / (11.0 * Nc - 2.0 * c_nf) / std::log(q24 / L2);
473  } else {
474  JSWARN << " alpha too large ";
475  a = 0.6;
476  }
477 
478  return a;
479  }