Version: SMASH-1.5
processstring.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2017-
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include <array>
11 
12 #include "smash/angles.h"
13 #include "smash/kinematics.h"
14 #include "smash/processstring.h"
15 #include "smash/random.h"
16 
17 namespace smash {
18 
19 StringProcess::StringProcess(double string_tension, double time_formation,
20  double gluon_beta, double gluon_pmin,
21  double quark_alpha, double quark_beta,
22  double strange_supp, double diquark_supp,
23  double sigma_perp, double leading_frag_mean,
24  double leading_frag_width, double stringz_a,
25  double stringz_b, double string_sigma_T,
26  double factor_t_form, bool use_yoyo_model,
27  double prob_proton_to_d_uu)
28  : pmin_gluon_lightcone_(gluon_pmin),
29  pow_fgluon_beta_(gluon_beta),
30  pow_fquark_alpha_(quark_alpha),
31  pow_fquark_beta_(quark_beta),
32  sigma_qperp_(sigma_perp),
33  leading_frag_mean_(leading_frag_mean),
34  leading_frag_width_(leading_frag_width),
35  kappa_tension_string_(string_tension),
36  additional_xsec_supp_(0.7),
37  time_formation_const_(time_formation),
38  soft_t_form_(factor_t_form),
39  time_collision_(0.),
40  use_yoyo_model_(use_yoyo_model),
41  prob_proton_to_d_uu_(prob_proton_to_d_uu) {
42  // setup and initialize pythia for hard string process
43  pythia_parton_ = make_unique<Pythia8::Pythia>(PYTHIA_XML_DIR, false);
44  /* select only non-diffractive events
45  * diffractive ones are implemented in a separate routine */
46  pythia_parton_->readString("SoftQCD:nonDiffractive = on");
47  pythia_parton_->readString("MultipartonInteractions:pTmin = 1.5");
48  pythia_parton_->readString("HadronLevel:all = off");
49  common_setup_pythia(pythia_parton_.get(), strange_supp, diquark_supp,
50  stringz_a, stringz_b, string_sigma_T);
51 
52  // setup and initialize pythia for fragmentation
53  pythia_hadron_ = make_unique<Pythia8::Pythia>(PYTHIA_XML_DIR, false);
54  /* turn off all parton-level processes to implement only hadronization */
55  pythia_hadron_->readString("ProcessLevel:all = off");
56  common_setup_pythia(pythia_hadron_.get(), strange_supp, diquark_supp,
57  stringz_a, stringz_b, string_sigma_T);
58 
59  /* initialize PYTHIA */
60  pythia_hadron_->init();
61  pythia_sigmatot_.init(&pythia_hadron_->info, pythia_hadron_->settings,
62  &pythia_hadron_->particleData, &pythia_hadron_->rndm);
63  event_intermediate_.init("intermediate partons",
64  &pythia_hadron_->particleData);
65 
66  sqrt2_ = std::sqrt(2.);
67 
68  for (int imu = 0; imu < 3; imu++) {
69  evecBasisAB_[imu] = ThreeVector(0., 0., 0.);
70  }
71 
72  final_state_.clear();
73 }
74 
75 void StringProcess::common_setup_pythia(Pythia8::Pythia *pythia_in,
76  double strange_supp,
77  double diquark_supp, double stringz_a,
78  double stringz_b,
79  double string_sigma_T) {
80  // choose parametrization for mass-dependent width
81  pythia_in->readString("ParticleData:modeBreitWigner = 4");
82  /* choose minimum transverse momentum scale
83  * involved in partonic interactions */
84  pythia_in->readString("MultipartonInteractions:pTmin = 1.5");
85  pythia_in->readString("MultipartonInteractions:nSample = 10000");
86  // transverse momentum spread in string fragmentation
87  pythia_in->readString("StringPT:sigma = " + std::to_string(string_sigma_T));
88  // diquark suppression factor in string fragmentation
89  pythia_in->readString("StringFlav:probQQtoQ = " +
90  std::to_string(diquark_supp));
91  // strangeness suppression factor in string fragmentation
92  pythia_in->readString("StringFlav:probStoUD = " +
93  std::to_string(strange_supp));
94  // parameters for the fragmentation function
95  pythia_in->readString("StringZ:aLund = " + std::to_string(stringz_a));
96  pythia_in->readString("StringZ:bLund = " + std::to_string(stringz_b));
97 
98  // manually set the parton distribution function
99  pythia_in->readString("PDF:pSet = 13");
100  pythia_in->readString("PDF:pSetB = 13");
101  pythia_in->readString("PDF:piSet = 1");
102  pythia_in->readString("PDF:piSetB = 1");
103  pythia_in->readString("Beams:idA = 2212");
104  pythia_in->readString("Beams:idB = 2212");
105  pythia_in->readString("Beams:eCM = 10.");
106 
107  // set PYTHIA random seed from outside
108  pythia_in->readString("Random:setSeed = on");
109  // suppress unnecessary output
110  pythia_in->readString("Print:quiet = on");
111  // No resonance decays, since the resonances will be handled by SMASH
112  pythia_in->readString("HadronLevel:Decay = off");
113  // set particle masses and widths in PYTHIA to be same with those in SMASH
114  for (auto &ptype : ParticleType::list_all()) {
115  int pdgid = ptype.pdgcode().get_decimal();
116  double mass_pole = ptype.mass();
117  double width_pole = ptype.width_at_pole();
118  // check if the particle species is in PYTHIA
119  if (pythia_in->particleData.isParticle(pdgid)) {
120  // set mass and width in PYTHIA
121  pythia_in->particleData.m0(pdgid, mass_pole);
122  pythia_in->particleData.mWidth(pdgid, width_pole);
123  } else if (pdgid == 310 || pdgid == 130) {
124  // set mass and width of Kaon-L and Kaon-S
125  pythia_in->particleData.m0(pdgid, kaon_mass);
126  pythia_in->particleData.mWidth(pdgid, 0.);
127  }
128  }
129 
130  // make energy-momentum conservation in PYTHIA more precise
131  pythia_in->readString("Check:epTolErr = 1e-6");
132  pythia_in->readString("Check:epTolWarn = 1e-8");
133 }
134 
135 // compute the formation time and fill the arrays with final-state particles
136 int StringProcess::append_final_state(ParticleList &intermediate_particles,
137  const FourVector &uString,
138  const ThreeVector &evecLong) {
139  int nfrag = 0;
140  int bstring = 0;
141  double p_pos_tot = 0.0, p_neg_tot = 0.0;
142 
143  for (ParticleData &data : intermediate_particles) {
144  nfrag += 1;
145  bstring += data.pdgcode().baryon_number();
146 
147  const double pparallel = data.momentum().threevec() * evecLong;
148  p_pos_tot += (data.momentum().x0() + pparallel) / sqrt2_;
149  p_neg_tot += (data.momentum().x0() - pparallel) / sqrt2_;
150  }
151  assert(nfrag > 0);
152 
153  /* compute the cross section scaling factor for leading hadrons
154  * based on the number of valence quarks. */
155  assign_all_scaling_factors(bstring, intermediate_particles, evecLong,
157 
158  std::vector<double> xvertex_pos, xvertex_neg;
159  xvertex_pos.resize(nfrag + 1);
160  xvertex_neg.resize(nfrag + 1);
161  // x^{+} coordinates of the forward end
162  xvertex_pos[0] = p_pos_tot / kappa_tension_string_;
163  // x^{-} coordinates of the backward end
164  xvertex_neg[nfrag] = p_neg_tot / kappa_tension_string_;
165 
166  for (int i = 0; i < nfrag; i++) {
167  const double pparallel =
168  intermediate_particles[i].momentum().threevec() * evecLong;
169 
170  // recursively compute x^{+} coordinates of q-qbar formation vertex
171  xvertex_pos[i + 1] =
172  xvertex_pos[i] -
173  (intermediate_particles[i].momentum().x0() + pparallel) /
175 
176  // recursively compute x^{-} coordinates of q-qbar formation vertex
177  const int ineg = nfrag - i - 1;
178  xvertex_neg[ineg] =
179  xvertex_neg[ineg + 1] -
180  (intermediate_particles[ineg].momentum().x0() - pparallel) /
182  }
183 
184  // Velocity three-vector to perform Lorentz boost.
185  const ThreeVector vstring = uString.velocity();
186 
187  /* compute the formation times of hadrons
188  * from the lightcone coordinates of q-qbar formation vertices. */
189  for (int i = 0; i < nfrag; i++) {
190  // boost 4-momentum into the center of mass frame
191  FourVector momentum =
192  intermediate_particles[i].momentum().LorentzBoost(-vstring);
193  intermediate_particles[i].set_4momentum(momentum);
194 
195  if (use_yoyo_model_) {
196  // set the formation time and position in the rest frame of string
197  double t_prod = (xvertex_pos[i] + xvertex_neg[i + 1]) / sqrt2_;
198  FourVector fragment_position = FourVector(
199  t_prod, evecLong * (xvertex_pos[i] - xvertex_neg[i + 1]) / sqrt2_);
200  /* boost formation vertex into the center of mass frame
201  * and then into the lab frame */
202  fragment_position = fragment_position.LorentzBoost(-vstring);
203  fragment_position = fragment_position.LorentzBoost(-vcomAB_);
204  intermediate_particles[i].set_formation_time(
205  soft_t_form_ * fragment_position.x0() + time_collision_);
206  } else {
207  ThreeVector v_calc = momentum.LorentzBoost(-vcomAB_).velocity();
208  double gamma_factor = 1.0 / std::sqrt(1 - (v_calc).sqr());
209  intermediate_particles[i].set_slow_formation_times(
211  time_formation_const_ * gamma_factor + time_collision_);
212  }
213 
214  final_state_.push_back(intermediate_particles[i]);
215  }
216 
217  return nfrag;
218 }
219 
220 void StringProcess::init(const ParticleList &incoming, double tcoll) {
221  PDGcodes_[0] = incoming[0].pdgcode();
222  PDGcodes_[1] = incoming[1].pdgcode();
223  massA_ = incoming[0].effective_mass();
224  massB_ = incoming[1].effective_mass();
225 
226  plab_[0] = incoming[0].momentum();
227  plab_[1] = incoming[1].momentum();
228 
229  // compute sqrts and velocity of the center of mass.
230  sqrtsAB_ = (plab_[0] + plab_[1]).abs();
231  ucomAB_ = (plab_[0] + plab_[1]) / sqrtsAB_;
233 
234  pcom_[0] = plab_[0].LorentzBoost(vcomAB_);
235  pcom_[1] = plab_[1].LorentzBoost(vcomAB_);
236 
237  const double pabscomAB = pCM(sqrtsAB_, massA_, massB_);
238  ThreeVector evec_coll = pcom_[0].threevec() / pabscomAB;
240 
242 
243  time_collision_ = tcoll;
244 }
245 
246 /* single diffractive
247  * is_AB_to_AX = true : A + B -> A + X
248  * is_AB_to_AX = false : A + B -> X + B */
249 bool StringProcess::next_SDiff(bool is_AB_to_AX) {
250  NpartFinal_ = 0;
251  NpartString_[0] = 0;
252  NpartString_[1] = 0;
253  final_state_.clear();
254 
255  double massH = is_AB_to_AX ? massA_ : massB_;
256  double mstrMin = is_AB_to_AX ? massB_ : massA_;
257  double mstrMax = sqrtsAB_ - massH;
258 
259  int idqX1, idqX2;
260  double QTrn, QTrx, QTry;
261  double pabscomHX_sqr, massX;
262 
263  // decompose hadron into quarks
264  make_string_ends(is_AB_to_AX ? PDGcodes_[1] : PDGcodes_[0], idqX1, idqX2,
266  // string mass must be larger than threshold set by PYTHIA.
267  mstrMin = pythia_hadron_->particleData.m0(idqX1) +
268  pythia_hadron_->particleData.m0(idqX2);
269  // this threshold cannot be larger than maximum of allowed string mass.
270  if (mstrMin > mstrMax) {
271  return false;
272  }
273  // sample the transverse momentum transfer
274  QTrx = random::normal(0., sigma_qperp_ / sqrt2_);
275  QTry = random::normal(0., sigma_qperp_ / sqrt2_);
276  QTrn = std::sqrt(QTrx * QTrx + QTry * QTry);
277  /* sample the string mass and
278  * evaluate the three-momenta of hadron and string. */
279  massX = random::power(-1.0, mstrMin, mstrMax);
280  pabscomHX_sqr = pCM_sqr(sqrtsAB_, massH, massX);
281  /* magnitude of the three momentum must be larger
282  * than the transverse momentum. */
283  const bool foundPabsX = pabscomHX_sqr > QTrn * QTrn;
284 
285  if (!foundPabsX) {
286  return false;
287  }
288  double sign_direction = is_AB_to_AX ? 1. : -1.;
289  // determine three momentum of the final state hadron
290  const ThreeVector cm_momentum =
291  sign_direction *
292  (evecBasisAB_[0] * std::sqrt(pabscomHX_sqr - QTrn * QTrn) +
293  evecBasisAB_[1] * QTrx + evecBasisAB_[2] * QTry);
294  const FourVector pstrHcom(std::sqrt(pabscomHX_sqr + massH * massH),
295  cm_momentum);
296  const FourVector pstrXcom(std::sqrt(pabscomHX_sqr + massX * massX),
297  -cm_momentum);
298 
299  const FourVector ustrXcom = pstrXcom / massX;
300  /* determine direction in which the string is stretched.
301  * this is set to be same with the the collision axis
302  * in the center of mass frame. */
303  const ThreeVector threeMomentum =
304  is_AB_to_AX ? pcom_[1].threevec() : pcom_[0].threevec();
305  const FourVector pnull = FourVector(threeMomentum.abs(), threeMomentum);
306  const FourVector prs = pnull.LorentzBoost(ustrXcom.velocity());
307  ThreeVector evec = prs.threevec() / prs.threevec().abs();
308  // perform fragmentation and add particles to final_state.
309  ParticleList new_intermediate_particles;
310  bool separate_fragment_baryon = false;
311  int nfrag =
312  fragment_string(idqX1, idqX2, massX, evec, true, separate_fragment_baryon,
313  new_intermediate_particles);
314  if (nfrag < 1) {
315  NpartString_[0] = 0;
316  return false;
317  }
318  NpartString_[0] =
319  append_final_state(new_intermediate_particles, ustrXcom, evec);
320 
321  NpartString_[1] = 1;
322  PdgCode hadron_code = is_AB_to_AX ? PDGcodes_[0] : PDGcodes_[1];
323  ParticleData new_particle(ParticleType::find(hadron_code));
324  new_particle.set_4momentum(pstrHcom);
325  new_particle.set_cross_section_scaling_factor(1.);
326  new_particle.set_formation_time(time_collision_);
327  final_state_.push_back(new_particle);
328 
330  return true;
331 }
332 
334  const std::array<std::array<int, 2>, 2> &quarks,
335  const std::array<FourVector, 2> &pstr_com, std::array<double, 2> &m_str,
336  std::array<ThreeVector, 2> &evec_str) {
337  std::array<bool, 2> found_mass;
338  for (int i = 0; i < 2; i++) {
339  found_mass[i] = false;
340 
341  m_str[i] = pstr_com[i].sqr();
342  m_str[i] = (m_str[i] > 0.) ? std::sqrt(m_str[i]) : 0.;
343  const double threshold = pythia_hadron_->particleData.m0(quarks[i][0]) +
344  pythia_hadron_->particleData.m0(quarks[i][1]);
345  // string mass must be larger than threshold set by PYTHIA.
346  if (m_str[i] > threshold) {
347  found_mass[i] = true;
348  /* Determine direction in which string i is stretched.
349  * This is set to be same with the collision axis
350  * in the center of mass frame.
351  * Initial state partons inside incoming hadrons are
352  * moving along the collision axis,
353  * which is parallel to three momenta of incoming hadrons
354  * in the center of mass frame.
355  * Given that partons are assumed to be massless,
356  * their four momenta are null vectors and parallel to pnull.
357  * If we take unit three-vector of prs,
358  * which is pnull in the rest frame of string,
359  * it would be the direction in which string ends are moving. */
360  const ThreeVector mom = pcom_[i].threevec();
361  const FourVector pnull(mom.abs(), mom);
362  const FourVector prs = pnull.LorentzBoost(pstr_com[i].velocity());
363  evec_str[i] = prs.threevec() / prs.threevec().abs();
364  }
365  }
366 
367  return found_mass[0] && found_mass[1];
368 }
369 
371  const std::array<std::array<int, 2>, 2> &quarks,
372  const std::array<FourVector, 2> &pstr_com,
373  const std::array<double, 2> &m_str,
374  const std::array<ThreeVector, 2> &evec_str, bool flip_string_ends,
375  bool separate_fragment_baryon) {
376  const std::array<FourVector, 2> ustr_com = {pstr_com[0] / m_str[0],
377  pstr_com[1] / m_str[1]};
378  for (int i = 0; i < 2; i++) {
379  ParticleList new_intermediate_particles;
380 
381  // determine direction in which string i is stretched.
382  ThreeVector evec = evec_str[i];
383  // perform fragmentation and add particles to final_state.
384  int nfrag = fragment_string(quarks[i][0], quarks[i][1], m_str[i], evec,
385  flip_string_ends, separate_fragment_baryon,
386  new_intermediate_particles);
387  if (nfrag <= 0) {
388  NpartString_[i] = 0;
389  return false;
390  }
391  NpartString_[i] =
392  append_final_state(new_intermediate_particles, ustr_com[i], evec);
393  assert(nfrag == NpartString_[i]);
394  }
395  if ((NpartString_[0] > 0) && (NpartString_[1] > 0)) {
397  return true;
398  }
399  return false;
400 }
401 
402 // double-diffractive : A + B -> X + X
404  NpartFinal_ = 0;
405  NpartString_[0] = 0;
406  NpartString_[1] = 0;
407  final_state_.clear();
408 
409  std::array<std::array<int, 2>, 2> quarks;
410  std::array<FourVector, 2> pstr_com;
411  std::array<double, 2> m_str;
412  std::array<ThreeVector, 2> evec_str;
413  ThreeVector threeMomentum;
414 
415  // decompose hadron into quark (and diquark) contents
416  make_string_ends(PDGcodes_[0], quarks[0][0], quarks[0][1],
418  make_string_ends(PDGcodes_[1], quarks[1][0], quarks[1][1],
420  // sample the lightcone momentum fraction carried by gluons
421  const double xmin_gluon_fraction = pmin_gluon_lightcone_ / sqrtsAB_;
422  const double xfracA =
423  random::beta_a0(xmin_gluon_fraction, pow_fgluon_beta_ + 1.);
424  const double xfracB =
425  random::beta_a0(xmin_gluon_fraction, pow_fgluon_beta_ + 1.);
426  // sample the transverse momentum transfer
427  const double QTrx = random::normal(0., sigma_qperp_ / sqrt2_);
428  const double QTry = random::normal(0., sigma_qperp_ / sqrt2_);
429  const double QTrn = std::sqrt(QTrx * QTrx + QTry * QTry);
430  // evaluate the lightcone momentum transfer
431  const double QPos = -QTrn * QTrn / (2. * xfracB * PNegB_);
432  const double QNeg = QTrn * QTrn / (2. * xfracA * PPosA_);
433  // compute four-momentum of string 1
434  threeMomentum = evecBasisAB_[0] * (PPosA_ + QPos - PNegA_ - QNeg) / sqrt2_ +
435  evecBasisAB_[1] * QTrx + evecBasisAB_[2] * QTry;
436  pstr_com[0] =
437  FourVector((PPosA_ + QPos + PNegA_ + QNeg) / sqrt2_, threeMomentum);
438  // compute four-momentum of string 2
439  threeMomentum = evecBasisAB_[0] * (PPosB_ - QPos - PNegB_ + QNeg) / sqrt2_ -
440  evecBasisAB_[1] * QTrx - evecBasisAB_[2] * QTry;
441  pstr_com[1] =
442  FourVector((PPosB_ - QPos + PNegB_ - QNeg) / sqrt2_, threeMomentum);
443 
444  const bool found_masses =
445  set_mass_and_direction_2strings(quarks, pstr_com, m_str, evec_str);
446  if (!found_masses) {
447  return false;
448  }
449  const bool flip_string_ends = true;
450  const bool separate_fragment_baryon = false;
451  const bool success =
452  make_final_state_2strings(quarks, pstr_com, m_str, evec_str,
453  flip_string_ends, separate_fragment_baryon);
454  return success;
455 }
456 
457 // soft non-diffractive
459  NpartFinal_ = 0;
460  NpartString_[0] = 0;
461  NpartString_[1] = 0;
462  final_state_.clear();
463 
464  std::array<std::array<int, 2>, 2> quarks;
465  std::array<FourVector, 2> pstr_com;
466  std::array<double, 2> m_str;
467  std::array<ThreeVector, 2> evec_str;
468 
469  // decompose hadron into quark (and diquark) contents
470  int idqA1, idqA2, idqB1, idqB2;
473 
474  const int bar_a = PDGcodes_[0].baryon_number(),
475  bar_b = PDGcodes_[1].baryon_number();
476  if (bar_a == 1 || // baryon-baryon, baryon-meson, baryon-antibaryon
477  (bar_a == 0 && bar_b == 1) || // meson-baryon
478  (bar_a == 0 && bar_b == 0)) { // meson-meson
479  quarks[0][0] = idqB1;
480  quarks[0][1] = idqA2;
481  quarks[1][0] = idqA1;
482  quarks[1][1] = idqB2;
483  } else if (((bar_a == 0) && (bar_b == -1)) || // meson-antibaryon
484  (bar_a == -1)) { // antibaryon-baryon, antibaryon-meson,
485  // antibaryon-antibaryon
486  quarks[0][0] = idqA1;
487  quarks[0][1] = idqB2;
488  quarks[1][0] = idqB1;
489  quarks[1][1] = idqA2;
490  } else {
491  std::stringstream ss;
492  ss << " StringProcess::next_NDiff : baryonA = " << bar_a
493  << ", baryonB = " << bar_b;
494  throw std::runtime_error(ss.str());
495  }
496  // sample the lightcone momentum fraction carried by quarks
497  const double xfracA = random::beta(pow_fquark_alpha_, pow_fquark_beta_);
498  const double xfracB = random::beta(pow_fquark_alpha_, pow_fquark_beta_);
499  // sample the transverse momentum transfer
500  const double QTrx = random::normal(0., sigma_qperp_ / sqrt2_);
501  const double QTry = random::normal(0., sigma_qperp_ / sqrt2_);
502  const double QTrn = std::sqrt(QTrx * QTrx + QTry * QTry);
503  // evaluate the lightcone momentum transfer
504  const double QPos = -QTrn * QTrn / (2. * xfracB * PNegB_);
505  const double QNeg = QTrn * QTrn / (2. * xfracA * PPosA_);
506  const double dPPos = -xfracA * PPosA_ - QPos;
507  const double dPNeg = xfracB * PNegB_ - QNeg;
508  // compute four-momentum of string 1
509  ThreeVector threeMomentum =
510  evecBasisAB_[0] * (PPosA_ + dPPos - PNegA_ - dPNeg) / sqrt2_ +
511  evecBasisAB_[1] * QTrx + evecBasisAB_[2] * QTry;
512  pstr_com[0] =
513  FourVector((PPosA_ + dPPos + PNegA_ + dPNeg) / sqrt2_, threeMomentum);
514  m_str[0] = pstr_com[0].sqr();
515  // compute four-momentum of string 2
516  threeMomentum = evecBasisAB_[0] * (PPosB_ - dPPos - PNegB_ + dPNeg) / sqrt2_ -
517  evecBasisAB_[1] * QTrx - evecBasisAB_[2] * QTry;
518  pstr_com[1] =
519  FourVector((PPosB_ - dPPos + PNegB_ - dPNeg) / sqrt2_, threeMomentum);
520 
521  const bool found_masses =
522  set_mass_and_direction_2strings(quarks, pstr_com, m_str, evec_str);
523  if (!found_masses) {
524  return false;
525  }
526  const bool flip_string_ends = false;
527  const bool separate_fragment_baryon = true;
528  const bool success =
529  make_final_state_2strings(quarks, pstr_com, m_str, evec_str,
530  flip_string_ends, separate_fragment_baryon);
531  return success;
532 }
533 
534 // hard non-diffractive
536  const auto &log = logger<LogArea::Pythia>();
537  NpartFinal_ = 0;
538  final_state_.clear();
539 
540  log.debug("Hard non-diff. with ", PDGcodes_[0], " + ", PDGcodes_[1],
541  " at CM energy [GeV] ", sqrtsAB_);
542 
543  std::array<int, 2> pdg_for_pythia;
544  std::array<std::array<int, 5>, 2> excess_quark;
545  std::array<std::array<int, 5>, 2> excess_antiq;
546  for (int i = 0; i < 2; i++) {
547  for (int j = 0; j < 5; j++) {
548  excess_quark[i][j] = 0;
549  excess_antiq[i][j] = 0;
550  }
551 
552  // get PDG id used in PYTHIA event generation
553  pdg_for_pythia[i] = pdg_map_for_pythia(PDGcodes_[i]);
554  log.debug(" incoming particle ", i, " : ", PDGcodes_[i],
555  " is mapped onto ", pdg_for_pythia[i]);
556 
557  PdgCode pdgcode_for_pythia(std::to_string(pdg_for_pythia[i]));
558  /* evaluate how many more constituents incoming hadron has
559  * compared to the mapped one. */
560  find_excess_constituent(PDGcodes_[i], pdgcode_for_pythia, excess_quark[i],
561  excess_antiq[i]);
562  log.debug(" excess_quark[", i, "] = (", excess_quark[i][0], ", ",
563  excess_quark[i][1], ", ", excess_quark[i][2], ", ",
564  excess_quark[i][3], ", ", excess_quark[i][4], ")");
565  log.debug(" excess_antiq[", i, "] = (", excess_antiq[i][0], ", ",
566  excess_antiq[i][1], ", ", excess_antiq[i][2], ", ",
567  excess_antiq[i][3], ", ", excess_antiq[i][4], ")");
568  }
569 
570  int previous_idA = pythia_parton_->mode("Beams:idA"),
571  previous_idB = pythia_parton_->mode("Beams:idB");
572  double previous_eCM = pythia_parton_->parm("Beams:eCM");
573  // check if the initial state for PYTHIA remains same.
574  bool same_initial_state = previous_idA == pdg_for_pythia[0] &&
575  previous_idB == pdg_for_pythia[1] &&
576  std::abs(previous_eCM - sqrtsAB_) < really_small;
577 
578  /* Perform PYTHIA initialization if it was not previously initialized
579  * or the initial state changed. */
580  if (!pythia_parton_initialized_ || !same_initial_state) {
581  pythia_parton_->settings.mode("Beams:idA", pdg_for_pythia[0]);
582  pythia_parton_->settings.mode("Beams:idB", pdg_for_pythia[1]);
583  pythia_parton_->settings.parm("Beams:eCM", sqrtsAB_);
584 
586  log.debug("Pythia initialized with ", pdg_for_pythia[0], " + ",
587  pdg_for_pythia[1], " at CM energy [GeV] ", sqrtsAB_);
589  throw std::runtime_error("Pythia failed to initialize.");
590  }
591  }
592  /* Set the random seed of the Pythia random Number Generator.
593  * Pythia's random is controlled by SMASH in every single collision.
594  * In this way we ensure that the results are reproducible
595  * for every event if one knows SMASH random seed. */
596  const int seed_new = random::uniform_int(1, maximum_rndm_seed_in_pythia);
597  pythia_parton_->rndm.init(seed_new);
598  log.debug("pythia_parton_ : rndm is initialized with seed ", seed_new);
599 
600  // Short notation for Pythia event
601  Pythia8::Event &event_hadron = pythia_hadron_->event;
602  log.debug("Pythia hard event created");
603  bool final_state_success = pythia_parton_->next();
604  log.debug("Pythia final state computed, success = ", final_state_success);
605  if (!final_state_success) {
606  return false;
607  }
608 
609  ParticleList new_intermediate_particles;
610  ParticleList new_non_hadron_particles;
611 
612  Pythia8::Vec4 pSum = 0.;
613  event_intermediate_.reset();
614  /* Update the partonic intermediate state from PYTHIA output.
615  * Note that hadronization will be performed separately,
616  * after identification of strings and replacement of constituents. */
617  for (int i = 0; i < pythia_parton_->event.size(); i++) {
618  if (pythia_parton_->event[i].isFinal()) {
619  const int pdgid = pythia_parton_->event[i].id();
620  Pythia8::Vec4 pquark = pythia_parton_->event[i].p();
621  const double mass = pythia_parton_->particleData.m0(pdgid);
622 
623  const int status = pythia_parton_->event[i].status();
624  const int color = pythia_parton_->event[i].col();
625  const int anticolor = pythia_parton_->event[i].acol();
626 
627  pSum += pquark;
628  event_intermediate_.append(pdgid, status, color, anticolor, pquark, mass);
629  }
630  }
631  // add junctions to the intermediate state if there is any.
632  event_intermediate_.clearJunctions();
633  for (int i = 0; i < pythia_parton_->event.sizeJunction(); i++) {
634  const int kind = pythia_parton_->event.kindJunction(i);
635  std::array<int, 3> col;
636  for (int j = 0; j < 3; j++) {
637  col[j] = pythia_parton_->event.colJunction(i, j);
638  }
639  event_intermediate_.appendJunction(kind, col[0], col[1], col[2]);
640  }
641  /* The zeroth entry of event record is supposed to have the information
642  * on the whole system. Specify the total momentum and invariant mass. */
643  event_intermediate_[0].p(pSum);
644  event_intermediate_[0].m(pSum.mCalc());
645 
646  /* Replace quark constituents according to the excess of valence quarks
647  * and then rescale momenta of partons by constant factor
648  * to fulfill the energy-momentum conservation. */
649  bool correct_constituents =
650  restore_constituent(event_intermediate_, excess_quark, excess_antiq);
651  if (!correct_constituents) {
652  log.debug("failed to find correct partonic constituents.");
653  return false;
654  }
655 
656  int npart = event_intermediate_.size();
657  int ipart = 0;
658  while (ipart < npart) {
659  const int pdgid = event_intermediate_[ipart].id();
660  if (event_intermediate_[ipart].isFinal() &&
661  !event_intermediate_[ipart].isParton() &&
662  !pythia_parton_->particleData.isOctetHadron(pdgid)) {
663  log.debug("PDG ID from Pythia: ", pdgid);
665  log.debug("4-momentum from Pythia: ", momentum);
666  bool found_ptype =
667  append_intermediate_list(pdgid, momentum, new_non_hadron_particles);
668  if (!found_ptype) {
669  log.warn("PDG ID ", pdgid,
670  " does not exist in ParticleType - start over.");
671  final_state_success = false;
672  }
673  event_intermediate_.remove(ipart, ipart);
674  npart -= 1;
675  } else {
676  ipart += 1;
677  }
678  }
679 
680  bool hadronize_success = false;
681  bool find_forward_string = true;
682  log.debug("Hard non-diff: partonic process gives ",
683  event_intermediate_.size(), " partons.");
684  // identify and fragment strings until there is no parton left.
685  while (event_intermediate_.size() > 1) {
686  // dummy event to initialize the internal variables of PYTHIA.
687  pythia_hadron_->event.reset();
688  if (!pythia_hadron_->next()) {
689  log.debug(" Dummy event in hard string routine failed.");
690  hadronize_success = false;
691  break;
692  }
693 
694  if (event_intermediate_.sizeJunction() > 0) {
695  // identify string from a junction if there is any.
696  compose_string_junction(find_forward_string, event_intermediate_,
697  pythia_hadron_->event);
698  } else {
699  /* identify string from a most forward or backward parton.
700  * if there is no junction. */
701  compose_string_parton(find_forward_string, event_intermediate_,
702  pythia_hadron_->event);
703  }
704 
705  // fragment the (identified) string into hadrons.
706  hadronize_success = pythia_hadron_->forceHadronLevel(false);
707  log.debug("Pythia hadronized, success = ", hadronize_success);
708 
709  new_intermediate_particles.clear();
710  if (hadronize_success) {
711  for (int i = 0; i < event_hadron.size(); i++) {
712  if (event_hadron[i].isFinal()) {
713  int pythia_id = event_hadron[i].id();
714  log.debug("PDG ID from Pythia: ", pythia_id);
715  /* K_short and K_long need to be converted to K0
716  * since SMASH only knows K0 */
717  convert_KaonLS(pythia_id);
718 
719  /* evecBasisAB_[0] is a unit 3-vector in the collision axis,
720  * while evecBasisAB_[1] and evecBasisAB_[2] spans the transverse
721  * plane. Given that PYTHIA assumes z-direction to be the collision
722  * axis, pz from PYTHIA should be the momentum compoment in
723  * evecBasisAB_[0]. px and py are respectively the momentum components
724  * in two transverse directions evecBasisAB_[1] and evecBasisAB_[2].
725  */
726  FourVector momentum = reorient(event_hadron[i], evecBasisAB_);
727  log.debug("4-momentum from Pythia: ", momentum);
728  log.debug("appending the particle ", pythia_id,
729  " to the intermediate particle list.");
730  bool found_ptype = false;
731  if (event_hadron[i].isHadron()) {
732  found_ptype = append_intermediate_list(pythia_id, momentum,
733  new_intermediate_particles);
734  } else {
735  found_ptype = append_intermediate_list(pythia_id, momentum,
736  new_non_hadron_particles);
737  }
738  if (!found_ptype) {
739  log.warn("PDG ID ", pythia_id,
740  " does not exist in ParticleType - start over.");
741  hadronize_success = false;
742  }
743  }
744  }
745  }
746 
747  /* if hadronization is not successful,
748  * reset the event records, return false and then start over. */
749  if (!hadronize_success) {
750  break;
751  }
752 
753  FourVector uString = FourVector(1., 0., 0., 0.);
754  ThreeVector evec = find_forward_string ? evecBasisAB_[0] : -evecBasisAB_[0];
755  int nfrag = append_final_state(new_intermediate_particles, uString, evec);
756  NpartFinal_ += nfrag;
757 
758  find_forward_string = !find_forward_string;
759  }
760 
761  if (hadronize_success) {
762  // add the final state particles, which are not hadron.
763  for (ParticleData data : new_non_hadron_particles) {
764  data.set_cross_section_scaling_factor(1.);
765  data.set_formation_time(time_collision_);
766  final_state_.push_back(data);
767  }
768  } else {
769  final_state_.clear();
770  }
771 
772  return hadronize_success;
773 }
774 
776  PdgCode &pdg_mapped,
777  std::array<int, 5> &excess_quark,
778  std::array<int, 5> &excess_antiq) {
779  /* decompose PDG id of the actual hadron and mapped one
780  * to get the valence quark constituents */
781  std::array<int, 3> qcontent_actual = pdg_actual.quark_content();
782  std::array<int, 3> qcontent_mapped = pdg_mapped.quark_content();
783 
784  excess_quark = {0, 0, 0, 0, 0};
785  excess_antiq = {0, 0, 0, 0, 0};
786  for (int i = 0; i < 3; i++) {
787  if (qcontent_actual[i] > 0) { // quark content of the actual hadron
788  int j = qcontent_actual[i] - 1;
789  excess_quark[j] += 1;
790  }
791 
792  if (qcontent_mapped[i] > 0) { // quark content of the mapped hadron
793  int j = qcontent_mapped[i] - 1;
794  excess_quark[j] -= 1;
795  }
796 
797  if (qcontent_actual[i] < 0) { // antiquark content of the actual hadron
798  int j = std::abs(qcontent_actual[i]) - 1;
799  excess_antiq[j] += 1;
800  }
801 
802  if (qcontent_mapped[i] < 0) { // antiquark content of the mapped hadron
803  int j = std::abs(qcontent_mapped[i]) - 1;
804  excess_antiq[j] -= 1;
805  }
806  }
807 }
808 
810  Pythia8::Particle &particle, std::array<int, 5> &excess_constituent) {
811  const auto &log = logger<LogArea::Pythia>();
812 
813  // If the particle is neither quark nor diquark, nothing to do.
814  if (!particle.isQuark() && !particle.isDiquark()) {
815  return;
816  }
817 
818  // If there is no excess of constituents, nothing to do.
819  const std::array<int, 5> excess_null = {0, 0, 0, 0, 0};
820  if (excess_constituent == excess_null) {
821  return;
822  }
823 
824  int nq = 0;
825  std::array<int, 2> pdgid = {0, 0};
826  int spin_deg = 0;
827  int pdgid_new = 0;
828  if (particle.isQuark()) {
829  nq = 1;
830  pdgid[0] = particle.id();
831  } else if (particle.isDiquark()) {
832  nq = 2;
833  quarks_from_diquark(particle.id(), pdgid[0], pdgid[1], spin_deg);
834  }
835 
836  for (int iq = 0; iq < nq; iq++) {
837  int jq = std::abs(pdgid[iq]) - 1;
838  int k_select = 0;
839  std::vector<int> k_found;
840  k_found.clear();
841  // check if the constituent needs to be converted.
842  if (excess_constituent[jq] < 0) {
843  for (int k = 0; k < 5; k++) {
844  // check which specie it can be converted into.
845  if (k != jq && excess_constituent[k] > 0) {
846  k_found.push_back(k);
847  }
848  }
849  }
850 
851  // make a random selection of specie and update the excess of constituent.
852  if (k_found.size() > 0) {
853  const int l =
854  random::uniform_int(0, static_cast<int>(k_found.size()) - 1);
855  k_select = k_found[l];
856  /* flavor jq + 1 is converted into k_select + 1
857  * and excess_constituent is updated. */
858  pdgid[iq] = pdgid[iq] > 0 ? k_select + 1 : -(k_select + 1);
859  excess_constituent[jq] += 1;
860  excess_constituent[k_select] -= 1;
861  }
862  }
863 
864  // determine PDG id of the converted parton.
865  if (particle.isQuark()) {
866  pdgid_new = pdgid[0];
867  } else if (particle.isDiquark()) {
868  if (std::abs(pdgid[0]) < std::abs(pdgid[1])) {
869  std::swap(pdgid[0], pdgid[1]);
870  }
871 
872  pdgid_new = std::abs(pdgid[0]) * 1000 + std::abs(pdgid[1]) * 100;
873  if (std::abs(pdgid[0]) == std::abs(pdgid[1])) {
874  pdgid_new += 3;
875  } else {
876  pdgid_new += spin_deg;
877  }
878 
879  if (particle.id() < 0) {
880  pdgid_new *= -1;
881  }
882  }
883  log.debug(" parton id = ", particle.id(), " is converted to ", pdgid_new);
884 
885  // update the constituent mass and energy.
886  Pythia8::Vec4 pquark = particle.p();
887  double mass_new = pythia_hadron_->particleData.m0(pdgid_new);
888  double e_new = std::sqrt(mass_new * mass_new + pquark.pAbs() * pquark.pAbs());
889  // update the particle object.
890  particle.id(pdgid_new);
891  particle.e(e_new);
892  particle.m(mass_new);
893 }
894 
896  Pythia8::Event &event_intermediate, std::array<int, 5> &nquark_total,
897  std::array<int, 5> &nantiq_total) {
898  for (int iflav = 0; iflav < 5; iflav++) {
899  nquark_total[iflav] = 0;
900  nantiq_total[iflav] = 0;
901  }
902 
903  for (int ip = 1; ip < event_intermediate.size(); ip++) {
904  if (!event_intermediate[ip].isFinal()) {
905  continue;
906  }
907  const int pdgid = event_intermediate[ip].id();
908  if (pdgid > 0) {
909  // quarks
910  for (int iflav = 0; iflav < 5; iflav++) {
911  nquark_total[iflav] +=
912  pythia_hadron_->particleData.nQuarksInCode(pdgid, iflav + 1);
913  }
914  } else {
915  // antiquarks
916  for (int iflav = 0; iflav < 5; iflav++) {
917  nantiq_total[iflav] += pythia_hadron_->particleData.nQuarksInCode(
918  std::abs(pdgid), iflav + 1);
919  }
920  }
921  }
922 }
923 
925  Pythia8::Event &event_intermediate, std::array<int, 5> &nquark_total,
926  std::array<int, 5> &nantiq_total, bool sign_constituent,
927  std::array<std::array<int, 5>, 2> &excess_constituent) {
928  const auto &log = logger<LogArea::Pythia>();
929 
930  Pythia8::Vec4 pSum = event_intermediate[0].p();
931 
932  /* compute total number of quark and antiquark constituents
933  * in the whole system. */
934  find_total_number_constituent(event_intermediate, nquark_total, nantiq_total);
935 
936  for (int iflav = 0; iflav < 5; iflav++) {
937  /* Find how many constituent will be in the system after
938  * changing the flavors.
939  * Note that nquark_total is number of constituent right after
940  * the pythia event (with mapped incoming hadrons), while the excess
941  * shows how many constituents we have more or less that nquark_total. */
942  int nquark_final =
943  excess_constituent[0][iflav] + excess_constituent[1][iflav];
944  if (sign_constituent) {
945  nquark_final += nquark_total[iflav];
946  } else {
947  nquark_final += nantiq_total[iflav];
948  }
949  /* Therefore, nquark_final should not be negative.
950  * negative nquark_final means that it will not be possible to
951  * find a constituent to change the flavor. */
952  bool enough_quark = nquark_final >= 0;
953  /* If that is the case, a gluon will be splitted into
954  * a quark-antiquark pair with the desired flavor. */
955  if (!enough_quark) {
956  log.debug(" not enough constituents with flavor ", iflav + 1,
957  " : try to split a gluon to qqbar.");
958  for (int ic = 0; ic < std::abs(nquark_final); ic++) {
959  /* Since each incoming hadron has its own count of the excess,
960  * it is necessary to find which one is problematic. */
961  int ih_mod = -1;
962  if (excess_constituent[0][iflav] < 0) {
963  ih_mod = 0;
964  } else {
965  ih_mod = 1;
966  }
967 
968  /* find the most forward or backward gluon
969  * depending on which incoming hadron is found to be an issue. */
970  int iforward = 1;
971  for (int ip = 2; ip < event_intermediate.size(); ip++) {
972  if (!event_intermediate[ip].isFinal() ||
973  !event_intermediate[ip].isGluon()) {
974  continue;
975  }
976 
977  const double y_gluon_current = event_intermediate[ip].y();
978  const double y_gluon_forward = event_intermediate[iforward].y();
979  if ((ih_mod == 0 && y_gluon_current > y_gluon_forward) ||
980  (ih_mod == 1 && y_gluon_current < y_gluon_forward)) {
981  iforward = ip;
982  }
983  }
984 
985  if (!event_intermediate[iforward].isGluon()) {
986  log.debug("There is no gluon to split into qqbar.");
987  return false;
988  }
989 
990  // four momentum of the original gluon
991  Pythia8::Vec4 pgluon = event_intermediate[iforward].p();
992 
993  const int pdgid = iflav + 1;
994  const double mass = pythia_hadron_->particleData.m0(pdgid);
995  const int status = event_intermediate[iforward].status();
996  /* color and anticolor indices.
997  * the color index of gluon goes to the quark, while
998  * the anticolor index goes to the antiquark */
999  const int col = event_intermediate[iforward].col();
1000  const int acol = event_intermediate[iforward].acol();
1001 
1002  // three momenta of quark and antiquark
1003  std::array<double, 2> px_quark;
1004  std::array<double, 2> py_quark;
1005  std::array<double, 2> pz_quark;
1006  // energies of quark and antiquark
1007  std::array<double, 2> e_quark;
1008  // four momenta of quark and antiquark
1009  std::array<Pythia8::Vec4, 2> pquark;
1010  // transverse momentum scale of string fragmentation
1011  const double sigma_qt_frag = pythia_hadron_->parm("StringPT:sigma");
1012  // sample relative transverse momentum between quark and antiquark
1013  const double qx = random::normal(0., sigma_qt_frag / sqrt2_);
1014  const double qy = random::normal(0., sigma_qt_frag / sqrt2_);
1015  // setup kinematics
1016  for (int isign = 0; isign < 2; isign++) {
1017  /* As the simplest assumption, the three momentum of gluon
1018  * is equally distributed to quark and antiquark.
1019  * Then, they have a relative transverse momentum. */
1020  px_quark[isign] = 0.5 * pgluon.px() + (isign == 0 ? 1. : -1.) * qx;
1021  py_quark[isign] = 0.5 * pgluon.py() + (isign == 0 ? 1. : -1.) * qy;
1022  pz_quark[isign] = 0.5 * pgluon.pz();
1023  e_quark[isign] =
1024  std::sqrt(mass * mass + px_quark[isign] * px_quark[isign] +
1025  py_quark[isign] * py_quark[isign] +
1026  pz_quark[isign] * pz_quark[isign]);
1027  pquark[isign] = Pythia8::Vec4(px_quark[isign], py_quark[isign],
1028  pz_quark[isign], e_quark[isign]);
1029  }
1030 
1031  /* Total energy is not conserved at this point,
1032  * but this will be cured later. */
1033  pSum += pquark[0] + pquark[1] - pgluon;
1034  // add quark and antiquark to the event record
1035  event_intermediate.append(pdgid, status, col, 0, pquark[0], mass);
1036  event_intermediate.append(-pdgid, status, 0, acol, pquark[1], mass);
1037  // then remove the gluon from the record
1038  event_intermediate.remove(iforward, iforward);
1039 
1040  log.debug(" gluon at iforward = ", iforward, " is splitted into ",
1041  pdgid, ",", -pdgid, " qqbar pair.");
1042  /* Increase the total number of quarks and antiquarks by 1,
1043  * as we have extra ones from a gluon. */
1044  nquark_total[iflav] += 1;
1045  nantiq_total[iflav] += 1;
1046  }
1047  }
1048  }
1049 
1050  /* The zeroth entry of event record is supposed to have the information
1051  * on the whole system. Specify the total momentum and invariant mass. */
1052  event_intermediate[0].p(pSum);
1053  event_intermediate[0].m(pSum.mCalc());
1054 
1055  return true;
1056 }
1057 
1059  std::array<int, 5> &nquark_total,
1060  std::array<std::array<int, 5>, 2> &excess_quark,
1061  std::array<std::array<int, 5>, 2> &excess_antiq) {
1062  const auto &log = logger<LogArea::Pythia>();
1063 
1064  for (int iflav = 0; iflav < 5; iflav++) {
1065  /* Find how many constituent will be in the system after
1066  * changing the flavors.
1067  * Note that nquark_total is number of constituent right after
1068  * the pythia event (with mapped incoming hadrons), while the excess
1069  * shows how many constituents we have more or less that nquark_total. */
1070  int nquark_final =
1071  nquark_total[iflav] + excess_quark[0][iflav] + excess_quark[1][iflav];
1072  /* Therefore, nquark_final should not be negative.
1073  * negative nquark_final means that it will not be possible to
1074  * find a constituent to change the flavor. */
1075  bool enough_quark = nquark_final >= 0;
1076  // If that is the case, excess of constituents will be modified
1077  if (!enough_quark) {
1078  log.debug(" not enough constituents with flavor ", iflav + 1,
1079  " : try to modify excess of constituents.");
1080  for (int ic = 0; ic < std::abs(nquark_final); ic++) {
1081  /* Since each incoming hadron has its own count of the excess,
1082  * it is necessary to find which one is problematic. */
1083  int ih_mod = -1;
1084  if (excess_quark[0][iflav] < 0) {
1085  ih_mod = 0;
1086  } else {
1087  ih_mod = 1;
1088  }
1089  /* Increase the excess of both quark and antiquark
1090  * with corresponding flavor (iflav + 1) by 1.
1091  * This is for conservation of the net quark number. */
1092  excess_quark[ih_mod][iflav] += 1;
1093  excess_antiq[ih_mod][iflav] += 1;
1094 
1095  /* Since incoming hadrons are mapped onto ones with
1096  * the same baryon number (or quark number),
1097  * summation of the excesses over all flavors should be zero.
1098  * Therefore, we need to find another flavor which has
1099  * a positive excess and subtract by 1. */
1100  for (int jflav = 0; jflav < 5; jflav++) {
1101  // another flavor with positive excess of constituents
1102  if (jflav != iflav && excess_quark[ih_mod][jflav] > 0) {
1103  /* Decrease the excess of both quark and antiquark
1104  * with corresponding flavor (jflav + 1) by 1. */
1105  excess_quark[ih_mod][jflav] -= 1;
1106  excess_antiq[ih_mod][jflav] -= 1;
1107  /* We only need to find one (another) flavor to subtract.
1108  * No more! */
1109  break;
1110  }
1111  }
1112  }
1113  }
1114  }
1115 }
1116 
1118  Pythia8::Event &event_intermediate,
1119  std::array<std::array<int, 5>, 2> &excess_quark,
1120  std::array<std::array<int, 5>, 2> &excess_antiq) {
1121  const auto &log = logger<LogArea::Pythia>();
1122 
1123  Pythia8::Vec4 pSum = event_intermediate[0].p();
1124  const double energy_init = pSum.e();
1125  log.debug(" initial total energy [GeV] : ", energy_init);
1126 
1127  // Total number of quarks and antiquarks, respectively.
1128  std::array<int, 5> nquark_total;
1129  std::array<int, 5> nantiq_total;
1130 
1131  /* Split a gluon into qqbar if we do not have enough constituents
1132  * to be converted in the system. */
1133  bool split_for_quark = splitting_gluon_qqbar(
1134  event_intermediate, nquark_total, nantiq_total, true, excess_quark);
1135  bool split_for_antiq = splitting_gluon_qqbar(
1136  event_intermediate, nquark_total, nantiq_total, false, excess_antiq);
1137 
1138  /* Modify excess_quark and excess_antiq if we do not have enough constituents
1139  * to be converted in the system. */
1140  if (!split_for_quark || !split_for_antiq) {
1141  rearrange_excess(nquark_total, excess_quark, excess_antiq);
1142  rearrange_excess(nantiq_total, excess_antiq, excess_quark);
1143  }
1144 
1145  // Final check if there are enough constituents.
1146  for (int iflav = 0; iflav < 5; iflav++) {
1147  if (nquark_total[iflav] + excess_quark[0][iflav] + excess_quark[1][iflav] <
1148  0) {
1149  log.debug("Not enough quark constituents of flavor ", iflav + 1);
1150  return false;
1151  }
1152 
1153  if (nantiq_total[iflav] + excess_antiq[0][iflav] + excess_antiq[1][iflav] <
1154  0) {
1155  log.debug("Not enough antiquark constituents of flavor ", -(iflav + 1));
1156  return false;
1157  }
1158  }
1159 
1160  for (int ih = 0; ih < 2; ih++) {
1161  log.debug(" initial excess_quark[", ih, "] = (", excess_quark[ih][0], ", ",
1162  excess_quark[ih][1], ", ", excess_quark[ih][2], ", ",
1163  excess_quark[ih][3], ", ", excess_quark[ih][4], ")");
1164  log.debug(" initial excess_antiq[", ih, "] = (", excess_antiq[ih][0], ", ",
1165  excess_antiq[ih][1], ", ", excess_antiq[ih][2], ", ",
1166  excess_antiq[ih][3], ", ", excess_antiq[ih][4], ")");
1167  }
1168 
1169  bool recovered_quarks = false;
1170  while (!recovered_quarks) {
1171  /* Flavor conversion begins with the most forward and backward parton
1172  * respectively for incoming_particles_[0] and incoming_particles_[1]. */
1173  std::array<bool, 2> find_forward = {true, false};
1174  const std::array<int, 5> excess_null = {0, 0, 0, 0, 0};
1175  std::array<int, 5> excess_total = excess_null;
1176 
1177  for (int ih = 0; ih < 2; ih++) { // loop over incoming hadrons
1178  int nfrag = event_intermediate.size();
1179  for (int np_end = 0; np_end < nfrag - 1; np_end++) { // constituent loop
1180  /* select the np_end-th most forward or backward parton and
1181  * change its specie.
1182  * np_end = 0 corresponds to the most forward,
1183  * np_end = 1 corresponds to the second most forward and so on. */
1184  int iforward =
1185  get_index_forward(find_forward[ih], np_end, event_intermediate);
1186  pSum -= event_intermediate[iforward].p();
1187 
1188  if (event_intermediate[iforward].id() > 0) { // quark and diquark
1189  replace_constituent(event_intermediate[iforward], excess_quark[ih]);
1190  log.debug(" excess_quark[", ih, "] = (", excess_quark[ih][0], ", ",
1191  excess_quark[ih][1], ", ", excess_quark[ih][2], ", ",
1192  excess_quark[ih][3], ", ", excess_quark[ih][4], ")");
1193  } else { // antiquark and anti-diquark
1194  replace_constituent(event_intermediate[iforward], excess_antiq[ih]);
1195  log.debug(" excess_antiq[", ih, "] = (", excess_antiq[ih][0], ", ",
1196  excess_antiq[ih][1], ", ", excess_antiq[ih][2], ", ",
1197  excess_antiq[ih][3], ", ", excess_antiq[ih][4], ")");
1198  }
1199 
1200  const int pdgid = event_intermediate[iforward].id();
1201  Pythia8::Vec4 pquark = event_intermediate[iforward].p();
1202  const double mass = pythia_hadron_->particleData.m0(pdgid);
1203 
1204  const int status = event_intermediate[iforward].status();
1205  const int color = event_intermediate[iforward].col();
1206  const int anticolor = event_intermediate[iforward].acol();
1207 
1208  pSum += pquark;
1209  event_intermediate.append(pdgid, status, color, anticolor, pquark,
1210  mass);
1211 
1212  event_intermediate.remove(iforward, iforward);
1213  /* Now the last np_end + 1 entries in event_intermediate
1214  * are np_end + 1 most forward (or backward) partons. */
1215  }
1216 
1217  // Compute the excess of net quark numbers.
1218  for (int j = 0; j < 5; j++) {
1219  excess_total[j] += (excess_quark[ih][j] - excess_antiq[ih][j]);
1220  }
1221  }
1222 
1223  /* If there is no excess of net quark numbers,
1224  * quark content is considered to be correct. */
1225  recovered_quarks = excess_total == excess_null;
1226  }
1227  log.debug(" valence quark contents of hadons are recovered.");
1228 
1229  log.debug(" current total energy [GeV] : ", pSum.e());
1230  /* rescale momenta of all partons by a constant factor
1231  * to conserve the total energy. */
1232  while (true) {
1233  if (std::abs(pSum.e() - energy_init) < really_small * energy_init) {
1234  break;
1235  }
1236 
1237  double energy_current = pSum.e();
1238  double slope = 0.;
1239  for (int i = 1; i < event_intermediate.size(); i++) {
1240  slope += event_intermediate[i].pAbs2() / event_intermediate[i].e();
1241  }
1242 
1243  const double rescale_factor = 1. + (energy_init - energy_current) / slope;
1244  pSum = 0.;
1245  for (int i = 1; i < event_intermediate.size(); i++) {
1246  const double px = rescale_factor * event_intermediate[i].px();
1247  const double py = rescale_factor * event_intermediate[i].py();
1248  const double pz = rescale_factor * event_intermediate[i].pz();
1249  const double pabs = rescale_factor * event_intermediate[i].pAbs();
1250  const double mass = event_intermediate[i].m();
1251 
1252  event_intermediate[i].px(px);
1253  event_intermediate[i].py(py);
1254  event_intermediate[i].pz(pz);
1255  event_intermediate[i].e(std::sqrt(mass * mass + pabs * pabs));
1256  pSum += event_intermediate[i].p();
1257  }
1258  log.debug(" parton momenta are rescaled by factor of ", rescale_factor);
1259  }
1260 
1261  log.debug(" final total energy [GeV] : ", pSum.e());
1262  /* The zeroth entry of event record is supposed to have the information
1263  * on the whole system. Specify the total momentum and invariant mass. */
1264  event_intermediate[0].p(pSum);
1265  event_intermediate[0].m(pSum.mCalc());
1266 
1267  return true;
1268 }
1269 
1270 void StringProcess::compose_string_parton(bool find_forward_string,
1271  Pythia8::Event &event_intermediate,
1272  Pythia8::Event &event_hadronize) {
1273  const auto &log = logger<LogArea::Pythia>();
1274 
1275  Pythia8::Vec4 pSum = 0.;
1276  event_hadronize.reset();
1277 
1278  // select the most forward or backward parton.
1279  int iforward = get_index_forward(find_forward_string, 0, event_intermediate);
1280  log.debug("Hard non-diff: iforward = ", iforward, "(",
1281  event_intermediate[iforward].id(), ")");
1282 
1283  pSum += event_intermediate[iforward].p();
1284  event_hadronize.append(event_intermediate[iforward]);
1285 
1286  int col_to_find = event_intermediate[iforward].acol();
1287  int acol_to_find = event_intermediate[iforward].col();
1288  event_intermediate.remove(iforward, iforward);
1289  log.debug("Hard non-diff: event_intermediate reduces in size to ",
1290  event_intermediate.size());
1291 
1292  // trace color and anti-color indices and find corresponding partons.
1293  while (col_to_find != 0 || acol_to_find != 0) {
1294  log.debug(" col_to_find = ", col_to_find,
1295  ", acol_to_find = ", acol_to_find);
1296 
1297  int ifound = -1;
1298  for (int i = 1; i < event_intermediate.size(); i++) {
1299  const int pdgid = event_intermediate[i].id();
1300  bool found_col =
1301  col_to_find != 0 && col_to_find == event_intermediate[i].col();
1302  bool found_acol =
1303  acol_to_find != 0 && acol_to_find == event_intermediate[i].acol();
1304  if (found_col) {
1305  log.debug(" col_to_find ", col_to_find, " from i ", i, "(", pdgid,
1306  ") found");
1307  }
1308  if (found_acol) {
1309  log.debug(" acol_to_find ", acol_to_find, " from i ", i, "(", pdgid,
1310  ") found");
1311  }
1312 
1313  if (found_col && !found_acol) {
1314  ifound = i;
1315  col_to_find = event_intermediate[i].acol();
1316  break;
1317  } else if (!found_col && found_acol) {
1318  ifound = i;
1319  acol_to_find = event_intermediate[i].col();
1320  break;
1321  } else if (found_col && found_acol) {
1322  ifound = i;
1323  col_to_find = 0;
1324  acol_to_find = 0;
1325  break;
1326  }
1327  }
1328 
1329  if (ifound < 0) {
1330  event_intermediate.list();
1331  event_intermediate.listJunctions();
1332  event_hadronize.list();
1333  event_hadronize.listJunctions();
1334  if (col_to_find != 0) {
1335  log.error("No parton with col = ", col_to_find);
1336  }
1337  if (acol_to_find != 0) {
1338  log.error("No parton with acol = ", acol_to_find);
1339  }
1340  throw std::runtime_error("Hard string could not be identified.");
1341  } else {
1342  pSum += event_intermediate[ifound].p();
1343  // add a parton to the new event record.
1344  event_hadronize.append(event_intermediate[ifound]);
1345  // then remove from the original event record.
1346  event_intermediate.remove(ifound, ifound);
1347  log.debug("Hard non-diff: event_intermediate reduces in size to ",
1348  event_intermediate.size());
1349  }
1350  }
1351 
1352  /* The zeroth entry of event record is supposed to have the information
1353  * on the whole system. Specify the total momentum and invariant mass. */
1354  event_hadronize[0].p(pSum);
1355  event_hadronize[0].m(pSum.mCalc());
1356 }
1357 
1358 void StringProcess::compose_string_junction(bool &find_forward_string,
1359  Pythia8::Event &event_intermediate,
1360  Pythia8::Event &event_hadronize) {
1361  const auto &log = logger<LogArea::Pythia>();
1362 
1363  event_hadronize.reset();
1364 
1365  /* Move the first junction to the event record for hadronization
1366  * and specify color or anti-color indices to be found.
1367  * If junction kind is an odd number, it connects three quarks
1368  * to make a color-neutral baryonic configuration.
1369  * Otherwise, it connects three antiquarks
1370  * to make a color-neutral anti-baryonic configuration. */
1371  const int kind = event_intermediate.kindJunction(0);
1372  bool sign_color = kind % 2 == 1;
1373  std::vector<int> col; // color or anti-color indices of the junction legs
1374  for (int j = 0; j < 3; j++) {
1375  col.push_back(event_intermediate.colJunction(0, j));
1376  }
1377  event_hadronize.appendJunction(kind, col[0], col[1], col[2]);
1378  event_intermediate.eraseJunction(0);
1379  log.debug("junction (", col[0], ", ", col[1], ", ", col[2], ") with kind ",
1380  kind, " will be handled.");
1381 
1382  bool found_string = false;
1383  while (!found_string) {
1384  // trace color or anti-color indices and find corresponding partons.
1385  find_junction_leg(sign_color, col, event_intermediate, event_hadronize);
1386  found_string = true;
1387  for (unsigned int j = 0; j < col.size(); j++) {
1388  found_string = found_string && col[j] == 0;
1389  }
1390  if (!found_string) {
1391  /* if there is any leg which is not closed with parton,
1392  * look over junctions and find connected ones. */
1393  log.debug(" still has leg(s) unfinished.");
1394  sign_color = !sign_color;
1395  std::vector<int> junction_to_move;
1396  for (int i = 0; i < event_intermediate.sizeJunction(); i++) {
1397  const int kind_new = event_intermediate.kindJunction(i);
1398  /* If the original junction is associated with positive baryon number,
1399  * it looks for anti-junctions whose legs are connected with
1400  * anti-quarks (anti-colors in general). */
1401  if (sign_color != (kind_new % 2 == 1)) {
1402  continue;
1403  }
1404 
1405  std::array<int, 3> col_new;
1406  for (int k = 0; k < 3; k++) {
1407  col_new[k] = event_intermediate.colJunction(i, k);
1408  }
1409 
1410  int n_legs_connected = 0;
1411  // loop over remaining legs
1412  for (unsigned int j = 0; j < col.size(); j++) {
1413  if (col[j] == 0) {
1414  continue;
1415  }
1416  for (int k = 0; k < 3; k++) {
1417  if (col[j] == col_new[k]) {
1418  n_legs_connected += 1;
1419  col[j] = 0;
1420  col_new[k] = 0;
1421  }
1422  }
1423  }
1424 
1425  // specify which junction is connected to the original one.
1426  if (n_legs_connected > 0) {
1427  for (int k = 0; k < 3; k++) {
1428  if (col_new[k] != 0) {
1429  col.push_back(col_new[k]);
1430  }
1431  }
1432  log.debug(" junction ", i, " (",
1433  event_intermediate.colJunction(i, 0), ", ",
1434  event_intermediate.colJunction(i, 1), ", ",
1435  event_intermediate.colJunction(i, 2), ") with kind ",
1436  kind_new, " will be added.");
1437  junction_to_move.push_back(i);
1438  }
1439  }
1440 
1441  /* If there is any connected junction,
1442  * move it to the event record for hadronization. */
1443  for (unsigned int i = 0; i < junction_to_move.size(); i++) {
1444  unsigned int imove = junction_to_move[i] - i;
1445  const int kind_add = event_intermediate.kindJunction(imove);
1446  std::array<int, 3> col_add;
1447  for (int k = 0; k < 3; k++) {
1448  col_add[k] = event_intermediate.colJunction(imove, k);
1449  }
1450  // add a junction to the new event record.
1451  event_hadronize.appendJunction(kind_add, col_add[0], col_add[1],
1452  col_add[2]);
1453  // then remove from the original event record.
1454  event_intermediate.eraseJunction(imove);
1455  }
1456  }
1457  }
1458 
1459  Pythia8::Vec4 pSum = event_hadronize[0].p();
1460  find_forward_string = pSum.pz() > 0.;
1461 }
1462 
1463 void StringProcess::find_junction_leg(bool sign_color, std::vector<int> &col,
1464  Pythia8::Event &event_intermediate,
1465  Pythia8::Event &event_hadronize) {
1466  const auto &log = logger<LogArea::Pythia>();
1467 
1468  Pythia8::Vec4 pSum = event_hadronize[0].p();
1469  for (unsigned int j = 0; j < col.size(); j++) {
1470  if (col[j] == 0) {
1471  continue;
1472  }
1473  bool found_leg = false;
1474  while (!found_leg) {
1475  int ifound = -1;
1476  for (int i = 1; i < event_intermediate.size(); i++) {
1477  const int pdgid = event_intermediate[i].id();
1478  if (sign_color && col[j] == event_intermediate[i].col()) {
1479  log.debug(" col[", j, "] = ", col[j], " from i ", i, "(", pdgid,
1480  ") found");
1481  ifound = i;
1482  col[j] = event_intermediate[i].acol();
1483  break;
1484  } else if (!sign_color && col[j] == event_intermediate[i].acol()) {
1485  log.debug(" acol[", j, "] = ", col[j], " from i ", i, "(", pdgid,
1486  ") found");
1487  ifound = i;
1488  col[j] = event_intermediate[i].col();
1489  break;
1490  }
1491  }
1492 
1493  if (ifound < 0) {
1494  found_leg = true;
1495  if (event_intermediate.sizeJunction() == 0) {
1496  event_intermediate.list();
1497  event_intermediate.listJunctions();
1498  event_hadronize.list();
1499  event_hadronize.listJunctions();
1500  log.error("No parton with col = ", col[j],
1501  " connected with junction leg ", j);
1502  throw std::runtime_error("Hard string could not be identified.");
1503  }
1504  } else {
1505  pSum += event_intermediate[ifound].p();
1506  // add a parton to the new event record.
1507  event_hadronize.append(event_intermediate[ifound]);
1508  // then remove from the original event record.
1509  event_intermediate.remove(ifound, ifound);
1510  log.debug("Hard non-diff: event_intermediate reduces in size to ",
1511  event_intermediate.size());
1512  if (col[j] == 0) {
1513  found_leg = true;
1514  }
1515  }
1516  }
1517  }
1518 
1519  /* The zeroth entry of event record is supposed to have the information
1520  * on the whole system. Specify the total momentum and invariant mass. */
1521  event_hadronize[0].p(pSum);
1522  event_hadronize[0].m(pSum.mCalc());
1523 }
1524 
1525 // baryon-antibaryon annihilation
1527  const auto &log = logger<LogArea::Pythia>();
1528  const std::array<FourVector, 2> ustrcom = {FourVector(1., 0., 0., 0.),
1529  FourVector(1., 0., 0., 0.)};
1530 
1531  NpartFinal_ = 0;
1532  NpartString_[0] = 0;
1533  NpartString_[1] = 0;
1534  final_state_.clear();
1535 
1536  log.debug("Annihilation occurs between ", PDGcodes_[0], "+", PDGcodes_[1],
1537  " at CM energy [GeV] ", sqrtsAB_);
1538 
1539  // check if the initial state is baryon-antibaryon pair.
1540  PdgCode baryon = PDGcodes_[0], antibaryon = PDGcodes_[1];
1541  if (baryon.baryon_number() == -1) {
1542  std::swap(baryon, antibaryon);
1543  }
1544  if (baryon.baryon_number() != 1 || antibaryon.baryon_number() != -1) {
1545  throw std::invalid_argument("Expected baryon-antibaryon pair.");
1546  }
1547 
1548  // Count how many qqbar combinations are possible for each quark type
1549  constexpr int n_q_types = 5; // u, d, s, c, b
1550  std::vector<int> qcount_bar, qcount_antibar;
1551  std::vector<int> n_combinations;
1552  bool no_combinations = true;
1553  for (int i = 0; i < n_q_types; i++) {
1554  qcount_bar.push_back(baryon.net_quark_number(i + 1));
1555  qcount_antibar.push_back(-antibaryon.net_quark_number(i + 1));
1556  const int n_i = qcount_bar[i] * qcount_antibar[i];
1557  n_combinations.push_back(n_i);
1558  if (n_i > 0) {
1559  no_combinations = false;
1560  }
1561  }
1562 
1563  /* if it is a BBbar pair but there is no qqbar pair to annihilate,
1564  * nothing happens */
1565  if (no_combinations) {
1566  for (int i = 0; i < 2; i++) {
1567  NpartString_[i] = 1;
1568  ParticleData new_particle(ParticleType::find(PDGcodes_[i]));
1569  new_particle.set_4momentum(pcom_[i]);
1570  new_particle.set_cross_section_scaling_factor(1.);
1571  new_particle.set_formation_time(time_collision_);
1572  final_state_.push_back(new_particle);
1573  }
1575  return true;
1576  }
1577 
1578  // Select qqbar pair to annihilate and remove it away
1579  auto discrete_distr = random::discrete_dist<int>(n_combinations);
1580  const int q_annihilate = discrete_distr() + 1;
1581  qcount_bar[q_annihilate - 1]--;
1582  qcount_antibar[q_annihilate - 1]--;
1583 
1584  // Get the remaining quarks and antiquarks
1585  std::vector<int> remaining_quarks, remaining_antiquarks;
1586  for (int i = 0; i < n_q_types; i++) {
1587  for (int j = 0; j < qcount_bar[i]; j++) {
1588  remaining_quarks.push_back(i + 1);
1589  }
1590  for (int j = 0; j < qcount_antibar[i]; j++) {
1591  remaining_antiquarks.push_back(-(i + 1));
1592  }
1593  }
1594  assert(remaining_quarks.size() == 2);
1595  assert(remaining_antiquarks.size() == 2);
1596 
1597  const std::array<double, 2> mstr = {0.5 * sqrtsAB_, 0.5 * sqrtsAB_};
1598 
1599  // randomly select two quark-antiquark pairs
1600  if (random::uniform_int(0, 1) == 0) {
1601  std::swap(remaining_quarks[0], remaining_quarks[1]);
1602  }
1603  if (random::uniform_int(0, 1) == 0) {
1604  std::swap(remaining_antiquarks[0], remaining_antiquarks[1]);
1605  }
1606  // Make sure it satisfies kinematical threshold constraint
1607  bool kin_threshold_satisfied = true;
1608  for (int i = 0; i < 2; i++) {
1609  const double mstr_min =
1610  pythia_hadron_->particleData.m0(remaining_quarks[i]) +
1611  pythia_hadron_->particleData.m0(remaining_antiquarks[i]);
1612  if (mstr_min > mstr[i]) {
1613  kin_threshold_satisfied = false;
1614  }
1615  }
1616  if (!kin_threshold_satisfied) {
1617  return false;
1618  }
1619  // Fragment two strings
1620  for (int i = 0; i < 2; i++) {
1621  ParticleList new_intermediate_particles;
1622 
1623  ThreeVector evec = pcom_[i].threevec() / pcom_[i].threevec().abs();
1624  const int nfrag =
1625  fragment_string(remaining_quarks[i], remaining_antiquarks[i], mstr[i],
1626  evec, true, false, new_intermediate_particles);
1627  if (nfrag <= 0) {
1628  NpartString_[i] = 0;
1629  return false;
1630  }
1631  NpartString_[i] =
1632  append_final_state(new_intermediate_particles, ustrcom[i], evec);
1633  }
1635  return true;
1636 }
1637 
1639  ThreeVector &evec_polar, std::array<ThreeVector, 3> &evec_basis) {
1640  if (std::abs(evec_polar.x3()) < (1. - 1.0e-8)) {
1641  double ex, ey, et;
1642  double theta, phi;
1643 
1644  // evec_basis[0] is set to be longitudinal direction
1645  evec_basis[0] = evec_polar;
1646 
1647  theta = std::acos(evec_basis[0].x3());
1648 
1649  ex = evec_basis[0].x1();
1650  ey = evec_basis[0].x2();
1651  et = std::sqrt(ex * ex + ey * ey);
1652  if (ey > 0.) {
1653  phi = std::acos(ex / et);
1654  } else {
1655  phi = -std::acos(ex / et);
1656  }
1657 
1658  /* The transverse plane is spanned
1659  * by evec_basis[1] and evec_basis[2]. */
1660  evec_basis[1].set_x1(cos(theta) * cos(phi));
1661  evec_basis[1].set_x2(cos(theta) * sin(phi));
1662  evec_basis[1].set_x3(-sin(theta));
1663 
1664  evec_basis[2].set_x1(-sin(phi));
1665  evec_basis[2].set_x2(cos(phi));
1666  evec_basis[2].set_x3(0.);
1667  } else {
1668  // if evec_polar is very close to the z axis
1669  if (evec_polar.x3() > 0.) {
1670  evec_basis[1] = ThreeVector(1., 0., 0.);
1671  evec_basis[2] = ThreeVector(0., 1., 0.);
1672  evec_basis[0] = ThreeVector(0., 0., 1.);
1673  } else {
1674  evec_basis[1] = ThreeVector(0., 1., 0.);
1675  evec_basis[2] = ThreeVector(1., 0., 0.);
1676  evec_basis[0] = ThreeVector(0., 0., -1.);
1677  }
1678  }
1679 }
1680 
1682  PPosA_ = (pcom_[0].x0() + evecBasisAB_[0] * pcom_[0].threevec()) / sqrt2_;
1683  PNegA_ = (pcom_[0].x0() - evecBasisAB_[0] * pcom_[0].threevec()) / sqrt2_;
1684  PPosB_ = (pcom_[1].x0() + evecBasisAB_[0] * pcom_[1].threevec()) / sqrt2_;
1685  PNegB_ = (pcom_[1].x0() - evecBasisAB_[0] * pcom_[1].threevec()) / sqrt2_;
1686 }
1687 
1688 void StringProcess::quarks_from_diquark(int diquark, int &q1, int &q2,
1689  int &deg_spin) {
1690  // The 4-digit pdg id should be diquark.
1691  assert((std::abs(diquark) > 1000) && (std::abs(diquark) < 5510) &&
1692  (std::abs(diquark) % 100 < 10));
1693 
1694  // The fourth digit corresponds to the spin degeneracy.
1695  deg_spin = std::abs(diquark) % 10;
1696  // Diquark (anti-diquark) is decomposed into two quarks (antiquarks).
1697  const int sign_anti = diquark > 0 ? 1 : -1;
1698 
1699  // Obtain two quarks (or antiquarks) from the first and second digit.
1700  q1 = sign_anti * (std::abs(diquark) - (std::abs(diquark) % 1000)) / 1000;
1701  q2 = sign_anti * (std::abs(diquark) % 1000 - deg_spin) / 100;
1702 }
1703 
1705  assert((q1 > 0 && q2 > 0) || (q1 < 0 && q2 < 0));
1706  if (std::abs(q1) < std::abs(q2)) {
1707  std::swap(q1, q2);
1708  }
1709  int diquark = std::abs(q1 * 1000 + q2 * 100);
1710  /* Adding spin degeneracy = 2S+1. For identical quarks spin cannot be 0
1711  * because of Pauli exclusion principle, so spin 1 is assumed. Otherwise
1712  * S = 0 with probability 1/4 and S = 1 with probability 3/4. */
1713  diquark += (q1 != q2 && random::uniform_int(0, 3) == 0) ? 1 : 3;
1714  return (q1 < 0) ? -diquark : diquark;
1715 }
1716 
1717 void StringProcess::make_string_ends(const PdgCode &pdg, int &idq1, int &idq2,
1718  double xi) {
1719  std::array<int, 3> quarks = pdg.quark_content();
1720  if (pdg.is_nucleon()) {
1721  // protons and neutrons treated seperately since single quarks is at a
1722  // different position in the PDG code
1723  if (pdg.charge() == 0) { // (anti)neutron
1724  if (random::uniform(0., 1.) < xi) {
1725  idq1 = quarks[0];
1726  idq2 = diquark_from_quarks(quarks[1], quarks[2]);
1727  } else {
1728  idq1 = quarks[1];
1729  idq2 = diquark_from_quarks(quarks[0], quarks[2]);
1730  }
1731  } else { // (anti)proton
1732  if (random::uniform(0., 1.) < xi) {
1733  idq1 = quarks[2];
1734  idq2 = diquark_from_quarks(quarks[0], quarks[1]);
1735  } else {
1736  idq1 = quarks[0];
1737  idq2 = diquark_from_quarks(quarks[1], quarks[2]);
1738  }
1739  }
1740  } else {
1741  if (pdg.is_meson()) {
1742  idq1 = quarks[1];
1743  idq2 = quarks[2];
1744  /* Some mesons with PDG id 11X are actually mixed state of uubar and
1745  * ddbar. have a random selection whether we have uubar or ddbar in this
1746  * case. */
1747  if (idq1 == 1 && idq2 == -1 && random::uniform_int(0, 1) == 0) {
1748  idq1 = 2;
1749  idq2 = -2;
1750  }
1751  } else {
1752  assert(pdg.is_baryon());
1753  // Get random quark to position 0
1754  std::swap(quarks[random::uniform_int(0, 2)], quarks[0]);
1755  idq1 = quarks[0];
1756  idq2 = diquark_from_quarks(quarks[1], quarks[2]);
1757  }
1758  }
1759  // Fulfil the convention: idq1 should be quark or anti-diquark
1760  if (idq1 < 0) {
1761  std::swap(idq1, idq2);
1762  }
1763 }
1764 
1765 int StringProcess::fragment_string(int idq1, int idq2, double mString,
1766  ThreeVector &evecLong, bool flip_string_ends,
1767  bool separate_fragment_baryon,
1768  ParticleList &intermediate_particles) {
1769  const auto &log = logger<LogArea::Pythia>();
1770  pythia_hadron_->event.reset();
1771  intermediate_particles.clear();
1772 
1773  log.debug("initial quark content for fragment_string : ", idq1, ", ", idq2);
1774  // PDG id of quark constituents of string ends
1775  std::array<int, 2> idqIn;
1776  idqIn[0] = idq1;
1777  idqIn[1] = idq2;
1778 
1779  int bstring = 0;
1780  // constituent masses of the string
1781  std::array<double, 2> m_const;
1782 
1783  for (int i = 0; i < 2; i++) {
1784  // evaluate total baryon number of the string times 3
1785  bstring += pythia_hadron_->particleData.baryonNumberType(idqIn[i]);
1786 
1787  m_const[i] = pythia_hadron_->particleData.m0(idqIn[i]);
1788  }
1789  log.debug("baryon number of string times 3 : ", bstring);
1790 
1791  if (flip_string_ends && random::uniform_int(0, 1) == 0) {
1792  /* in the case where we flip the string ends,
1793  * we need to flip the longitudinal unit vector itself
1794  * since it is set to be direction of diquark (anti-quark)
1795  * or anti-diquark. */
1796  evecLong = -evecLong;
1797  }
1798 
1799  if (m_const[0] + m_const[1] > mString) {
1800  throw std::runtime_error("String fragmentation: m1 + m2 > mString");
1801  }
1802  Pythia8::Vec4 pSum = 0.;
1803 
1804  int number_of_fragments = 0;
1805 
1806  if (separate_fragment_baryon && (std::abs(bstring) == 3) &&
1807  (mString > m_const[0] + m_const[1] + 1.)) {
1808  /* In the case of separate fragmentation function for leading baryons,
1809  * the leading baryon (antibaryon) is fragmented from the original string
1810  * with a gaussian fragmentation function.
1811  * It is then followed by fragmentation of the remaining mesonic string. */
1812  const double ssbar_supp = pythia_hadron_->parm("StringFlav:probStoUD");
1813  const double sigma_qt_frag = pythia_hadron_->parm("StringPT:sigma");
1814  std::array<ThreeVector, 3> evec_basis;
1815  make_orthonormal_basis(evecLong, evec_basis);
1816 
1817  /* transverse momentum (and magnitude) acquired by the qqbar pair
1818  * created in the middle of string */
1819  double QTrx, QTry, QTrn;
1820  /* lightcone momenta p^+ and p^- of the remaining (mesonic) string
1821  * p^{\pm} is defined as (E \pm p_{longitudinal}) / sqrts{2}. */
1822  double ppos_string_new, pneg_string_new;
1823  // transverse mass of the remaining (mesonic) string
1824  double mTrn_string_new;
1825  // transverse masses of quark constituents of string ends
1826  std::array<double, 2> m_trans;
1827 
1828  const int niter_max = 10000;
1829  int iiter = 0;
1830  bool found_leading_baryon = false;
1831  while (!found_leading_baryon) {
1832  int idnew_qqbar = 0;
1833  /* Determine flavor of the new qqbar pair
1834  * based on the strangeness suppression factor specifed in config. */
1835  if (random::uniform(0., 1. + ssbar_supp) < 1.) {
1836  if (random::uniform_int(0, 1) == 0) {
1837  idnew_qqbar = 1; // ddbar pair
1838  } else {
1839  idnew_qqbar = 2; // uubar pair
1840  }
1841  } else {
1842  idnew_qqbar = 3; // ssbar pair
1843  }
1844 
1845  int id_diquark = 0;
1846  if (bstring > 0) {
1847  /* q(idq1) + diquark(idq2) baryonic string fragments into
1848  * q(idq1) + qbar(-idnew_qqbar) mesonic string and
1849  * q(idnew_qqbar) + diquark(idq2) baryon. */
1850  id_diquark = std::abs(idq2);
1851  idqIn[1] = -idnew_qqbar;
1852  } else {
1853  /* anti-diquark(idq1) + qbar(idq2) anti-baryonic string fragments into
1854  * q(idnew_qqbar) + qbar(idq2) mesonic string and
1855  * anti-diquark(idq1) + qbar(-idnew_qqbar) antibaryon. */
1856  id_diquark = std::abs(idq1);
1857  idqIn[0] = idnew_qqbar;
1858  }
1859  log.debug("quark constituents for leading baryon : ", idnew_qqbar, ", ",
1860  id_diquark);
1861 
1862  // net quark number of d, u, s, c and b flavors
1863  std::array<int, 5> frag_net_q;
1864  /* Evaluate total net quark number of baryon (antibaryon)
1865  * from the valence quark constituents. */
1866  for (int iq = 0; iq < 5; iq++) {
1867  frag_net_q[iq] =
1868  (bstring > 0 ? 1 : -1) *
1869  (pythia_hadron_->particleData.nQuarksInCode(idnew_qqbar, iq + 1) +
1870  pythia_hadron_->particleData.nQuarksInCode(id_diquark, iq + 1));
1871  }
1872  const int frag_iso3 = frag_net_q[1] - frag_net_q[0];
1873  const int frag_strange = -frag_net_q[2];
1874  const int frag_charm = frag_net_q[3];
1875  const int frag_bottom = -frag_net_q[4];
1876  log.debug(" conserved charges of leading baryon : iso3 = ", frag_iso3,
1877  ", strangeness = ", frag_strange, ", charmness = ", frag_charm,
1878  ", bottomness = ", frag_bottom);
1879 
1880  std::vector<int> pdgid_possible;
1881  std::vector<double> weight_possible;
1882  std::vector<double> weight_summed;
1883  /* loop over hadronic species
1884  * Any hadron with the same valence quark contents is allowed and
1885  * the probability goes like spin degeneracy over mass. */
1886  for (auto &ptype : ParticleType::list_all()) {
1887  const int pdgid =
1888  (bstring > 0 ? 1 : -1) * std::abs(ptype.pdgcode().get_decimal());
1889  if ((pythia_hadron_->particleData.isParticle(pdgid)) &&
1890  (bstring == 3 * ptype.pdgcode().baryon_number()) &&
1891  (frag_iso3 == ptype.pdgcode().isospin3()) &&
1892  (frag_strange == ptype.pdgcode().strangeness()) &&
1893  (frag_charm == ptype.pdgcode().charmness()) &&
1894  (frag_bottom == ptype.pdgcode().bottomness())) {
1895  const int spin_degeneracy = ptype.pdgcode().spin_degeneracy();
1896  const double mass_pole = ptype.mass();
1897  const double weight =
1898  static_cast<double>(spin_degeneracy) / mass_pole;
1899  pdgid_possible.push_back(pdgid);
1900  weight_possible.push_back(weight);
1901 
1902  log.debug(" PDG id ", pdgid, " is possible with weight ", weight);
1903  }
1904  }
1905  const int n_possible = pdgid_possible.size();
1906  weight_summed.push_back(0.);
1907  for (int i = 0; i < n_possible; i++) {
1908  weight_summed.push_back(weight_summed[i] + weight_possible[i]);
1909  }
1910 
1911  /* Sample baryon (antibaryon) specie,
1912  * which is fragmented from the leading diquark (anti-diquark). */
1913  int pdgid_frag = 0;
1914  const double uspc = random::uniform(0., weight_summed[n_possible]);
1915  for (int i = 0; i < n_possible; i++) {
1916  if ((uspc >= weight_summed[i]) && (uspc < weight_summed[i + 1])) {
1917  pdgid_frag = pdgid_possible[i];
1918  log.debug("PDG id ", pdgid_frag, " selected for leading baryon.");
1919  break;
1920  }
1921  }
1922  // Sample mass.
1923  const double mass_frag = pythia_hadron_->particleData.mSel(pdgid_frag);
1924 
1925  // Sample transverse momentum carried by baryon (antibaryon).
1926  QTrx = random::normal(0., sigma_qt_frag / sqrt2_);
1927  QTry = random::normal(0., sigma_qt_frag / sqrt2_);
1928  log.debug("Transverse momentum (", QTrx, ", ", QTry,
1929  ") GeV selected for the leading baryon.");
1930  QTrn = std::sqrt(QTrx * QTrx + QTry * QTry);
1931  // transverse mass of the fragmented leading hadron
1932  const double mTrn_frag = std::sqrt(QTrn * QTrn + mass_frag * mass_frag);
1933 
1934  /* Sample lightcone momentum fraction carried by the baryon (antibaryon).
1935  * The corresponding fragmentation function has gaussian shape
1936  * i.e., exp( - (x - leading_frag_mean_)^2 / (2 leading_frag_width_^2) ).
1937  * This is done using the rejection method with an Lorentzian
1938  * envelop function, which is
1939  * 1 / (1 + (x - leading_frag_mean_)^2 / (4 leading_frag_width_^2) ). */
1940  double xfrac = 0.;
1941  bool xfrac_accepted = false;
1942  while (!xfrac_accepted) {
1943  const double angle =
1944  random::uniform(0., 1.) *
1945  (std::atan(0.5 * (1. - leading_frag_mean_) /
1947  std::atan(0.5 * leading_frag_mean_ / leading_frag_width_)) -
1948  std::atan(0.5 * leading_frag_mean_ / leading_frag_width_);
1949  // lightcone momentum fraction sampled from the envelop function
1950  xfrac = leading_frag_mean_ + 2. * leading_frag_width_ * std::tan(angle);
1951 
1952  /* The rejection method is applied to sample
1953  * according to the real fragmentation function. */
1954  const double xf_tmp =
1955  std::abs(xfrac - leading_frag_mean_) / leading_frag_width_;
1956  const double xf_env = (1. + really_small) / (1. + xf_tmp * xf_tmp / 4.);
1957  const double xf_pdf = std::exp(-xf_tmp * xf_tmp / 2.);
1958  if (random::uniform(0., xf_env) < xf_pdf && xfrac > 0. && xfrac < 1.) {
1959  xfrac_accepted = true;
1960  }
1961  }
1962 
1963  /* Determine kinematics of the fragmented baryon (antibaryon) and
1964  * remaining (mesonic) string.
1965  * It begins with the lightcone momentum, p^+ (ppos) and p^- (pneg). */
1966  const double ppos_frag = xfrac * mString / sqrt2_;
1967  const double pneg_frag = 0.5 * mTrn_frag * mTrn_frag / ppos_frag;
1968  ppos_string_new = mString / sqrt2_ - ppos_frag;
1969  pneg_string_new = mString / sqrt2_ - pneg_frag;
1970  mTrn_string_new =
1971  std::sqrt(std::max(0., 2. * ppos_string_new * pneg_string_new));
1972 
1973  /* Quark (antiquark) constituents of the remaining string are
1974  * different from those of the original string.
1975  * Therefore, the constituent masses have to be updated. */
1976  for (int i = 0; i < 2; i++) {
1977  m_const[i] = pythia_hadron_->particleData.m0(idqIn[i]);
1978  }
1979  if (bstring > 0) { // in the case of baryonic string
1980  /* Quark is coming from the original string
1981  * and therefore has zero transverse momentum. */
1982  m_trans[0] = m_const[0];
1983  /* Antiquark is coming from the newly produced qqbar pair
1984  * and therefore has transverse momentum, which is opposite to
1985  * that of the fragmented (leading) antibaryon. */
1986  m_trans[1] = std::sqrt(m_const[1] * m_const[1] + QTrn * QTrn);
1987  } else { // in the case of anti-baryonic string
1988  /* Quark is coming from the newly produced qqbar pair
1989  * and therefore has transverse momentum, which is opposite to
1990  * that of the fragmented (leading) baryon. */
1991  m_trans[0] = std::sqrt(m_const[0] * m_const[0] + QTrn * QTrn);
1992  /* Antiquark is coming from the original string
1993  * and therefore has zero transverse momentum. */
1994  m_trans[1] = m_const[1];
1995  }
1996 
1997  if (mTrn_string_new > m_trans[0] + m_trans[1]) {
1998  found_leading_baryon = true;
1999 
2000  FourVector mom_frag((ppos_frag + pneg_frag) / sqrt2_,
2001  evec_basis[0] * (ppos_frag - pneg_frag) / sqrt2_ +
2002  evec_basis[1] * QTrx + evec_basis[2] * QTry);
2003  log.debug("appending the leading baryon ", pdgid_frag,
2004  " to the intermediate particle list.");
2005  /* If the remaining string has enough transverse mass,
2006  * add fragmented baryon (antibaryon) to the intermediate list. */
2007  bool found_ptype = append_intermediate_list(pdgid_frag, mom_frag,
2008  intermediate_particles);
2009  if (!found_ptype) {
2010  log.error("PDG ID ", pdgid_frag, " should exist in ParticleType.");
2011  throw std::runtime_error("string fragmentation failed.");
2012  }
2013  number_of_fragments++;
2014  log.debug("proceed to the next step");
2015  }
2016 
2017  if (iiter == niter_max) {
2018  return 0;
2019  }
2020  iiter += 1;
2021  }
2022 
2023  // lightcone momentum p^+ of the quark constituents on the string ends
2024  std::array<double, 2> ppos_parton;
2025  // lightcone momentum p^- of the quark constituents on the string ends
2026  std::array<double, 2> pneg_parton;
2027 
2028  /* lightcone momenta of the string ends (quark and antiquark)
2029  * First, obtain ppos_parton[0] and ppos_parton[1]
2030  * (p^+ of quark and antiquark) by solving the following equations.
2031  * ppos_string_new = ppos_parton[0] + ppos_parton[1]
2032  * pneg_string_new = 0.5 * m_trans[0] * m_trans[0] / ppos_parton[0] +
2033  * 0.5 * m_trans[1] * m_trans[1] / ppos_parton[1] */
2034  const double pb_const =
2035  (mTrn_string_new * mTrn_string_new + m_trans[0] * m_trans[0] -
2036  m_trans[1] * m_trans[1]) /
2037  (4. * pneg_string_new);
2038  const double pc_const =
2039  0.5 * m_trans[0] * m_trans[0] * ppos_string_new / pneg_string_new;
2040  ppos_parton[0] = pb_const + (bstring > 0 ? -1. : 1.) *
2041  std::sqrt(pb_const * pb_const - pc_const);
2042  ppos_parton[1] = ppos_string_new - ppos_parton[0];
2043  /* Then, compute pneg_parton[0] and pneg_parton[1]
2044  * (p^- of quark and antiquark) from the dispersion relation.
2045  * 2 p^+ p^- = m_transverse^2 */
2046  for (int i = 0; i < 2; i++) {
2047  pneg_parton[i] = 0.5 * m_trans[i] * m_trans[i] / ppos_parton[i];
2048  }
2049 
2050  const int status = 1;
2051  int color, anticolor;
2052  ThreeVector three_mom;
2053  Pythia8::Vec4 pquark;
2054 
2055  // quark end of the remaining (mesonic) string
2056  color = 1;
2057  anticolor = 0;
2058  if (bstring > 0) { // in the case of baryonic string
2059  /* Quark is coming from the original string
2060  * and therefore has zero transverse momentum. */
2061  three_mom = evec_basis[0] * (ppos_parton[0] - pneg_parton[0]) / sqrt2_;
2062  } else { // in the case of anti-baryonic string
2063  /* Quark is coming from the newly produced qqbar pair
2064  * and therefore has transverse momentum, which is opposite to
2065  * that of the fragmented (leading) baryon. */
2066  three_mom = evec_basis[0] * (ppos_parton[0] - pneg_parton[0]) / sqrt2_ -
2067  evec_basis[1] * QTrx - evec_basis[2] * QTry;
2068  }
2069  pquark = set_Vec4((ppos_parton[0] + pneg_parton[0]) / sqrt2_, three_mom);
2070  pSum += pquark;
2071  pythia_hadron_->event.append(idqIn[0], status, color, anticolor, pquark,
2072  m_const[0]);
2073 
2074  // antiquark end of the remaining (mesonic) string
2075  color = 0;
2076  anticolor = 1;
2077  if (bstring > 0) { // in the case of baryonic string
2078  /* Antiquark is coming from the newly produced qqbar pair
2079  * and therefore has transverse momentum, which is opposite to
2080  * that of the fragmented (leading) antibaryon. */
2081  three_mom = evec_basis[0] * (ppos_parton[1] - pneg_parton[1]) / sqrt2_ -
2082  evec_basis[1] * QTrx - evec_basis[2] * QTry;
2083  } else { // in the case of anti-baryonic string
2084  /* Antiquark is coming from the original string
2085  * and therefore has zero transverse momentum. */
2086  three_mom = evec_basis[0] * (ppos_parton[1] - pneg_parton[1]) / sqrt2_;
2087  }
2088  pquark = set_Vec4((ppos_parton[1] + pneg_parton[1]) / sqrt2_, three_mom);
2089  pSum += pquark;
2090  pythia_hadron_->event.append(idqIn[1], status, color, anticolor, pquark,
2091  m_const[1]);
2092  } else {
2093  /* diquark (anti-quark) with PDG id idq2 is going in the direction of
2094  * evecLong.
2095  * quark with PDG id idq1 is going in the direction opposite to evecLong. */
2096  double sign_direction = 1.;
2097  if (bstring == -3) { // anti-baryonic string
2098  /* anti-diquark with PDG id idq1 is going in the direction of evecLong.
2099  * anti-quark with PDG id idq2 is going in the direction
2100  * opposite to evecLong. */
2101  sign_direction = -1;
2102  }
2103 
2104  // evaluate momenta of quarks
2105  const double pCMquark = pCM(mString, m_const[0], m_const[1]);
2106  const double E1 = std::sqrt(m_const[0] * m_const[0] + pCMquark * pCMquark);
2107  const double E2 = std::sqrt(m_const[1] * m_const[1] + pCMquark * pCMquark);
2108 
2109  ThreeVector direction = sign_direction * evecLong;
2110 
2111  // For status and (anti)color see \iref{Sjostrand:2007gs}.
2112  const int status1 = 1, color1 = 1, anticolor1 = 0;
2113  Pythia8::Vec4 pquark = set_Vec4(E1, -direction * pCMquark);
2114  pSum += pquark;
2115  pythia_hadron_->event.append(idqIn[0], status1, color1, anticolor1, pquark,
2116  m_const[0]);
2117 
2118  const int status2 = 1, color2 = 0, anticolor2 = 1;
2119  pquark = set_Vec4(E2, direction * pCMquark);
2120  pSum += pquark;
2121  pythia_hadron_->event.append(idqIn[1], status2, color2, anticolor2, pquark,
2122  m_const[1]);
2123  }
2124 
2125  log.debug("fragmenting a string with ", idqIn[0], ", ", idqIn[1]);
2126  // implement PYTHIA fragmentation
2127  pythia_hadron_->event[0].p(pSum);
2128  pythia_hadron_->event[0].m(pSum.mCalc());
2129  const bool successful_hadronization = pythia_hadron_->next();
2130  if (successful_hadronization) {
2131  for (int ipyth = 0; ipyth < pythia_hadron_->event.size(); ipyth++) {
2132  if (!pythia_hadron_->event[ipyth].isFinal()) {
2133  continue;
2134  }
2135  int pythia_id = pythia_hadron_->event[ipyth].id();
2136  /* K_short and K_long need are converted to K0
2137  * since SMASH only knows K0 */
2138  convert_KaonLS(pythia_id);
2139  FourVector momentum(
2140  pythia_hadron_->event[ipyth].e(), pythia_hadron_->event[ipyth].px(),
2141  pythia_hadron_->event[ipyth].py(), pythia_hadron_->event[ipyth].pz());
2142  log.debug("appending the fragmented hadron ", pythia_id,
2143  " to the intermediate particle list.");
2144  bool found_ptype =
2145  append_intermediate_list(pythia_id, momentum, intermediate_particles);
2146  if (!found_ptype) {
2147  log.warn("PDG ID ", pythia_id,
2148  " does not exist in ParticleType - start over.");
2149  intermediate_particles.clear();
2150  return 0;
2151  }
2152 
2153  number_of_fragments++;
2154  }
2155 
2156  return number_of_fragments;
2157  } else {
2158  return 0;
2159  }
2160 }
2161 
2163  double suppression_factor) {
2164  int nbaryon = data.pdgcode().baryon_number();
2165  if (nbaryon == 0) {
2166  // Mesons always get a scaling factor of 1/2 since there is never
2167  // a q-qbar pair at the end of a string so nquark is always 1
2168  data.set_cross_section_scaling_factor(0.5 * suppression_factor);
2169  } else if (data.is_baryon()) {
2170  // Leading baryons get a factor of 2/3 if they carry 2
2171  // and 1/3 if they carry 1 of the strings valence quarks
2172  data.set_cross_section_scaling_factor(suppression_factor * nquark /
2173  (3.0 * nbaryon));
2174  }
2175 }
2176 
2177 std::pair<int, int> StringProcess::find_leading(int nq1, int nq2,
2178  ParticleList &list) {
2179  assert(list.size() >= 2);
2180  int end = list.size() - 1;
2181  int i1, i2;
2182  for (i1 = 0;
2183  i1 <= end && !list[i1].pdgcode().contains_enough_valence_quarks(nq1);
2184  i1++) {
2185  }
2186  for (i2 = end;
2187  i2 >= 0 && !list[i2].pdgcode().contains_enough_valence_quarks(nq2);
2188  i2--) {
2189  }
2190  std::pair<int, int> indices(i1, i2);
2191  return indices;
2192 }
2193 
2195  ParticleList &outgoing_particles,
2196  const ThreeVector &evecLong,
2197  double suppression_factor) {
2198  // Set each particle's cross section scaling factor to 0 first
2199  for (ParticleData &data : outgoing_particles) {
2200  data.set_cross_section_scaling_factor(0.0);
2201  }
2202  // sort outgoing particles according to the longitudinal velocity
2203  std::sort(outgoing_particles.begin(), outgoing_particles.end(),
2204  [&](ParticleData i, ParticleData j) {
2205  return i.momentum().velocity() * evecLong >
2206  j.momentum().velocity() * evecLong;
2207  });
2208  int nq1, nq2; // number of quarks at both ends of the string
2209  switch (baryon_string) {
2210  case 0:
2211  nq1 = -1;
2212  nq2 = 1;
2213  break;
2214  case 1:
2215  nq1 = 2;
2216  nq2 = 1;
2217  break;
2218  case -1:
2219  nq1 = -2;
2220  nq2 = -1;
2221  break;
2222  default:
2223  throw std::runtime_error("string is neither mesonic nor baryonic");
2224  }
2225  // Try to find nq1 on one string end and nq2 on the other string end and the
2226  // other way around. When the leading particles are close to the string ends,
2227  // the quarks are assumed to be distributed this way.
2228  std::pair<int, int> i = find_leading(nq1, nq2, outgoing_particles);
2229  std::pair<int, int> j = find_leading(nq2, nq1, outgoing_particles);
2230  if (baryon_string == 0 && i.second - i.first < j.second - j.first) {
2231  assign_scaling_factor(nq2, outgoing_particles[j.first], suppression_factor);
2232  assign_scaling_factor(nq1, outgoing_particles[j.second],
2233  suppression_factor);
2234  } else {
2235  assign_scaling_factor(nq1, outgoing_particles[i.first], suppression_factor);
2236  assign_scaling_factor(nq2, outgoing_particles[i.second],
2237  suppression_factor);
2238  }
2239 }
2240 
2242  PdgCode pdg_mapped(0x0);
2243 
2244  if (pdg.baryon_number() == 1) { // baryon
2245  pdg_mapped = pdg.charge() > 0 ? PdgCode(pdg::p) : PdgCode(pdg::n);
2246  } else if (pdg.baryon_number() == -1) { // antibaryon
2247  pdg_mapped = pdg.charge() < 0 ? PdgCode(-pdg::p) : PdgCode(-pdg::n);
2248  } else if (pdg.is_hadron()) { // meson
2249  if (pdg.charge() >= 0) {
2250  pdg_mapped = PdgCode(pdg::pi_p);
2251  } else {
2252  pdg_mapped = PdgCode(pdg::pi_m);
2253  }
2254  } else if (pdg.is_lepton()) { // lepton
2255  pdg_mapped = pdg.charge() < 0 ? PdgCode(0x11) : PdgCode(-0x11);
2256  } else {
2257  throw std::runtime_error("StringProcess::pdg_map_for_pythia failed.");
2258  }
2259 
2260  return pdg_mapped.get_decimal();
2261 }
2262 
2263 } // namespace smash
std::array< int, 3 > quark_content() const
The return is always an array of three numbers, which are pdgcodes of quarks: 1 - d...
Definition: pdgcode.h:557
double additional_xsec_supp_
additional cross-section suppression factor to take coherence effect into account.
std::array< int, 2 > NpartString_
number of particles fragmented from strings
Definition: processstring.h:81
bool next_NDiffSoft()
Soft Non-diffractive process is modelled in accordance with dual-topological approach Capella:1978ig...
int net_quark_number(const int quark) const
Returns the net number of quarks with given flavour number For public use, see strangeness(), charmness(), bottomness() and isospin3().
Definition: pdgcode.cc:31
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:30
bool is_lepton() const
Definition: pdgcode.h:302
double PPosA_
forward lightcone momentum p^{+} of incoming particle A in CM-frame [GeV]
Definition: processstring.h:50
static FourVector reorient(Pythia8::Particle &particle, std::array< ThreeVector, 3 > &evec_basis)
compute the four-momentum properly oriented in the lab frame.
T beta_a0(T xmin, T b)
Draws a random number from a beta-distribution with a = 0.
Definition: random.h:348
double x3() const
Definition: threevector.h:163
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:34
static Pythia8::Vec4 set_Vec4(double energy, const ThreeVector &mom)
Easy setter of Pythia Vec4 from SMASH.
double pmin_gluon_lightcone_
the minimum lightcone momentum scale carried by a gluon [GeV]
Definition: processstring.h:83
int get_decimal() const
Definition: pdgcode.h:655
ThreeVector velocity() const
Get the velocity (3-vector divided by zero component).
Definition: fourvector.h:310
bool make_final_state_2strings(const std::array< std::array< int, 2 >, 2 > &quarks, const std::array< FourVector, 2 > &pstr_com, const std::array< double, 2 > &m_str, const std::array< ThreeVector, 2 > &evec_str, bool flip_string_ends, bool separate_fragment_baryon)
Prepare kinematics of two strings, fragment them and append to final_state.
FourVector LorentzBoost(const ThreeVector &v) const
Returns the FourVector boosted with velocity v.
Definition: fourvector.cc:16
int NpartFinal_
total number of final state particles
Definition: processstring.h:79
static void make_string_ends(const PdgCode &pdgcode_in, int &idq1, int &idq2, double xi)
make a random selection to determine partonic contents at the string ends.
int fragment_string(int idq1, int idq2, double mString, ThreeVector &evecLong, bool flip_string_ends, bool separate_fragment_baryon, ParticleList &intermediate_particles)
perform string fragmentation to determine species and momenta of hadrons by implementing PYTHIA 8...
bool is_nucleon() const
Definition: pdgcode.h:324
void replace_constituent(Pythia8::Particle &particle, std::array< int, 5 > &excess_constituent)
Convert a partonic PYTHIA partice into the desired species and update the excess of constituents...
std::unique_ptr< Pythia8::Pythia > pythia_hadron_
PYTHIA object used in fragmentation.
std::array< FourVector, 2 > plab_
momenta of incoming particles in the lab frame [GeV]
Definition: processstring.h:66
int append_final_state(ParticleList &intermediate_particles, const FourVector &uString, const ThreeVector &evecLong)
compute the formation time and fill the arrays with final-state particles as described in Andersson:1...
void common_setup_pythia(Pythia8::Pythia *pythia_in, double strange_supp, double diquark_supp, double stringz_a, double stringz_b, double string_sigma_T)
Common setup of PYTHIA objects for soft and hard string routines.
static int pdg_map_for_pythia(PdgCode &pdg)
Take pdg code and map onto particle specie which can be handled by PYTHIA.
T pCM_sqr(const T sqrts, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:91
std::array< PdgCode, 2 > PDGcodes_
PdgCodes of incoming particles.
Definition: processstring.h:64
void find_total_number_constituent(Pythia8::Event &event_intermediate, std::array< int, 5 > &nquark_total, std::array< int, 5 > &nantiq_total)
Compute how many quarks and antiquarks we have in the system, and update the correspoing arrays with ...
const FourVector & momentum() const
Get the particle&#39;s 4-momentum.
Definition: particledata.h:139
double prob_proton_to_d_uu_
Probability of splitting a nucleon into the quark flavour it has only once and a diquark it has twice...
int charge() const
The charge of the particle.
Definition: pdgcode.h:470
double x0() const
Definition: fourvector.h:290
Pythia8::SigmaTotal pythia_sigmatot_
An object to compute cross-sections.
double abs() const
Definition: threevector.h:251
double PNegB_
backward lightcone momentum p^{-} of incoming particle B in CM-frame [GeV]
Definition: processstring.h:56
std::array< FourVector, 2 > pcom_
momenta of incoming particles in the center of mass frame [GeV]
Definition: processstring.h:68
void set_formation_time(const double &form_time)
Set the absolute formation time.
Definition: particledata.h:232
std::unique_ptr< Pythia8::Pythia > pythia_parton_
PYTHIA object used in hard string routine.
double sqrtsAB_
sqrt of Mandelstam variable s of collision [GeV]
Definition: processstring.h:62
static const ParticleType & find(PdgCode pdgcode)
Returns the ParticleType object for the given pdgcode.
void rearrange_excess(std::array< int, 5 > &nquark_total, std::array< std::array< int, 5 >, 2 > &excess_quark, std::array< std::array< int, 5 >, 2 > &excess_antiq)
Take total number of quarks and check if the system has enough constitents that need to be converted ...
static void quarks_from_diquark(int diquark, int &q1, int &q2, int &deg_spin)
find two quarks from a diquark.
static bool append_intermediate_list(int pdgid, FourVector momentum, ParticleList &intermediate_particles)
append new particle from PYTHIA to a specific particle list
static const ParticleTypeList & list_all()
Definition: particletype.cc:55
void init(const ParticleList &incoming, double tcoll)
initialization feed intial particles, time of collision and gamma factor of the center of mass...
static int diquark_from_quarks(int q1, int q2)
Construct diquark from two quarks.
static void make_orthonormal_basis(ThreeVector &evec_polar, std::array< ThreeVector, 3 > &evec_basis)
compute three orthonormal basis vectors from unit vector in the longitudinal direction ...
bool is_baryon() const
Definition: particledata.h:88
T uniform_int(T min, T max)
Definition: random.h:97
Pythia8::Event event_intermediate_
event record for intermediate partonic state in the hard string routine
double kappa_tension_string_
string tension [GeV/fm]
double pow_fquark_alpha_
parameter for the quark distribution function
Definition: processstring.h:93
static void assign_all_scaling_factors(int baryon_string, ParticleList &outgoing_particles, const ThreeVector &evecLong, double suppression_factor)
Assign a cross section scaling factor to all outgoing particles.
void compute_incoming_lightcone_momenta()
compute the lightcone momenta of incoming particles where the longitudinal direction is set to be sam...
bool next_SDiff(bool is_AB_to_AX)
Single-diffractive process is based on single pomeron exchange described in Ingelman:1984ns.
bool use_yoyo_model_
Whether to calculate the string formation times from the yoyo-model.
static void assign_scaling_factor(int nquark, ParticleData &data, double suppression_factor)
Assign a cross section scaling factor to the given particle.
void compose_string_parton(bool find_forward_string, Pythia8::Event &event_intermediate, Pythia8::Event &event_hadronize)
Identify a set of partons, which are connected to form a color-neutral string, from a given PYTHIA ev...
bool next_DDiff()
Double-diffractive process ( A + B -> X + X ) is similar to the single-diffractive process...
void set_4momentum(const FourVector &momentum_vector)
Set the particle&#39;s 4-momentum directly.
Definition: particledata.h:145
ThreeVector vcomAB_
velocity three vector of the center of mass in the lab frame
Definition: processstring.h:72
ParticleList final_state_
final state array which must be accessed after the collision
bool pythia_parton_initialized_
Remembers if Pythia is initialized or not.
static std::pair< int, int > find_leading(int nq1, int nq2, ParticleList &list)
Find the leading string fragments.
double leading_frag_mean_
mean value of the fragmentation function for the leading baryons
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
Definition: pdgcode.h:108
T uniform(T min, T max)
Definition: random.h:85
double PNegA_
backward lightcone momentum p^{-} of incoming particle A in CM-frame [GeV]
Definition: processstring.h:54
constexpr double kaon_mass
Kaon mass in GeV.
Definition: constants.h:66
double PPosB_
forward lightcone momentum p^{+} of incoming particle B in CM-frame [GeV]
Definition: processstring.h:52
static void convert_KaonLS(int &pythia_id)
convert Kaon-L or Kaon-S into K0 or Anti-K0
Discrete distribution with weight given by probability vector.
Definition: random.h:255
void find_junction_leg(bool sign_color, std::vector< int > &col, Pythia8::Event &event_intermediate, Pythia8::Event &event_hadronize)
Identify partons, which are associated with junction legs, from a given PYTHIA event record...
void compose_string_junction(bool &find_forward_string, Pythia8::Event &event_intermediate, Pythia8::Event &event_hadronize)
Identify a set of partons and junction(s), which are connected to form a color-neutral string...
constexpr int pi_p
π⁺.
bool is_baryon() const
Definition: pdgcode.h:318
FourVector ucomAB_
velocity four vector of the center of mass in the lab frame
Definition: processstring.h:70
double time_collision_
time of collision in the computational frame [fm]
bool set_mass_and_direction_2strings(const std::array< std::array< int, 2 >, 2 > &quarks, const std::array< FourVector, 2 > &pstr_com, std::array< double, 2 > &m_str, std::array< ThreeVector, 2 > &evec_str)
Determine string masses and directions in which strings are stretched.
T power(T n, T xMin, T xMax)
Draws a random number according to a power-law distribution ~ x^n.
Definition: random.h:200
double soft_t_form_
factor to be multiplied to formation times in soft strings
constexpr int p
Proton.
double leading_frag_width_
width of the fragmentation function for the leading baryons
bool restore_constituent(Pythia8::Event &event_intermediate, std::array< std::array< int, 5 >, 2 > &excess_quark, std::array< std::array< int, 5 >, 2 > &excess_antiq)
Take the intermediate partonic state from PYTHIA event with mapped hadrons and convert constituents i...
StringProcess(double string_tension, double time_formation, double gluon_beta, double gluon_pmin, double quark_alpha, double quark_beta, double strange_supp, double diquark_supp, double sigma_perp, double leading_frag_mean, double leading_frag_width, double stringz_a, double stringz_b, double string_sigma_T, double factor_t_form, bool use_yoyo_model, double prob_proton_to_d_uu)
Constructor, initializes pythia.
PdgCode pdgcode() const
Get the pdgcode of the particle.
Definition: particledata.h:81
double pow_fgluon_beta_
parameter for the gluon distribution function
Definition: processstring.h:88
double sqrt2_
square root of 2 ( )
constexpr int maximum_rndm_seed_in_pythia
The maximum value of the random seed used in PYTHIA.
Definition: constants.h:113
std::array< ThreeVector, 3 > evecBasisAB_
Orthonormal basis vectors in the center of mass frame, where the 0th one is parallel to momentum of i...
Definition: processstring.h:77
constexpr int pi_m
π⁻.
int get_index_forward(bool find_forward, int np_end, Pythia8::Event &event)
Obtain index of the most forward or backward particle in a given PYTHIA event record.
double normal(const T &mean, const T &sigma)
Returns a random number drawn from a normal distribution.
Definition: random.h:247
bool next_BBbarAnn()
Baryon-antibaryon annihilation process Based on what UrQMD Bass:1998ca, Bleicher:1999xi does...
constexpr int n
Neutron.
bool is_meson() const
Definition: pdgcode.h:321
double sigma_qperp_
Transverse momentum spread of the excited strings.
void set_cross_section_scaling_factor(const double &xsec_scal)
Set the particle&#39;s initial cross_section_scaling_factor.
Definition: particledata.h:274
T pCM(const T sqrts, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:79
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:32
static void find_excess_constituent(PdgCode &pdg_actual, PdgCode &pdg_mapped, std::array< int, 5 > &excess_quark, std::array< int, 5 > &excess_antiq)
Compare the valence quark contents of the actual and mapped hadrons and evaluate how many more consti...
bool next_NDiffHard()
Hard Non-diffractive process is based on PYTHIA 8 with partonic showers and interactions.
double massB_
mass of incoming particle B [GeV]
Definition: processstring.h:60
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:52
T beta(T a, T b)
Draws a random number from a beta-distribution, where probability density of is .
Definition: random.h:326
double time_formation_const_
constant proper time in the case of constant formation time [fm]
double massA_
mass of incoming particle A [GeV]
Definition: processstring.h:58
int baryon_number() const
Definition: pdgcode.h:308
Definition: action.h:24
bool splitting_gluon_qqbar(Pythia8::Event &event_intermediate, std::array< int, 5 > &nquark_total, std::array< int, 5 > &nantiq_total, bool sign_constituent, std::array< std::array< int, 5 >, 2 > &excess_constituent)
Take total number of quarks and check if the system has enough constituents that need to be converted...
bool is_hadron() const
Definition: pdgcode.h:297
double pow_fquark_beta_
parameter for the quark distribution function
Definition: processstring.h:98