Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ColorlessHadronization.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file ColorlessHadronization.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 #include "ColorlessHadronization.h"
17 #include "JetScapeXML.h"
18 #include "JetScapeLogger.h"
19 #include "tinyxml2.h"
20 #include "JetScapeConstants.h"
21 #include <sstream>
22 #include <iostream>
23 #include <fstream>
24 #include <sstream>
25 #include <random>
26 
27 using namespace Jetscape;
28 using namespace Pythia8;
29 
30 // Hadrons output file
31 //ofstream hadfile;
32 
33 // Register the module with the base class
35  ColorlessHadronization::reg("ColorlessHadronization");
36 
37 // Initialize static helper here
38 Pythia8::Pythia ColorlessHadronization::pythia("IntentionallyEmpty", false);
39 
41  SetId("ColorlessHadronization");
42  VERBOSE(8);
43 }
44 
46 
48  // Open output file
49  //hadfile.open("CH_myhad.dat");
50 
51  std::string s = GetXMLElementText({"JetHadronization", "name"});
52  JSDEBUG << s << " to be initializied ...";
53 
54  // Read sqrts to know remnants energies
55  double p_read_xml =
56  GetXMLElementDouble({"JetHadronization", "eCMforHadronization"});
57  p_fake = p_read_xml;
58 
59  take_recoil = GetXMLElementInt({"JetHadronization", "take_recoil"});
60 
61  JSDEBUG << "Initialize ColorlessHadronization";
62  VERBOSE(8);
63 
64  // No event record printout.
65  pythia.readString("Next:numberShowInfo = 0");
66  pythia.readString("Next:numberShowProcess = 0");
67  pythia.readString("Next:numberShowEvent = 0");
68 
69  // Standard settings
70  pythia.readString("ProcessLevel:all = off");
71  pythia.readString("PartonLevel:FSR=off");
72 
73  // General settings for hadron decays
74  std::string pythia_decays = GetXMLElementText({"JetHadronization", "pythia_decays"});
75  double tau0Max = 10.0;
76  double tau0Max_xml = GetXMLElementDouble({"JetHadronization", "tau0Max"});
77  if(tau0Max_xml >= 0){tau0Max = tau0Max_xml;}
78  else{JSWARN << "tau0Max should be larger than 0. Set it to 10.";}
79  if(pythia_decays == "on"){
80  JSINFO << "Pythia decays are turned on for tau0Max < " << tau0Max;
81  pythia.readString("HadronLevel:Decay = on");
82  pythia.readString("ParticleDecays:limitTau0 = on");
83  pythia.readString("ParticleDecays:tau0Max = " + std::to_string(tau0Max));
84  } else {
85  JSINFO << "Pythia decays are turned off";
86  pythia.readString("HadronLevel:Decay = off");
87  }
88 
89  // Settings for decays (old flag, will be depracted at some point)
90  // This overwrites the previous settings if the user xml file contains the flag
91  std::string weak_decays =
92  GetXMLElementText({"JetHadronization", "weak_decays"});
93  if (weak_decays == "off") {
94  JSINFO << "Hadron decays are turned off.";
95  JSWARN << "This parameter will be depracted at some point. Use 'pythia_decays' instead.\nOverwriting 'pythia_decays'.";
96  pythia.readString("HadronLevel:Decay = off");
97  } else if(weak_decays == "on") {
98  JSINFO << "Hadron decays inside a range of 10 mm/c are turned on.";
99  JSWARN << "This parameter will be depracted at some point. Use 'pythia_decays' and 'tau0Max' for more control on decays.\nOverwriting 'pythia_decays' and fix 'tau0Max' to 10.";
100  pythia.readString("HadronLevel:Decay = on");
101  pythia.readString("ParticleDecays:limitTau0 = on");
102  pythia.readString("ParticleDecays:tau0Max = 10.0");
103  }
104 
105  std::stringstream lines;
106  lines << GetXMLElementText({"JetHadronization", "LinesToRead"}, false);
107  while (std::getline(lines, s, '\n')) {
108  if (s.find_first_not_of(" \t\v\f\r") == s.npos)
109  continue; // skip empty lines
110  JSINFO << "Also reading in: " << s;
111  pythia.readString(s);
112  }
113 
114  // Initialize random number distribution
115  ZeroOneDistribution = std::uniform_real_distribution<double> { 0.0, 1.0 };
116  // And initialize
117  pythia.init();
118 }
119 
120 void ColorlessHadronization::WriteTask(weak_ptr<JetScapeWriter> w) {
121  VERBOSE(8);
122  auto f = w.lock();
123  if (!f)
124  return;
125  f->WriteComment("Hadronization Module : " + GetId());
126 }
127 
129  vector<vector<shared_ptr<Parton>>> &shower,
130  vector<shared_ptr<Hadron>> &hOut, vector<shared_ptr<Parton>> &pOut) {
131  VERBOSE(1) << "Start Hadronizing using PYTHIA Lund string model (does NOT "
132  "use color flow, needs to be tested)...";
133  Event &event = pythia.event;
134  ParticleData &pdt = pythia.particleData;
135 
136  // Hadronize positive (status = 0) and negative (status = -1) partons in a different space
137  for (int want_pos = 1; want_pos >= 0; --want_pos) {
138  event.reset();
139 
140  if (!take_recoil && want_pos == 0)
141  continue; // SC: don't need negative if don't take recoil
142 
143  // Set remnants momentum
144  double random_direction = ZeroOneDistribution(*GetMt19937Generator());
145  if (random_direction>0.5) {
146  random_direction = 1.0;
147  }else{
148  random_direction = -1.0;
149  };
150 
151  double rempx = 0.2;
152  double rempy = 0.2;
153  double rempz = random_direction*p_fake;
154  double reme = std::sqrt(std::pow(rempx, 2.) + std::pow(rempy, 2.) +
155  std::pow(rempz, 2.));
156 
157  // Hadronize all showers together
158  vector<shared_ptr<Parton>> pIn;
159  vector<vector<Parton>> shower_in;
160  for (unsigned int ishower = 0; ishower < shower.size(); ++ishower) {
161  vector<Parton> p_sh;
162  for (unsigned int ipart = 0; ipart < shower.at(ishower).size(); ++ipart) {
163  p_sh.push_back(*(shower[ishower][ipart]));
164  }
165  shower_in.push_back(p_sh);
166  }
167  for (unsigned int ishower = 0; ishower < shower_in.size(); ++ishower) {
168  for (unsigned int ipart = 0; ipart < shower_in[ishower].size(); ++ipart) {
169  //if (shower_in.at(ishower).at(ipart)->pstat()==0 && want_pos==1) pIn.push_back(shower_in.at(ishower).at(ipart)); // Positive
170  if (want_pos == 1) { // Positive
171  if (take_recoil && shower_in[ishower][ipart].pstat() == 1) {
172  pIn.push_back(make_shared<Parton>(shower_in[ishower][ipart]));
173  }
174  if (shower_in[ishower][ipart].pstat() == 0) {
175  pIn.push_back(make_shared<Parton>(shower_in[ishower][ipart]));
176  }
177  if (shower_in[ishower][ipart].pstat() == 22) {
178  pIn.push_back(make_shared<Parton>(shower_in[ishower][ipart])); //Allow photons with status code 22 to pass through Colorless hadronization.
179  }
180  }
181  if (take_recoil && shower_in[ishower][ipart].pstat() == -1 &&
182  want_pos == 0) {
183  pIn.push_back(make_shared<Parton>(shower_in[ishower][ipart]));
184  } // Negative
185  }
186  JSDEBUG << "Shower#" << ishower + 1
187  << ". Number of partons to hadronize so far: " << pIn.size();
188  }
189  if (want_pos == 1)
190  VERBOSE(1) << "# Positive Partons to hadronize: " << pIn.size();
191  else
192  VERBOSE(1) << "# Negative Partons to hadronize: " << pIn.size();
193 
194  // Check whether event is empty (specially important for negative partons case)
195  if (pIn.size() == 0)
196  continue;
197 
198  int col[pIn.size() + 2], acol[pIn.size() + 2], isdone[pIn.size() + 2];
199  memset(col, 0, (pIn.size() + 2) * sizeof(int)),
200  memset(acol, 0, (pIn.size() + 2) * sizeof(int)),
201  memset(isdone, 0, (pIn.size() + 2) * sizeof(int));
202 
203  // Find number of quarks
204  int nquarks = 0;
205  int isquark[pIn.size() + 2];
206  memset(isquark, 0, (pIn.size() + 2) * sizeof(int));
207  for (unsigned int ipart = 0; ipart < pIn.size(); ++ipart) {
208  if (abs(pIn[ipart]->pid()) <= 6) {
209  isquark[nquarks] = ipart;
210  nquarks += 1;
211  }
212  }
213  JSDEBUG << "#Quarks = " << nquarks;
214 
215  // Find number of strings
216  int nstrings = max(int(double(nquarks) / 2. + 0.6), 1);
217  JSDEBUG << "#Strings = " << nstrings;
218 
219  // If there are no quarks, need to attach two of them
220  int istring = 0;
221  int one_end[nstrings], two_end[nstrings];
222  if (nquarks == 0) { // Only attach remnants if event is not empty
223  // First quark
224  FourVector p1(rempx, rempy, rempz, reme);
225  FourVector x1;
226  pIn.push_back(std::make_shared<Parton>(0, 1, 0, p1, x1));
227  isquark[nquarks] = pIn.size() - 1;
228  nquarks += 1;
229  isdone[pIn.size() - 1] = 1;
230  one_end[0] = pIn.size() - 1;
231  VERBOSE(1) << "Attached quark remnant flying down +Pz beam";
232  // Second quark
233  FourVector p2(rempx, rempy, -rempz, reme);
234  FourVector x2;
235  pIn.push_back(std::make_shared<Parton>(0, 1, 0, p2, x2));
236  isquark[nquarks] = pIn.size() - 1;
237  nquarks += 1;
238  isdone[pIn.size() - 1] = 1;
239  two_end[istring] = pIn.size() - 1;
240  VERBOSE(1) << "Attached quark remnant flying down -Pz beam";
241  }
242 
243  // Assign ends of strings (order matters in this algo)
244  for (unsigned int iquark = 0; iquark < nquarks; iquark++) {
245  if (isdone[isquark[iquark]] == 0) {
246  isdone[isquark[iquark]] = 1;
247  one_end[istring] = isquark[iquark];
248  double min_delR = 10000.;
249  int partner = -2;
250  for (unsigned int jquark = 0; jquark < nquarks; jquark++) {
251  if (iquark == jquark)
252  continue;
253  int d_jquark = isquark[jquark];
254  if (isdone[d_jquark] == 0) {
255  fjcore::PseudoJet pf(pIn[d_jquark]->px(), pIn[d_jquark]->py(),
256  pIn[d_jquark]->pz(), pIn[d_jquark]->e());
257  double delR = pIn[isquark[iquark]]->delta_R(pf);
258  if (delR < min_delR)
259  min_delR = delR, partner = jquark;
260  }
261  }
262  if (partner != -2) {
263  isdone[isquark[partner]] = 1;
264  two_end[istring] = isquark[partner];
265  istring += 1;
266  } else {
267  FourVector p(rempx, rempy, rempz, reme);
268  FourVector x;
269  pIn.push_back(std::make_shared<Parton>(0, 1, 0, p, x));
270  isquark[nquarks] = pIn.size() - 1;
271  nquarks += 1;
272  isdone[pIn.size() - 1] = 1;
273  two_end[istring] = pIn.size() - 1;
274  VERBOSE(1) << "Attached quark remnant flying down +Pz beam";
275  }
276  }
277  }
278 
279  // Assign gluons to a certain string
280  int my_string[pIn.size()];
281  memset(my_string, 0, pIn.size() * sizeof(int));
282  for (unsigned int ipart = 0; ipart < pIn.size(); ++ipart) {
283  if (pIn[ipart]->pid() == 21) {
284  double min_delR = 100000.;
285  for (int ns = 0; ns < nstrings; ns++) {
286  int fq = one_end[ns];
287  int sq = two_end[ns];
288  fjcore::PseudoJet pfq(pIn[fq]->px(), pIn[fq]->py(), pIn[fq]->pz(),
289  pIn[fq]->e());
290  double f_delR = pIn[ipart]->delta_R(pfq);
291  fjcore::PseudoJet psq(pIn[sq]->px(), pIn[sq]->py(), pIn[sq]->pz(),
292  pIn[sq]->e());
293  double s_delR = pIn[ipart]->delta_R(psq);
294  double delR = (f_delR + s_delR) / 2.;
295  if (delR < min_delR)
296  my_string[ipart] = ns, min_delR = delR;
297  }
298  }
299  }
300 
301  // Build up chain using gluons assigned to each string, in a closest pair order
302  int lab_col = 102;
303  for (int ns = 0; ns < nstrings; ns++) {
304  int tquark = one_end[ns];
305  if (pIn[tquark]->pid() > 0)
306  col[tquark] = lab_col;
307  else
308  acol[tquark] = lab_col;
309  lab_col += 1;
310  int link = tquark;
311  int changes = 1;
312  do {
313  changes = 0;
314  double min_delR = 100000.;
315  int next_link = 0;
316  for (unsigned int ipart = 0; ipart < pIn.size(); ++ipart) {
317  if (pIn[ipart]->pid() == 21 && isdone[ipart] == 0 &&
318  my_string[ipart] == ns) {
319  changes = 1;
320  fjcore::PseudoJet pf(pIn[ipart]->px(), pIn[ipart]->py(),
321  pIn[ipart]->pz(), pIn[ipart]->e());
322  double delR = pIn[link]->delta_R(pf);
323  if (delR < min_delR)
324  min_delR = delR, next_link = ipart;
325  }
326  }
327  if (changes == 1) {
328  isdone[next_link] = 1;
329  if (col[link] == lab_col - 1)
330  col[next_link] = lab_col, acol[next_link] = lab_col - 1;
331  else
332  col[next_link] = lab_col - 1, acol[next_link] = lab_col;
333  lab_col += 1;
334  JSDEBUG << " Linked parton= " << next_link;
335  link = next_link;
336  }
337  } while (changes == 1);
338  // Attach second end
339  if (col[link] == lab_col - 1)
340  col[two_end[ns]] = 0, acol[two_end[ns]] = lab_col - 1;
341  else
342  col[two_end[ns]] = lab_col - 1, acol[two_end[ns]] = 0;
343  }
344  // Changing identity of quarks to be consistent with color charge
345  for (int iq = 0; iq < nquarks; ++iq) {
346  if (col[isquark[iq]] != 0) {
347  if (pIn[isquark[iq]]->pid() < 0)
348  pIn[isquark[iq]]->set_id(-pIn[isquark[iq]]->pid());
349  } else {
350  if (pIn[isquark[iq]]->pid() > 0)
351  pIn[isquark[iq]]->set_id(-pIn[isquark[iq]]->pid());
352  }
353  }
354 
355  // Introduce partons into PYTHIA
356  /*
357  for (unsigned int ipart=0; ipart < pIn.size(); ++ipart)
358  {
359  JSDEBUG << "Parton #" << ipart << " is a " << pIn[ipart]->pid() << "with energy = " << pIn[ipart]->e() << " with phi= " << pIn[ipart]->phi() << " and has col= " << col[ipart] << " and acol= " << acol[ipart];
360  }
361  */
362 
363  /***************************************************************************************************************/
364  //
365  // Making collinear partons not collinear
366  //
367  // Could have dangerous effects, yet to be tested....
368  //
369 
370  for (unsigned int ipart = 0; ipart < pIn.size(); ++ipart) {
371  double px = pIn[ipart]->px();
372  double py = pIn[ipart]->py();
373  double pz = pIn[ipart]->pz();
374  double ee = pIn[ipart]->e();
375  for (unsigned int j = ipart + 1; j < pIn.size(); j++) {
376  double p2x = pIn[j]->px();
377  double p2y = pIn[j]->py();
378  double p2z = pIn[j]->pz();
379  double e2e = pIn[j]->e();
380 
381  double diff =
382  sqrt(pow(px - p2x, 2) + pow(py - p2y, 2) + pow(pz - p2z, 2));
383  double f = 4.0;
384  if (diff < f * Lambda_QCD) {
385  if ((pz >= 0) && (p2z >= 0)) {
386  if (pz >= p2z) {
387  pIn[ipart]->reset_momentum(px, py, pz + f * Lambda_QCD,
388  sqrt(ee * ee +
389  2 * pz * f * Lambda_QCD +
390  f * Lambda_QCD * f * Lambda_QCD));
391  } else
392  pIn[j]->reset_momentum(p2x, p2y, p2z + f * Lambda_QCD,
393  sqrt(e2e * e2e + 2 * p2z * f * Lambda_QCD +
394  f * Lambda_QCD * f * Lambda_QCD));
395  } else if ((pz >= 0) && (p2z < 0)) {
396  if (abs(pz) >= abs(p2z)) {
397  pIn[ipart]->reset_momentum(px, py, pz + f * Lambda_QCD,
398  sqrt(ee * ee +
399  2 * pz * f * Lambda_QCD +
400  f * Lambda_QCD * f * Lambda_QCD));
401  } else
402  pIn[j]->reset_momentum(p2x, p2y, p2z - f * Lambda_QCD,
403  sqrt(e2e * e2e - 2 * p2z * f * Lambda_QCD +
404  f * Lambda_QCD * f * Lambda_QCD));
405  } else if ((pz < 0) && (p2z >= 0)) {
406  if (abs(pz) >= abs(p2z)) {
407  pIn[ipart]->reset_momentum(px, py, pz - f * Lambda_QCD,
408  sqrt(ee * ee -
409  2 * pz * f * Lambda_QCD +
410  f * Lambda_QCD * f * Lambda_QCD));
411  } else
412  pIn[j]->reset_momentum(p2x, p2y, p2z + f * Lambda_QCD,
413  sqrt(e2e * e2e + 2 * p2z * f * Lambda_QCD +
414  f * Lambda_QCD * f * Lambda_QCD));
415  } else {
416  if (abs(pz) >= abs(p2z)) {
417  pIn[ipart]->reset_momentum(px, py, pz - f * Lambda_QCD,
418  sqrt(ee * ee -
419  2 * pz * f * Lambda_QCD +
420  f * Lambda_QCD * f * Lambda_QCD));
421  } else
422  pIn[j]->reset_momentum(p2x, p2y, p2z - f * Lambda_QCD,
423  sqrt(e2e * e2e - 2 * p2z * f * Lambda_QCD +
424  f * Lambda_QCD * f * Lambda_QCD));
425  }
426  }
427  }
428  }
429 
430  /**************************************************************************************************************************/
431 
432  for (unsigned int ipart = 0; ipart < pIn.size(); ++ipart) {
433  int ide = pIn[ipart]->pid();
434  double px = pIn[ipart]->px();
435  double py = pIn[ipart]->py();
436  double pz = pIn[ipart]->pz();
437  double ee = pIn[ipart]->e();
438  double mm = pdt.m0(int(ide));
439  ee = std::sqrt(px * px + py * py + pz * pz + mm * mm);
440  if (col[ipart] == 0 && acol[ipart] == 0 && (ide == 21 || abs(ide) <= 6)) {
441  JSINFO << "Stopping because of colorless parton trying to be "
442  "introduced in PYTHIA string";
443  exit(0);
444  }
445  event.append(int(ide), 23, col[ipart], acol[ipart], px, py, pz, ee, mm);
446  }
447 
448  pythia.next();
449  for (unsigned int ipart = 0; ipart < event.size(); ++ipart) {
450  if (event[ipart].isFinal()) {
451  int ide = pythia.event[ipart].id();
452  FourVector p(pythia.event[ipart].px(), pythia.event[ipart].py(),
453  pythia.event[ipart].pz(), pythia.event[ipart].e());
454  FourVector x;
455  if (want_pos == 1)
456  hOut.push_back(
457  std::make_shared<Hadron>(Hadron(0, ide, 0, p, x))); // Positive
458  else
459  hOut.push_back(
460  std::make_shared<Hadron>(Hadron(0, ide, -1, p, x))); // Negative
461  //JSINFO << "Produced Hadron has id = " << pythia.event[ipart].id();
462  // Print on output file
463  //hadfile << pythia.event[ipart].px() << " " << pythia.event[ipart].py() << " " << pythia.event[ipart].pz() << " " << pythia.event[ipart].e() << " " << pythia.event[ipart].id() << " " << pythia.event[ipart].charge() << endl;
464  }
465  }
466  VERBOSE(1) << "#Showers hadronized together: " << shower.size()
467  << ". There are " << hOut.size() << " hadrons and "
468  << pOut.size() << " partons after PYTHIA Hadronization";
469  //hadfile << "NEXT" << endl;
470 
471  } // End of positive or negative loop
472 }