Version: SMASH-3.1
stringprocess.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2017-2020,2022
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/stringprocess.h"
11 
12 #include <array>
13 
14 #include "smash/angles.h"
15 #include "smash/kinematics.h"
16 #include "smash/pow.h"
17 #include "smash/random.h"
18 
19 namespace smash {
20 static constexpr int LOutput = LogArea::Output::id;
21 
23  double string_tension, double time_formation, double gluon_beta,
24  double gluon_pmin, double quark_alpha, double quark_beta,
25  double strange_supp, double diquark_supp, double sigma_perp,
26  double stringz_a_leading, double stringz_b_leading, double stringz_a,
27  double stringz_b, double string_sigma_T, double factor_t_form,
28  bool mass_dependent_formation_times, double prob_proton_to_d_uu,
29  bool separate_fragment_baryon, double popcorn_rate, bool use_monash_tune)
30  : pmin_gluon_lightcone_(gluon_pmin),
31  pow_fgluon_beta_(gluon_beta),
32  pow_fquark_alpha_(quark_alpha),
33  pow_fquark_beta_(quark_beta),
34  sigma_qperp_(sigma_perp),
35  stringz_a_leading_(stringz_a_leading),
36  stringz_b_leading_(stringz_b_leading),
37  stringz_a_produce_(stringz_a),
38  stringz_b_produce_(stringz_b),
39  strange_supp_(strange_supp),
40  diquark_supp_(diquark_supp),
41  popcorn_rate_(popcorn_rate),
42  string_sigma_T_(string_sigma_T),
43  kappa_tension_string_(string_tension),
44  additional_xsec_supp_(0.7),
45  time_formation_const_(time_formation),
46  soft_t_form_(factor_t_form),
47  time_collision_(0.),
48  mass_dependent_formation_times_(mass_dependent_formation_times),
49  prob_proton_to_d_uu_(prob_proton_to_d_uu),
50  separate_fragment_baryon_(separate_fragment_baryon),
51  use_monash_tune_(use_monash_tune) {
52  // setup and initialize pythia for fragmentation
53  pythia_hadron_ = std::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  popcorn_rate, stringz_a, stringz_b, string_sigma_T);
58 
59  /* initialize PYTHIA */
60  pythia_hadron_->init();
61 
62  /*
63  * The const_cast<type>() function is used to obtain the reference of the
64  * PrivateInfo object in the pythia_hadron_.
65  * This cast is needed since Pythia 8.302 which included a major architecture
66  * change. The Info object of a Pythia object is now private, only a const
67  * reference can be obtained.
68  * In order to reference the PrivateInfo object during initialization, we
69  * cast the const reference to obtain the stored address.
70  */
71  pythia_sigmatot_.initInfoPtr(
72  const_cast<Pythia8::Info &>(pythia_hadron_->info));
73  pythia_sigmatot_.init();
74 
75  pythia_stringflav_.initInfoPtr(
76  const_cast<Pythia8::Info &>(pythia_hadron_->info));
77  pythia_stringflav_.init();
78 
79  event_intermediate_.init("intermediate partons",
80  &pythia_hadron_->particleData);
81 
82  for (int imu = 0; imu < 3; imu++) {
83  evecBasisAB_[imu] = ThreeVector(0., 0., 0.);
84  }
85 
86  final_state_.clear();
87 }
88 
89 void StringProcess::common_setup_pythia(Pythia8::Pythia *pythia_in,
90  double strange_supp,
91  double diquark_supp,
92  double popcorn_rate, double stringz_a,
93  double stringz_b,
94  double string_sigma_T) {
95  // choose parametrization for mass-dependent width
96  pythia_in->readString("ParticleData:modeBreitWigner = 4");
97  /* choose minimum transverse momentum scale
98  * involved in partonic interactions */
99  pythia_in->readString("MultipartonInteractions:pTmin = 1.5");
100  pythia_in->readString("MultipartonInteractions:nSample = 10000");
101  // transverse momentum spread in string fragmentation
102  pythia_in->readString("StringPT:sigma = " + std::to_string(string_sigma_T));
103  // diquark suppression factor in string fragmentation
104  pythia_in->readString("StringFlav:probQQtoQ = " +
105  std::to_string(diquark_supp));
106  // strangeness suppression factor in string fragmentation
107  pythia_in->readString("StringFlav:probStoUD = " +
108  std::to_string(strange_supp));
109  pythia_in->readString("StringFlav:popcornRate = " +
110  std::to_string(popcorn_rate));
111  // parameters for the fragmentation function
112  pythia_in->readString("StringZ:aLund = " + std::to_string(stringz_a));
113  pythia_in->readString("StringZ:bLund = " + std::to_string(stringz_b));
114 
115  // manually set the parton distribution function
116  pythia_in->readString("PDF:pSet = 13");
117  pythia_in->readString("PDF:pSetB = 13");
118  pythia_in->readString("PDF:piSet = 1");
119  pythia_in->readString("PDF:piSetB = 1");
120  pythia_in->readString("Beams:idA = 2212");
121  pythia_in->readString("Beams:idB = 2212");
122  pythia_in->readString("Beams:eCM = 10.");
123 
124  // set PYTHIA random seed from outside
125  pythia_in->readString("Random:setSeed = on");
126  // suppress unnecessary output
127  pythia_in->readString("Print:quiet = on");
128  // No resonance decays, since the resonances will be handled by SMASH
129  pythia_in->readString("HadronLevel:Decay = off");
130  // set particle masses and widths in PYTHIA to be same with those in SMASH
131  for (auto &ptype : ParticleType::list_all()) {
132  int pdgid = ptype.pdgcode().get_decimal();
133  double mass_pole = ptype.mass();
134  double width_pole = ptype.width_at_pole();
135  // check if the particle species is in PYTHIA
136  if (pythia_in->particleData.isParticle(pdgid)) {
137  // set mass and width in PYTHIA
138  pythia_in->particleData.m0(pdgid, mass_pole);
139  pythia_in->particleData.mWidth(pdgid, width_pole);
140  } else if (pdgid == 310 || pdgid == 130) {
141  // set mass and width of Kaon-L and Kaon-S
142  pythia_in->particleData.m0(pdgid, kaon_mass);
143  pythia_in->particleData.mWidth(pdgid, 0.);
144  }
145  }
146 
147  // make energy-momentum conservation in PYTHIA more precise
148  pythia_in->readString("Check:epTolErr = 1e-6");
149  pythia_in->readString("Check:epTolWarn = 1e-8");
150  if (use_monash_tune_) {
151  pythia_in->readString("Tune:ee = 7");
152  pythia_in->readString("Tune:pp = 14");
153  }
154 }
155 
156 // compute the formation time and fill the arrays with final-state particles
157 int StringProcess::append_final_state(ParticleList &intermediate_particles,
158  const FourVector &uString,
159  const ThreeVector &evecLong) {
160  int nfrag = 0;
161  int bstring = 0;
162 
163  for (ParticleData &data : intermediate_particles) {
164  nfrag += 1;
165  bstring += data.pdgcode().baryon_number();
166  }
167  assert(nfrag > 0);
168 
169  /* compute the cross section scaling factor for leading hadrons
170  * based on the number of valence quarks. */
171  assign_all_scaling_factors(bstring, intermediate_particles, evecLong,
173 
174  // Velocity three-vector to perform Lorentz boost.
175  const ThreeVector vstring = uString.velocity();
176 
177  // compute the formation times of hadrons
178  for (int i = 0; i < nfrag; i++) {
179  ThreeVector velocity = intermediate_particles[i].momentum().velocity();
180  double gamma = 1. / intermediate_particles[i].inverse_gamma();
181  // boost 4-momentum into the center of mass frame
182  FourVector momentum =
183  intermediate_particles[i].momentum().lorentz_boost(-vstring);
184  intermediate_particles[i].set_4momentum(momentum);
185 
187  // set the formation time and position in the rest frame of string
188  double tau_prod = M_SQRT2 * intermediate_particles[i].effective_mass() /
190  double t_prod = tau_prod * gamma;
191  FourVector fragment_position = FourVector(t_prod, t_prod * velocity);
192  /* boost formation position into the center of mass frame
193  * and then into the lab frame */
194  fragment_position = fragment_position.lorentz_boost(-vstring);
195  fragment_position = fragment_position.lorentz_boost(-vcomAB_);
196  intermediate_particles[i].set_slow_formation_times(
198  soft_t_form_ * fragment_position.x0() + time_collision_);
199  } else {
200  ThreeVector v_calc = momentum.lorentz_boost(-vcomAB_).velocity();
201  double gamma_factor = 1.0 / std::sqrt(1 - (v_calc).sqr());
202  intermediate_particles[i].set_slow_formation_times(
204  time_formation_const_ * gamma_factor + time_collision_);
205  }
206 
207  final_state_.push_back(intermediate_particles[i]);
208  }
209 
210  return nfrag;
211 }
212 
213 void StringProcess::init(const ParticleList &incoming, double tcoll) {
214  PDGcodes_[0] = incoming[0].pdgcode();
215  PDGcodes_[1] = incoming[1].pdgcode();
216  massA_ = incoming[0].effective_mass();
217  massB_ = incoming[1].effective_mass();
218 
219  plab_[0] = incoming[0].momentum();
220  plab_[1] = incoming[1].momentum();
221 
222  // compute sqrts and velocity of the center of mass.
223  sqrtsAB_ = (plab_[0] + plab_[1]).abs();
224  ucomAB_ = (plab_[0] + plab_[1]) / sqrtsAB_;
226 
227  pcom_[0] = plab_[0].lorentz_boost(vcomAB_);
228  pcom_[1] = plab_[1].lorentz_boost(vcomAB_);
229 
230  const double pabscomAB = pCM(sqrtsAB_, massA_, massB_);
231  ThreeVector evec_coll = pcom_[0].threevec() / pabscomAB;
233 
235 
236  time_collision_ = tcoll;
237 }
238 
239 /* single diffractive
240  * is_AB_to_AX = true : A + B -> A + X
241  * is_AB_to_AX = false : A + B -> X + B */
242 bool StringProcess::next_SDiff(bool is_AB_to_AX) {
243  NpartFinal_ = 0;
244  NpartString_[0] = 0;
245  NpartString_[1] = 0;
246  final_state_.clear();
247 
248  double massH = is_AB_to_AX ? massA_ : massB_;
249  double mstrMin = is_AB_to_AX ? massB_ : massA_;
250  double mstrMax = sqrtsAB_ - massH;
251 
252  int idqX1, idqX2;
253  double QTrn, QTrx, QTry;
254  double pabscomHX_sqr, massX;
255 
256  // decompose hadron into quarks
257  make_string_ends(is_AB_to_AX ? PDGcodes_[1] : PDGcodes_[0], idqX1, idqX2,
259  // string mass must be larger than threshold set by PYTHIA.
260  mstrMin = pythia_hadron_->particleData.m0(idqX1) +
261  pythia_hadron_->particleData.m0(idqX2);
262  // this threshold cannot be larger than maximum of allowed string mass.
263  if (mstrMin > mstrMax) {
264  return false;
265  }
266  // sample the transverse momentum transfer
267  QTrx = random::normal(0., sigma_qperp_ * M_SQRT1_2);
268  QTry = random::normal(0., sigma_qperp_ * M_SQRT1_2);
269  QTrn = std::sqrt(QTrx * QTrx + QTry * QTry);
270  /* sample the string mass and
271  * evaluate the three-momenta of hadron and string. */
272  massX = random::power(-1.0, mstrMin, mstrMax);
273  pabscomHX_sqr = pCM_sqr(sqrtsAB_, massH, massX);
274  /* magnitude of the three momentum must be larger
275  * than the transverse momentum. */
276  const bool foundPabsX = pabscomHX_sqr > QTrn * QTrn;
277 
278  if (!foundPabsX) {
279  return false;
280  }
281  double sign_direction = is_AB_to_AX ? 1. : -1.;
282  // determine three momentum of the final state hadron
283  const ThreeVector cm_momentum =
284  sign_direction *
285  (evecBasisAB_[0] * std::sqrt(pabscomHX_sqr - QTrn * QTrn) +
286  evecBasisAB_[1] * QTrx + evecBasisAB_[2] * QTry);
287  const FourVector pstrHcom(std::sqrt(pabscomHX_sqr + massH * massH),
288  cm_momentum);
289  const FourVector pstrXcom(std::sqrt(pabscomHX_sqr + massX * massX),
290  -cm_momentum);
291 
292  const FourVector ustrXcom = pstrXcom / massX;
293  /* determine direction in which the string is stretched.
294  * this is set to be same with the the collision axis
295  * in the center of mass frame. */
296  const ThreeVector threeMomentum =
297  is_AB_to_AX ? pcom_[1].threevec() : pcom_[0].threevec();
298  const FourVector pnull = FourVector(threeMomentum.abs(), threeMomentum);
299  const FourVector prs = pnull.lorentz_boost(ustrXcom.velocity());
300  ThreeVector evec = prs.threevec() / prs.threevec().abs();
301  // perform fragmentation and add particles to final_state.
302  ParticleList new_intermediate_particles;
303  int nfrag = fragment_string(idqX1, idqX2, massX, evec, true, false,
304  new_intermediate_particles);
305  if (nfrag < 1) {
306  NpartString_[0] = 0;
307  return false;
308  }
309  NpartString_[0] =
310  append_final_state(new_intermediate_particles, ustrXcom, evec);
311 
312  NpartString_[1] = 1;
313  PdgCode hadron_code = is_AB_to_AX ? PDGcodes_[0] : PDGcodes_[1];
314  ParticleData new_particle(ParticleType::find(hadron_code));
315  new_particle.set_4momentum(pstrHcom);
316  new_particle.set_cross_section_scaling_factor(1.);
317  new_particle.set_formation_time(time_collision_);
318  final_state_.push_back(new_particle);
319 
321  return true;
322 }
323 
325  const std::array<std::array<int, 2>, 2> &quarks,
326  const std::array<FourVector, 2> &pstr_com, std::array<double, 2> &m_str,
327  std::array<ThreeVector, 2> &evec_str) {
328  std::array<bool, 2> found_mass;
329  for (int i = 0; i < 2; i++) {
330  found_mass[i] = false;
331 
332  m_str[i] = pstr_com[i].sqr();
333  m_str[i] = (m_str[i] > 0.) ? std::sqrt(m_str[i]) : 0.;
334  const double threshold = pythia_hadron_->particleData.m0(quarks[i][0]) +
335  pythia_hadron_->particleData.m0(quarks[i][1]);
336  // string mass must be larger than threshold set by PYTHIA.
337  if (m_str[i] > threshold) {
338  found_mass[i] = true;
339  /* Determine direction in which string i is stretched.
340  * This is set to be same with the collision axis
341  * in the center of mass frame.
342  * Initial state partons inside incoming hadrons are
343  * moving along the collision axis,
344  * which is parallel to three momenta of incoming hadrons
345  * in the center of mass frame.
346  * Given that partons are assumed to be massless,
347  * their four momenta are null vectors and parallel to pnull.
348  * If we take unit three-vector of prs,
349  * which is pnull in the rest frame of string,
350  * it would be the direction in which string ends are moving. */
351  const ThreeVector mom = pcom_[i].threevec();
352  const FourVector pnull(mom.abs(), mom);
353  const FourVector prs = pnull.lorentz_boost(pstr_com[i].velocity());
354  evec_str[i] = prs.threevec() / prs.threevec().abs();
355  }
356  }
357 
358  return found_mass[0] && found_mass[1];
359 }
360 
362  const std::array<std::array<int, 2>, 2> &quarks,
363  const std::array<FourVector, 2> &pstr_com,
364  const std::array<double, 2> &m_str,
365  const std::array<ThreeVector, 2> &evec_str, bool flip_string_ends,
366  bool separate_fragment_baryon) {
367  const std::array<FourVector, 2> ustr_com = {pstr_com[0] / m_str[0],
368  pstr_com[1] / m_str[1]};
369  for (int i = 0; i < 2; i++) {
370  ParticleList new_intermediate_particles;
371 
372  // determine direction in which string i is stretched.
373  ThreeVector evec = evec_str[i];
374  // perform fragmentation and add particles to final_state.
375  int nfrag = fragment_string(quarks[i][0], quarks[i][1], m_str[i], evec,
376  flip_string_ends, separate_fragment_baryon,
377  new_intermediate_particles);
378  if (nfrag <= 0) {
379  NpartString_[i] = 0;
380  return false;
381  }
382  NpartString_[i] =
383  append_final_state(new_intermediate_particles, ustr_com[i], evec);
384  assert(nfrag == NpartString_[i]);
385  }
386  if ((NpartString_[0] > 0) && (NpartString_[1] > 0)) {
388  return true;
389  }
390  return false;
391 }
392 
393 // double-diffractive : A + B -> X + X
395  NpartFinal_ = 0;
396  NpartString_[0] = 0;
397  NpartString_[1] = 0;
398  final_state_.clear();
399 
400  std::array<std::array<int, 2>, 2> quarks;
401  std::array<FourVector, 2> pstr_com;
402  std::array<double, 2> m_str;
403  std::array<ThreeVector, 2> evec_str;
404  ThreeVector threeMomentum;
405 
406  // decompose hadron into quark (and diquark) contents
407  make_string_ends(PDGcodes_[0], quarks[0][0], quarks[0][1],
409  make_string_ends(PDGcodes_[1], quarks[1][0], quarks[1][1],
411  // sample the lightcone momentum fraction carried by gluons
412  const double xmin_gluon_fraction = pmin_gluon_lightcone_ / sqrtsAB_;
413  const double xfracA =
414  random::beta_a0(xmin_gluon_fraction, pow_fgluon_beta_ + 1.);
415  const double xfracB =
416  random::beta_a0(xmin_gluon_fraction, pow_fgluon_beta_ + 1.);
417  // sample the transverse momentum transfer
418  const double QTrx = random::normal(0., sigma_qperp_ * M_SQRT1_2);
419  const double QTry = random::normal(0., sigma_qperp_ * M_SQRT1_2);
420  const double QTrn = std::sqrt(QTrx * QTrx + QTry * QTry);
421  // evaluate the lightcone momentum transfer
422  const double QPos = -QTrn * QTrn / (2. * xfracB * PNegB_);
423  const double QNeg = QTrn * QTrn / (2. * xfracA * PPosA_);
424  // compute four-momentum of string 1
425  threeMomentum =
426  evecBasisAB_[0] * (PPosA_ + QPos - PNegA_ - QNeg) * M_SQRT1_2 +
427  evecBasisAB_[1] * QTrx + evecBasisAB_[2] * QTry;
428  pstr_com[0] =
429  FourVector((PPosA_ + QPos + PNegA_ + QNeg) * M_SQRT1_2, threeMomentum);
430  // compute four-momentum of string 2
431  threeMomentum =
432  evecBasisAB_[0] * (PPosB_ - QPos - PNegB_ + QNeg) * M_SQRT1_2 -
433  evecBasisAB_[1] * QTrx - evecBasisAB_[2] * QTry;
434  pstr_com[1] =
435  FourVector((PPosB_ - QPos + PNegB_ - QNeg) * M_SQRT1_2, threeMomentum);
436 
437  const bool found_masses =
438  set_mass_and_direction_2strings(quarks, pstr_com, m_str, evec_str);
439  if (!found_masses) {
440  return false;
441  }
442  const bool flip_string_ends = true;
443  const bool success = make_final_state_2strings(
444  quarks, pstr_com, m_str, evec_str, flip_string_ends, false);
445  return success;
446 }
447 
448 // soft non-diffractive
450  NpartFinal_ = 0;
451  NpartString_[0] = 0;
452  NpartString_[1] = 0;
453  final_state_.clear();
454 
455  std::array<std::array<int, 2>, 2> quarks;
456  std::array<FourVector, 2> pstr_com;
457  std::array<double, 2> m_str;
458  std::array<ThreeVector, 2> evec_str;
459 
460  // decompose hadron into quark (and diquark) contents
461  int idqA1, idqA2, idqB1, idqB2;
464 
465  const int bar_a = PDGcodes_[0].baryon_number(),
466  bar_b = PDGcodes_[1].baryon_number();
467  if (bar_a == 1 || // baryon-baryon, baryon-meson, baryon-antibaryon
468  (bar_a == 0 && bar_b == 1) || // meson-baryon
469  (bar_a == 0 && bar_b == 0)) { // meson-meson
470  quarks[0][0] = idqB1;
471  quarks[0][1] = idqA2;
472  quarks[1][0] = idqA1;
473  quarks[1][1] = idqB2;
474  } else if (((bar_a == 0) && (bar_b == -1)) || // meson-antibaryon
475  (bar_a == -1)) { // antibaryon-baryon, antibaryon-meson,
476  // antibaryon-antibaryon
477  quarks[0][0] = idqA1;
478  quarks[0][1] = idqB2;
479  quarks[1][0] = idqB1;
480  quarks[1][1] = idqA2;
481  } else {
482  std::stringstream ss;
483  ss << " StringProcess::next_NDiff : baryonA = " << bar_a
484  << ", baryonB = " << bar_b;
485  throw std::runtime_error(ss.str());
486  }
487  // sample the lightcone momentum fraction carried by quarks
488  const double xfracA = random::beta(pow_fquark_alpha_, pow_fquark_beta_);
489  const double xfracB = random::beta(pow_fquark_alpha_, pow_fquark_beta_);
490  // sample the transverse momentum transfer
491  const double QTrx = random::normal(0., sigma_qperp_ * M_SQRT1_2);
492  const double QTry = random::normal(0., sigma_qperp_ * M_SQRT1_2);
493  const double QTrn = std::sqrt(QTrx * QTrx + QTry * QTry);
494  // evaluate the lightcone momentum transfer
495  const double QPos = -QTrn * QTrn / (2. * xfracB * PNegB_);
496  const double QNeg = QTrn * QTrn / (2. * xfracA * PPosA_);
497  const double dPPos = -xfracA * PPosA_ - QPos;
498  const double dPNeg = xfracB * PNegB_ - QNeg;
499  // compute four-momentum of string 1
500  ThreeVector threeMomentum =
501  evecBasisAB_[0] * (PPosA_ + dPPos - PNegA_ - dPNeg) * M_SQRT1_2 +
502  evecBasisAB_[1] * QTrx + evecBasisAB_[2] * QTry;
503  pstr_com[0] =
504  FourVector((PPosA_ + dPPos + PNegA_ + dPNeg) * M_SQRT1_2, threeMomentum);
505  m_str[0] = pstr_com[0].sqr();
506  // compute four-momentum of string 2
507  threeMomentum =
508  evecBasisAB_[0] * (PPosB_ - dPPos - PNegB_ + dPNeg) * M_SQRT1_2 -
509  evecBasisAB_[1] * QTrx - evecBasisAB_[2] * QTry;
510  pstr_com[1] =
511  FourVector((PPosB_ - dPPos + PNegB_ - dPNeg) * M_SQRT1_2, threeMomentum);
512 
513  const bool found_masses =
514  set_mass_and_direction_2strings(quarks, pstr_com, m_str, evec_str);
515  if (!found_masses) {
516  return false;
517  }
518  const bool flip_string_ends = false;
519  const bool success =
520  make_final_state_2strings(quarks, pstr_com, m_str, evec_str,
521  flip_string_ends, separate_fragment_baryon_);
522  return success;
523 }
524 
525 // hard non-diffractive
527  NpartFinal_ = 0;
528  final_state_.clear();
529 
530  logg[LPythia].debug("Hard non-diff. with ", PDGcodes_[0], " + ", PDGcodes_[1],
531  " at CM energy [GeV] ", sqrtsAB_);
532 
533  std::array<int, 2> pdg_for_pythia;
534  std::array<std::array<int, 5>, 2> excess_quark;
535  std::array<std::array<int, 5>, 2> excess_antiq;
536  for (int i = 0; i < 2; i++) {
537  for (int j = 0; j < 5; j++) {
538  excess_quark[i][j] = 0;
539  excess_antiq[i][j] = 0;
540  }
541 
542  // get PDG id used in PYTHIA event generation
543  pdg_for_pythia[i] = pdg_map_for_pythia(PDGcodes_[i]);
544  logg[LPythia].debug(" incoming particle ", i, " : ", PDGcodes_[i],
545  " is mapped onto ", pdg_for_pythia[i]);
546 
547  PdgCode pdgcode_for_pythia(std::to_string(pdg_for_pythia[i]));
548  /* evaluate how many more constituents incoming hadron has
549  * compared to the mapped one. */
550  find_excess_constituent(PDGcodes_[i], pdgcode_for_pythia, excess_quark[i],
551  excess_antiq[i]);
552  logg[LPythia].debug(" excess_quark[", i, "] = (", excess_quark[i][0],
553  ", ", excess_quark[i][1], ", ", excess_quark[i][2],
554  ", ", excess_quark[i][3], ", ", excess_quark[i][4],
555  ")");
556  logg[LPythia].debug(" excess_antiq[", i, "] = (", excess_antiq[i][0],
557  ", ", excess_antiq[i][1], ", ", excess_antiq[i][2],
558  ", ", excess_antiq[i][3], ", ", excess_antiq[i][4],
559  ")");
560  }
561 
562  std::pair<int, int> idAB{pdg_for_pythia[0], pdg_for_pythia[1]};
563 
564  // If an entry for the calculated particle IDs does not exist, create one and
565  // initialize it accordingly
566  if (hard_map_.count(idAB) == 0) {
567  hard_map_[idAB] = std::make_unique<Pythia8::Pythia>(PYTHIA_XML_DIR, false);
568  hard_map_[idAB]->readString("SoftQCD:nonDiffractive = on");
569  hard_map_[idAB]->readString("MultipartonInteractions:pTmin = 1.5");
570  hard_map_[idAB]->readString("HadronLevel:all = off");
571 
575 
576  hard_map_[idAB]->settings.flag("Beams:allowVariableEnergy", true);
577 
578  hard_map_[idAB]->settings.mode("Beams:idA", idAB.first);
579  hard_map_[idAB]->settings.mode("Beams:idB", idAB.second);
580  hard_map_[idAB]->settings.parm("Beams:eCM", sqrtsAB_);
581 
582  logg[LPythia].debug("Pythia object initialized with ", pdg_for_pythia[0],
583  " + ", pdg_for_pythia[1], " at CM energy [GeV] ",
584  sqrtsAB_);
585 
586  if (!hard_map_[idAB]->init()) {
587  throw std::runtime_error("Pythia failed to initialize.");
588  }
589  }
590 
591  const int seed_new = random::uniform_int(1, maximum_rndm_seed_in_pythia);
592  hard_map_[idAB]->rndm.init(seed_new);
593  logg[LPythia].debug("hard_map_[", idAB.first, "][", idAB.second,
594  "] : rndm is initialized with seed ", seed_new);
595 
596  // Change the energy using the Pythia 8.302+ feature
597 
598  // Short notation for Pythia event
599  Pythia8::Event &event_hadron = pythia_hadron_->event;
600  logg[LPythia].debug("Pythia hard event created");
601  // we update the collision energy in the CM frame
602  hard_map_[idAB]->setKinematics(sqrtsAB_);
603  bool final_state_success = hard_map_[idAB]->next();
604  logg[LPythia].debug("Pythia final state computed, success = ",
605  final_state_success);
606  if (!final_state_success) {
607  return false;
608  }
609 
610  ParticleList new_intermediate_particles;
611  ParticleList new_non_hadron_particles;
612 
613  Pythia8::Vec4 pSum = 0.;
614  event_intermediate_.reset();
615  /* Update the partonic intermediate state from PYTHIA output.
616  * Note that hadronization will be performed separately,
617  * after identification of strings and replacement of constituents. */
618  for (int i = 0; i < hard_map_[idAB]->event.size(); i++) {
619  if (hard_map_[idAB]->event[i].isFinal()) {
620  const int pdgid = hard_map_[idAB]->event[i].id();
621  Pythia8::Vec4 pquark = hard_map_[idAB]->event[i].p();
622  const double mass = hard_map_[idAB]->particleData.m0(pdgid);
623 
624  const int status = hard_map_[idAB]->event[i].status();
625  const int color = hard_map_[idAB]->event[i].col();
626  const int anticolor = hard_map_[idAB]->event[i].acol();
627 
628  pSum += pquark;
629  event_intermediate_.append(pdgid, status, color, anticolor, pquark, mass);
630  }
631  }
632  // add junctions to the intermediate state if there is any.
633  event_intermediate_.clearJunctions();
634  for (int i = 0; i < hard_map_[idAB]->event.sizeJunction(); i++) {
635  const int kind = hard_map_[idAB]->event.kindJunction(i);
636  std::array<int, 3> col;
637  for (int j = 0; j < 3; j++) {
638  col[j] = hard_map_[idAB]->event.colJunction(i, j);
639  }
640  event_intermediate_.appendJunction(kind, col[0], col[1], col[2]);
641  }
642  /* The zeroth entry of event record is supposed to have the information
643  * on the whole system. Specify the total momentum and invariant mass. */
644  event_intermediate_[0].p(pSum);
645  event_intermediate_[0].m(pSum.mCalc());
646 
647  /* Replace quark constituents according to the excess of valence quarks
648  * and then rescale momenta of partons by constant factor
649  * to fulfill the energy-momentum conservation. */
650  bool correct_constituents =
651  restore_constituent(event_intermediate_, excess_quark, excess_antiq);
652  if (!correct_constituents) {
653  logg[LPythia].debug("failed to find correct partonic constituents.");
654  return false;
655  }
656 
657  int npart = event_intermediate_.size();
658  int ipart = 0;
659  while (ipart < npart) {
660  const int pdgid = event_intermediate_[ipart].id();
661  if (event_intermediate_[ipart].isFinal() &&
662  !event_intermediate_[ipart].isParton() &&
663  !hard_map_[idAB]->particleData.isOctetHadron(pdgid)) {
664  logg[LPythia].debug("PDG ID from Pythia: ", pdgid);
666  logg[LPythia].debug("4-momentum from Pythia: ", momentum);
667  bool found_ptype =
668  append_intermediate_list(pdgid, momentum, new_non_hadron_particles);
669  if (!found_ptype) {
670  logg[LPythia].warn("PDG ID ", pdgid,
671  " does not exist in ParticleType - start over.");
672  final_state_success = false;
673  }
674  event_intermediate_.remove(ipart, ipart);
675  npart -= 1;
676  } else {
677  ipart += 1;
678  }
679  }
680 
681  bool hadronize_success = false;
682  bool find_forward_string = true;
683  logg[LPythia].debug("Hard non-diff: partonic process gives ",
684  event_intermediate_.size(), " partons.");
685  // identify and fragment strings until there is no parton left.
686  while (event_intermediate_.size() > 1) {
687  // dummy event to initialize the internal variables of PYTHIA.
688  pythia_hadron_->event.reset();
689  if (!pythia_hadron_->next()) {
690  logg[LPythia].debug(" Dummy event in hard string routine failed.");
691  hadronize_success = false;
692  break;
693  }
694 
695  if (event_intermediate_.sizeJunction() > 0) {
696  // identify string from a junction if there is any.
697  compose_string_junction(find_forward_string, event_intermediate_,
698  pythia_hadron_->event);
699  } else {
700  /* identify string from a most forward or backward parton.
701  * if there is no junction. */
702  compose_string_parton(find_forward_string, event_intermediate_,
703  pythia_hadron_->event);
704  }
705 
706  // fragment the (identified) string into hadrons.
707  hadronize_success = pythia_hadron_->forceHadronLevel(false);
708  logg[LPythia].debug("Pythia hadronized, success = ", hadronize_success);
709 
710  new_intermediate_particles.clear();
711  if (hadronize_success) {
712  for (int i = 0; i < event_hadron.size(); i++) {
713  if (event_hadron[i].isFinal()) {
714  int pythia_id = event_hadron[i].id();
715  logg[LPythia].debug("PDG ID from Pythia: ", pythia_id);
716  /* K_short and K_long need to be converted to K0
717  * since SMASH only knows K0 */
718  convert_KaonLS(pythia_id);
719 
720  /* evecBasisAB_[0] is a unit 3-vector in the collision axis,
721  * while evecBasisAB_[1] and evecBasisAB_[2] spans the transverse
722  * plane. Given that PYTHIA assumes z-direction to be the collision
723  * axis, pz from PYTHIA should be the momentum compoment in
724  * evecBasisAB_[0]. px and py are respectively the momentum components
725  * in two transverse directions evecBasisAB_[1] and evecBasisAB_[2].
726  */
727  FourVector momentum = reorient(event_hadron[i], evecBasisAB_);
728  logg[LPythia].debug("4-momentum from Pythia: ", momentum);
729  logg[LPythia].debug("appending the particle ", pythia_id,
730  " to the intermediate particle list.");
731  bool found_ptype = false;
732  if (event_hadron[i].isHadron()) {
733  found_ptype = append_intermediate_list(pythia_id, momentum,
734  new_intermediate_particles);
735  } else {
736  found_ptype = append_intermediate_list(pythia_id, momentum,
737  new_non_hadron_particles);
738  }
739  if (!found_ptype) {
740  logg[LPythia].warn("PDG ID ", pythia_id,
741  " does not exist in ParticleType - start over.");
742  hadronize_success = false;
743  }
744  }
745  }
746  }
747 
748  /* if hadronization is not successful,
749  * reset the event records, return false and then start over. */
750  if (!hadronize_success) {
751  break;
752  }
753 
754  FourVector uString = FourVector(1., 0., 0., 0.);
755  ThreeVector evec = find_forward_string ? evecBasisAB_[0] : -evecBasisAB_[0];
756  int nfrag = append_final_state(new_intermediate_particles, uString, evec);
757  NpartFinal_ += nfrag;
758 
759  find_forward_string = !find_forward_string;
760  }
761 
762  if (hadronize_success) {
763  // add the final state particles, which are not hadron.
764  for (ParticleData data : new_non_hadron_particles) {
765  data.set_cross_section_scaling_factor(1.);
766  data.set_formation_time(time_collision_);
767  final_state_.push_back(data);
768  }
769  } else {
770  final_state_.clear();
771  }
772 
773  return hadronize_success;
774 }
775 
777  PdgCode &pdg_mapped,
778  std::array<int, 5> &excess_quark,
779  std::array<int, 5> &excess_antiq) {
780  /* decompose PDG id of the actual hadron and mapped one
781  * to get the valence quark constituents */
782  std::array<int, 3> qcontent_actual = pdg_actual.quark_content();
783  std::array<int, 3> qcontent_mapped = pdg_mapped.quark_content();
784 
785  excess_quark = {0, 0, 0, 0, 0};
786  excess_antiq = {0, 0, 0, 0, 0};
787  for (int i = 0; i < 3; i++) {
788  if (qcontent_actual[i] > 0) { // quark content of the actual hadron
789  int j = qcontent_actual[i] - 1;
790  excess_quark[j] += 1;
791  }
792 
793  if (qcontent_mapped[i] > 0) { // quark content of the mapped hadron
794  int j = qcontent_mapped[i] - 1;
795  excess_quark[j] -= 1;
796  }
797 
798  if (qcontent_actual[i] < 0) { // antiquark content of the actual hadron
799  int j = std::abs(qcontent_actual[i]) - 1;
800  excess_antiq[j] += 1;
801  }
802 
803  if (qcontent_mapped[i] < 0) { // antiquark content of the mapped hadron
804  int j = std::abs(qcontent_mapped[i]) - 1;
805  excess_antiq[j] -= 1;
806  }
807  }
808 }
809 
811  Pythia8::Particle &particle, std::array<int, 5> &excess_constituent) {
812  // If the particle is neither quark nor diquark, nothing to do.
813  if (!particle.isQuark() && !particle.isDiquark()) {
814  return;
815  }
816 
817  // If there is no excess of constituents, nothing to do.
818  const std::array<int, 5> excess_null = {0, 0, 0, 0, 0};
819  if (excess_constituent == excess_null) {
820  return;
821  }
822 
823  int nq = 0;
824  std::array<int, 2> pdgid = {0, 0};
825  int spin_deg = 0;
826  int pdgid_new = 0;
827  if (particle.isQuark()) {
828  nq = 1;
829  pdgid[0] = particle.id();
830  } else if (particle.isDiquark()) {
831  nq = 2;
832  quarks_from_diquark(particle.id(), pdgid[0], pdgid[1], spin_deg);
833  }
834 
835  for (int iq = 0; iq < nq; iq++) {
836  int jq = std::abs(pdgid[iq]) - 1;
837  int k_select = 0;
838  std::vector<int> k_found;
839  k_found.clear();
840  // check if the constituent needs to be converted.
841  if (excess_constituent[jq] < 0) {
842  for (int k = 0; k < 5; k++) {
843  // check which specie it can be converted into.
844  if (k != jq && excess_constituent[k] > 0) {
845  k_found.push_back(k);
846  }
847  }
848  }
849 
850  // make a random selection of specie and update the excess of constituent.
851  if (k_found.size() > 0) {
852  const int l =
853  random::uniform_int(0, static_cast<int>(k_found.size()) - 1);
854  k_select = k_found[l];
855  /* flavor jq + 1 is converted into k_select + 1
856  * and excess_constituent is updated. */
857  pdgid[iq] = pdgid[iq] > 0 ? k_select + 1 : -(k_select + 1);
858  excess_constituent[jq] += 1;
859  excess_constituent[k_select] -= 1;
860  }
861  }
862 
863  // determine PDG id of the converted parton.
864  if (particle.isQuark()) {
865  pdgid_new = pdgid[0];
866  } else if (particle.isDiquark()) {
867  if (std::abs(pdgid[0]) < std::abs(pdgid[1])) {
868  std::swap(pdgid[0], pdgid[1]);
869  }
870 
871  pdgid_new = std::abs(pdgid[0]) * 1000 + std::abs(pdgid[1]) * 100;
872  if (std::abs(pdgid[0]) == std::abs(pdgid[1])) {
873  pdgid_new += 3;
874  } else {
875  pdgid_new += spin_deg;
876  }
877 
878  if (particle.id() < 0) {
879  pdgid_new *= -1;
880  }
881  }
882  logg[LPythia].debug(" parton id = ", particle.id(), " is converted to ",
883  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  Pythia8::Vec4 pSum = event_intermediate[0].p();
929 
930  /* compute total number of quark and antiquark constituents
931  * in the whole system. */
932  find_total_number_constituent(event_intermediate, nquark_total, nantiq_total);
933 
934  for (int iflav = 0; iflav < 5; iflav++) {
935  /* Find how many constituent will be in the system after
936  * changing the flavors.
937  * Note that nquark_total is number of constituent right after
938  * the pythia event (with mapped incoming hadrons), while the excess
939  * shows how many constituents we have more or less that nquark_total. */
940  int nquark_final =
941  excess_constituent[0][iflav] + excess_constituent[1][iflav];
942  if (sign_constituent) {
943  nquark_final += nquark_total[iflav];
944  } else {
945  nquark_final += nantiq_total[iflav];
946  }
947  /* Therefore, nquark_final should not be negative.
948  * negative nquark_final means that it will not be possible to
949  * find a constituent to change the flavor. */
950  bool enough_quark = nquark_final >= 0;
951  /* If that is the case, a gluon will be splitted into
952  * a quark-antiquark pair with the desired flavor. */
953  if (!enough_quark) {
954  logg[LPythia].debug(" not enough constituents with flavor ", iflav + 1,
955  " : try to split a gluon to qqbar.");
956  for (int ic = 0; ic < std::abs(nquark_final); ic++) {
957  /* Since each incoming hadron has its own count of the excess,
958  * it is necessary to find which one is problematic. */
959  int ih_mod = -1;
960  if (excess_constituent[0][iflav] < 0) {
961  ih_mod = 0;
962  } else {
963  ih_mod = 1;
964  }
965 
966  /* find the most forward or backward gluon
967  * depending on which incoming hadron is found to be an issue. */
968  int iforward = 1;
969  for (int ip = 2; ip < event_intermediate.size(); ip++) {
970  if (!event_intermediate[ip].isFinal() ||
971  !event_intermediate[ip].isGluon()) {
972  continue;
973  }
974 
975  const double y_gluon_current = event_intermediate[ip].y();
976  const double y_gluon_forward = event_intermediate[iforward].y();
977  if ((ih_mod == 0 && y_gluon_current > y_gluon_forward) ||
978  (ih_mod == 1 && y_gluon_current < y_gluon_forward)) {
979  iforward = ip;
980  }
981  }
982 
983  if (!event_intermediate[iforward].isGluon()) {
984  logg[LPythia].debug("There is no gluon to split into qqbar.");
985  return false;
986  }
987 
988  // four momentum of the original gluon
989  Pythia8::Vec4 pgluon = event_intermediate[iforward].p();
990 
991  const int pdgid = iflav + 1;
992  const double mass = pythia_hadron_->particleData.m0(pdgid);
993  const int status = event_intermediate[iforward].status();
994  /* color and anticolor indices.
995  * the color index of gluon goes to the quark, while
996  * the anticolor index goes to the antiquark */
997  const int col = event_intermediate[iforward].col();
998  const int acol = event_intermediate[iforward].acol();
999 
1000  // three momenta of quark and antiquark
1001  std::array<double, 2> px_quark;
1002  std::array<double, 2> py_quark;
1003  std::array<double, 2> pz_quark;
1004  // energies of quark and antiquark
1005  std::array<double, 2> e_quark;
1006  // four momenta of quark and antiquark
1007  std::array<Pythia8::Vec4, 2> pquark;
1008  // transverse momentum scale of string fragmentation
1009  const double sigma_qt_frag = pythia_hadron_->parm("StringPT:sigma");
1010  // sample relative transverse momentum between quark and antiquark
1011  const double qx = random::normal(0., sigma_qt_frag * M_SQRT1_2);
1012  const double qy = random::normal(0., sigma_qt_frag * M_SQRT1_2);
1013  // setup kinematics
1014  for (int isign = 0; isign < 2; isign++) {
1015  /* As the simplest assumption, the three momentum of gluon
1016  * is equally distributed to quark and antiquark.
1017  * Then, they have a relative transverse momentum. */
1018  px_quark[isign] = 0.5 * pgluon.px() + (isign == 0 ? 1. : -1.) * qx;
1019  py_quark[isign] = 0.5 * pgluon.py() + (isign == 0 ? 1. : -1.) * qy;
1020  pz_quark[isign] = 0.5 * pgluon.pz();
1021  e_quark[isign] =
1022  std::sqrt(mass * mass + px_quark[isign] * px_quark[isign] +
1023  py_quark[isign] * py_quark[isign] +
1024  pz_quark[isign] * pz_quark[isign]);
1025  pquark[isign] = Pythia8::Vec4(px_quark[isign], py_quark[isign],
1026  pz_quark[isign], e_quark[isign]);
1027  }
1028 
1029  /* Total energy is not conserved at this point,
1030  * but this will be cured later. */
1031  pSum += pquark[0] + pquark[1] - pgluon;
1032  // add quark and antiquark to the event record
1033  event_intermediate.append(pdgid, status, col, 0, pquark[0], mass);
1034  event_intermediate.append(-pdgid, status, 0, acol, pquark[1], mass);
1035  // then remove the gluon from the record
1036  event_intermediate.remove(iforward, iforward);
1037 
1038  logg[LPythia].debug(" gluon at iforward = ", iforward,
1039  " is splitted into ", pdgid, ",", -pdgid,
1040  " qqbar pair.");
1041  /* Increase the total number of quarks and antiquarks by 1,
1042  * as we have extra ones from a gluon. */
1043  nquark_total[iflav] += 1;
1044  nantiq_total[iflav] += 1;
1045  }
1046  }
1047  }
1048 
1049  /* The zeroth entry of event record is supposed to have the information
1050  * on the whole system. Specify the total momentum and invariant mass. */
1051  event_intermediate[0].p(pSum);
1052  event_intermediate[0].m(pSum.mCalc());
1053 
1054  return true;
1055 }
1056 
1058  std::array<int, 5> &nquark_total,
1059  std::array<std::array<int, 5>, 2> &excess_quark,
1060  std::array<std::array<int, 5>, 2> &excess_antiq) {
1061  for (int iflav = 0; iflav < 5; iflav++) {
1062  /* Find how many constituent will be in the system after
1063  * changing the flavors.
1064  * Note that nquark_total is number of constituent right after
1065  * the pythia event (with mapped incoming hadrons), while the excess
1066  * shows how many constituents we have more or less that nquark_total. */
1067  int nquark_final =
1068  nquark_total[iflav] + excess_quark[0][iflav] + excess_quark[1][iflav];
1069  /* Therefore, nquark_final should not be negative.
1070  * negative nquark_final means that it will not be possible to
1071  * find a constituent to change the flavor. */
1072  bool enough_quark = nquark_final >= 0;
1073  // If that is the case, excess of constituents will be modified
1074  if (!enough_quark) {
1075  logg[LPythia].debug(" not enough constituents with flavor ", iflav + 1,
1076  " : try to modify excess of constituents.");
1077  for (int ic = 0; ic < std::abs(nquark_final); ic++) {
1078  /* Since each incoming hadron has its own count of the excess,
1079  * it is necessary to find which one is problematic. */
1080  int ih_mod = -1;
1081  if (excess_quark[0][iflav] < 0) {
1082  ih_mod = 0;
1083  } else {
1084  ih_mod = 1;
1085  }
1086  /* Increase the excess of both quark and antiquark
1087  * with corresponding flavor (iflav + 1) by 1.
1088  * This is for conservation of the net quark number. */
1089  excess_quark[ih_mod][iflav] += 1;
1090  excess_antiq[ih_mod][iflav] += 1;
1091 
1092  /* Since incoming hadrons are mapped onto ones with
1093  * the same baryon number (or quark number),
1094  * summation of the excesses over all flavors should be zero.
1095  * Therefore, we need to find another flavor which has
1096  * a positive excess and subtract by 1. */
1097  for (int jflav = 0; jflav < 5; jflav++) {
1098  // another flavor with positive excess of constituents
1099  if (jflav != iflav && excess_quark[ih_mod][jflav] > 0) {
1100  /* Decrease the excess of both quark and antiquark
1101  * with corresponding flavor (jflav + 1) by 1. */
1102  excess_quark[ih_mod][jflav] -= 1;
1103  excess_antiq[ih_mod][jflav] -= 1;
1104  /* We only need to find one (another) flavor to subtract.
1105  * No more! */
1106  break;
1107  }
1108  }
1109  }
1110  }
1111  }
1112 }
1113 
1115  Pythia8::Event &event_intermediate,
1116  std::array<std::array<int, 5>, 2> &excess_quark,
1117  std::array<std::array<int, 5>, 2> &excess_antiq) {
1118  Pythia8::Vec4 pSum = event_intermediate[0].p();
1119  const double energy_init = pSum.e();
1120  logg[LPythia].debug(" initial total energy [GeV] : ", energy_init);
1121 
1122  // Total number of quarks and antiquarks, respectively.
1123  std::array<int, 5> nquark_total;
1124  std::array<int, 5> nantiq_total;
1125 
1126  /* Split a gluon into qqbar if we do not have enough constituents
1127  * to be converted in the system. */
1128  bool split_for_quark = splitting_gluon_qqbar(
1129  event_intermediate, nquark_total, nantiq_total, true, excess_quark);
1130  bool split_for_antiq = splitting_gluon_qqbar(
1131  event_intermediate, nquark_total, nantiq_total, false, excess_antiq);
1132 
1133  /* Modify excess_quark and excess_antiq if we do not have enough constituents
1134  * to be converted in the system. */
1135  if (!split_for_quark || !split_for_antiq) {
1136  rearrange_excess(nquark_total, excess_quark, excess_antiq);
1137  rearrange_excess(nantiq_total, excess_antiq, excess_quark);
1138  }
1139 
1140  // Final check if there are enough constituents.
1141  for (int iflav = 0; iflav < 5; iflav++) {
1142  if (nquark_total[iflav] + excess_quark[0][iflav] + excess_quark[1][iflav] <
1143  0) {
1144  logg[LPythia].debug("Not enough quark constituents of flavor ",
1145  iflav + 1);
1146  return false;
1147  }
1148 
1149  if (nantiq_total[iflav] + excess_antiq[0][iflav] + excess_antiq[1][iflav] <
1150  0) {
1151  logg[LPythia].debug("Not enough antiquark constituents of flavor ",
1152  -(iflav + 1));
1153  return false;
1154  }
1155  }
1156 
1157  for (int ih = 0; ih < 2; ih++) {
1158  logg[LPythia].debug(" initial excess_quark[", ih, "] = (",
1159  excess_quark[ih][0], ", ", excess_quark[ih][1], ", ",
1160  excess_quark[ih][2], ", ", excess_quark[ih][3], ", ",
1161  excess_quark[ih][4], ")");
1162  logg[LPythia].debug(" initial excess_antiq[", ih, "] = (",
1163  excess_antiq[ih][0], ", ", excess_antiq[ih][1], ", ",
1164  excess_antiq[ih][2], ", ", excess_antiq[ih][3], ", ",
1165  excess_antiq[ih][4], ")");
1166  }
1167 
1168  bool recovered_quarks = false;
1169  while (!recovered_quarks) {
1170  /* Flavor conversion begins with the most forward and backward parton
1171  * respectively for incoming_particles_[0] and incoming_particles_[1]. */
1172  std::array<bool, 2> find_forward = {true, false};
1173  const std::array<int, 5> excess_null = {0, 0, 0, 0, 0};
1174  std::array<int, 5> excess_total = excess_null;
1175 
1176  for (int ih = 0; ih < 2; ih++) { // loop over incoming hadrons
1177  int nfrag = event_intermediate.size();
1178  for (int np_end = 0; np_end < nfrag - 1; np_end++) { // constituent loop
1179  /* select the np_end-th most forward or backward parton and
1180  * change its specie.
1181  * np_end = 0 corresponds to the most forward,
1182  * np_end = 1 corresponds to the second most forward and so on. */
1183  int iforward =
1184  get_index_forward(find_forward[ih], np_end, event_intermediate);
1185  pSum -= event_intermediate[iforward].p();
1186 
1187  if (event_intermediate[iforward].id() > 0) { // quark and diquark
1188  replace_constituent(event_intermediate[iforward], excess_quark[ih]);
1189  logg[LPythia].debug(
1190  " 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  logg[LPythia].debug(
1196  " excess_antiq[", ih, "] = (", excess_antiq[ih][0], ", ",
1197  excess_antiq[ih][1], ", ", excess_antiq[ih][2], ", ",
1198  excess_antiq[ih][3], ", ", excess_antiq[ih][4], ")");
1199  }
1200 
1201  const int pdgid = event_intermediate[iforward].id();
1202  Pythia8::Vec4 pquark = event_intermediate[iforward].p();
1203  const double mass = pythia_hadron_->particleData.m0(pdgid);
1204 
1205  const int status = event_intermediate[iforward].status();
1206  const int color = event_intermediate[iforward].col();
1207  const int anticolor = event_intermediate[iforward].acol();
1208 
1209  pSum += pquark;
1210  event_intermediate.append(pdgid, status, color, anticolor, pquark,
1211  mass);
1212 
1213  event_intermediate.remove(iforward, iforward);
1214  /* Now the last np_end + 1 entries in event_intermediate
1215  * are np_end + 1 most forward (or backward) partons. */
1216  }
1217 
1218  // Compute the excess of net quark numbers.
1219  for (int j = 0; j < 5; j++) {
1220  excess_total[j] += (excess_quark[ih][j] - excess_antiq[ih][j]);
1221  }
1222  }
1223 
1224  /* If there is no excess of net quark numbers,
1225  * quark content is considered to be correct. */
1226  recovered_quarks = excess_total == excess_null;
1227  }
1228  logg[LPythia].debug(" valence quark contents of hadons are recovered.");
1229 
1230  logg[LPythia].debug(" current total energy [GeV] : ", pSum.e());
1231  /* rescale momenta of all partons by a constant factor
1232  * to conserve the total energy. */
1233  while (true) {
1234  if (std::abs(pSum.e() - energy_init) <=
1235  std::abs(really_small * energy_init)) {
1236  break;
1237  }
1238 
1239  double energy_current = pSum.e();
1240  double slope = 0.;
1241  for (int i = 1; i < event_intermediate.size(); i++) {
1242  slope += event_intermediate[i].pAbs2() / event_intermediate[i].e();
1243  }
1244 
1245  const double rescale_factor = 1. + (energy_init - energy_current) / slope;
1246  pSum = 0.;
1247  for (int i = 1; i < event_intermediate.size(); i++) {
1248  const double px = rescale_factor * event_intermediate[i].px();
1249  const double py = rescale_factor * event_intermediate[i].py();
1250  const double pz = rescale_factor * event_intermediate[i].pz();
1251  const double pabs = rescale_factor * event_intermediate[i].pAbs();
1252  const double mass = event_intermediate[i].m();
1253 
1254  event_intermediate[i].px(px);
1255  event_intermediate[i].py(py);
1256  event_intermediate[i].pz(pz);
1257  event_intermediate[i].e(std::sqrt(mass * mass + pabs * pabs));
1258  pSum += event_intermediate[i].p();
1259  }
1260  logg[LPythia].debug(" parton momenta are rescaled by factor of ",
1261  rescale_factor);
1262  }
1263 
1264  logg[LPythia].debug(" final total energy [GeV] : ", pSum.e());
1265  /* The zeroth entry of event record is supposed to have the information
1266  * on the whole system. Specify the total momentum and invariant mass. */
1267  event_intermediate[0].p(pSum);
1268  event_intermediate[0].m(pSum.mCalc());
1269 
1270  return true;
1271 }
1272 
1273 void StringProcess::compose_string_parton(bool find_forward_string,
1274  Pythia8::Event &event_intermediate,
1275  Pythia8::Event &event_hadronize) {
1276  Pythia8::Vec4 pSum = 0.;
1277  event_hadronize.reset();
1278 
1279  // select the most forward or backward parton.
1280  int iforward = get_index_forward(find_forward_string, 0, event_intermediate);
1281  logg[LPythia].debug("Hard non-diff: iforward = ", iforward, "(",
1282  event_intermediate[iforward].id(), ")");
1283 
1284  pSum += event_intermediate[iforward].p();
1285  event_hadronize.append(event_intermediate[iforward]);
1286 
1287  int col_to_find = event_intermediate[iforward].acol();
1288  int acol_to_find = event_intermediate[iforward].col();
1289  event_intermediate.remove(iforward, iforward);
1290  logg[LPythia].debug("Hard non-diff: event_intermediate reduces in size to ",
1291  event_intermediate.size());
1292 
1293  // trace color and anti-color indices and find corresponding partons.
1294  while (col_to_find != 0 || acol_to_find != 0) {
1295  logg[LPythia].debug(" col_to_find = ", col_to_find,
1296  ", acol_to_find = ", acol_to_find);
1297 
1298  int ifound = -1;
1299  for (int i = 1; i < event_intermediate.size(); i++) {
1300  const int pdgid = event_intermediate[i].id();
1301  bool found_col =
1302  col_to_find != 0 && col_to_find == event_intermediate[i].col();
1303  bool found_acol =
1304  acol_to_find != 0 && acol_to_find == event_intermediate[i].acol();
1305  if (found_col) {
1306  logg[LPythia].debug(" col_to_find ", col_to_find, " from i ", i, "(",
1307  pdgid, ") found");
1308  }
1309  if (found_acol) {
1310  logg[LPythia].debug(" acol_to_find ", acol_to_find, " from i ", i, "(",
1311  pdgid, ") found");
1312  }
1313 
1314  if (found_col && !found_acol) {
1315  ifound = i;
1316  col_to_find = event_intermediate[i].acol();
1317  break;
1318  } else if (!found_col && found_acol) {
1319  ifound = i;
1320  acol_to_find = event_intermediate[i].col();
1321  break;
1322  } else if (found_col && found_acol) {
1323  ifound = i;
1324  col_to_find = 0;
1325  acol_to_find = 0;
1326  break;
1327  }
1328  }
1329 
1330  if (ifound < 0) {
1331  event_intermediate.list();
1332  event_intermediate.listJunctions();
1333  event_hadronize.list();
1334  event_hadronize.listJunctions();
1335  if (col_to_find != 0) {
1336  logg[LPythia].error("No parton with col = ", col_to_find);
1337  }
1338  if (acol_to_find != 0) {
1339  logg[LPythia].error("No parton with acol = ", acol_to_find);
1340  }
1341  throw std::runtime_error("Hard string could not be identified.");
1342  } else {
1343  pSum += event_intermediate[ifound].p();
1344  // add a parton to the new event record.
1345  event_hadronize.append(event_intermediate[ifound]);
1346  // then remove from the original event record.
1347  event_intermediate.remove(ifound, ifound);
1348  logg[LPythia].debug(
1349  "Hard non-diff: event_intermediate reduces in size to ",
1350  event_intermediate.size());
1351  }
1352  }
1353 
1354  /* The zeroth entry of event record is supposed to have the information
1355  * on the whole system. Specify the total momentum and invariant mass. */
1356  event_hadronize[0].p(pSum);
1357  event_hadronize[0].m(pSum.mCalc());
1358 }
1359 
1360 void StringProcess::compose_string_junction(bool &find_forward_string,
1361  Pythia8::Event &event_intermediate,
1362  Pythia8::Event &event_hadronize) {
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  logg[LPythia].debug("junction (", col[0], ", ", col[1], ", ", col[2],
1380  ") with kind ", 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  logg[LPythia].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  logg[LPythia].debug(" junction ", i, " (",
1433  event_intermediate.colJunction(i, 0), ", ",
1434  event_intermediate.colJunction(i, 1), ", ",
1435  event_intermediate.colJunction(i, 2),
1436  ") with kind ", 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  Pythia8::Vec4 pSum = event_hadronize[0].p();
1467  for (unsigned int j = 0; j < col.size(); j++) {
1468  if (col[j] == 0) {
1469  continue;
1470  }
1471  bool found_leg = false;
1472  while (!found_leg) {
1473  int ifound = -1;
1474  for (int i = 1; i < event_intermediate.size(); i++) {
1475  const int pdgid = event_intermediate[i].id();
1476  if (sign_color && col[j] == event_intermediate[i].col()) {
1477  logg[LPythia].debug(" col[", j, "] = ", col[j], " from i ", i, "(",
1478  pdgid, ") found");
1479  ifound = i;
1480  col[j] = event_intermediate[i].acol();
1481  break;
1482  } else if (!sign_color && col[j] == event_intermediate[i].acol()) {
1483  logg[LPythia].debug(" acol[", j, "] = ", col[j], " from i ", i, "(",
1484  pdgid, ") found");
1485  ifound = i;
1486  col[j] = event_intermediate[i].col();
1487  break;
1488  }
1489  }
1490 
1491  if (ifound < 0) {
1492  found_leg = true;
1493  if (event_intermediate.sizeJunction() == 0) {
1494  event_intermediate.list();
1495  event_intermediate.listJunctions();
1496  event_hadronize.list();
1497  event_hadronize.listJunctions();
1498  logg[LPythia].error("No parton with col = ", col[j],
1499  " connected with junction leg ", j);
1500  throw std::runtime_error("Hard string could not be identified.");
1501  }
1502  } else {
1503  pSum += event_intermediate[ifound].p();
1504  // add a parton to the new event record.
1505  event_hadronize.append(event_intermediate[ifound]);
1506  // then remove from the original event record.
1507  event_intermediate.remove(ifound, ifound);
1508  logg[LPythia].debug(
1509  "Hard non-diff: event_intermediate reduces in size to ",
1510  event_intermediate.size());
1511  if (col[j] == 0) {
1512  found_leg = true;
1513  }
1514  }
1515  }
1516  }
1517 
1518  /* The zeroth entry of event record is supposed to have the information
1519  * on the whole system. Specify the total momentum and invariant mass. */
1520  event_hadronize[0].p(pSum);
1521  event_hadronize[0].m(pSum.mCalc());
1522 }
1523 
1524 // baryon-antibaryon annihilation
1526  const std::array<FourVector, 2> ustrcom = {FourVector(1., 0., 0., 0.),
1527  FourVector(1., 0., 0., 0.)};
1528 
1529  NpartFinal_ = 0;
1530  NpartString_[0] = 0;
1531  NpartString_[1] = 0;
1532  final_state_.clear();
1533 
1534  logg[LPythia].debug("Annihilation occurs between ", PDGcodes_[0], "+",
1535  PDGcodes_[1], " at CM energy [GeV] ", sqrtsAB_);
1536 
1537  // check if the initial state is baryon-antibaryon pair.
1538  PdgCode baryon = PDGcodes_[0], antibaryon = PDGcodes_[1];
1539  if (baryon.baryon_number() == -1) {
1540  std::swap(baryon, antibaryon);
1541  }
1542  if (baryon.baryon_number() != 1 || antibaryon.baryon_number() != -1) {
1543  throw std::invalid_argument("Expected baryon-antibaryon pair.");
1544  }
1545 
1546  // Count how many qqbar combinations are possible for each quark type
1547  constexpr int n_q_types = 5; // u, d, s, c, b
1548  std::vector<int> qcount_bar, qcount_antibar;
1549  std::vector<int> n_combinations;
1550  bool no_combinations = true;
1551  for (int i = 0; i < n_q_types; i++) {
1552  qcount_bar.push_back(baryon.net_quark_number(i + 1));
1553  qcount_antibar.push_back(-antibaryon.net_quark_number(i + 1));
1554  const int n_i = qcount_bar[i] * qcount_antibar[i];
1555  n_combinations.push_back(n_i);
1556  if (n_i > 0) {
1557  no_combinations = false;
1558  }
1559  }
1560 
1561  /* if it is a BBbar pair but there is no qqbar pair to annihilate,
1562  * nothing happens */
1563  if (no_combinations) {
1564  for (int i = 0; i < 2; i++) {
1565  NpartString_[i] = 1;
1566  ParticleData new_particle(ParticleType::find(PDGcodes_[i]));
1567  new_particle.set_4momentum(pcom_[i]);
1568  new_particle.set_cross_section_scaling_factor(1.);
1569  new_particle.set_formation_time(time_collision_);
1570  final_state_.push_back(new_particle);
1571  }
1573  return true;
1574  }
1575 
1576  // Select qqbar pair to annihilate and remove it away
1577  auto discrete_distr = random::discrete_dist<int>(n_combinations);
1578  const int q_annihilate = discrete_distr() + 1;
1579  qcount_bar[q_annihilate - 1]--;
1580  qcount_antibar[q_annihilate - 1]--;
1581 
1582  // Get the remaining quarks and antiquarks
1583  std::vector<int> remaining_quarks, remaining_antiquarks;
1584  for (int i = 0; i < n_q_types; i++) {
1585  for (int j = 0; j < qcount_bar[i]; j++) {
1586  remaining_quarks.push_back(i + 1);
1587  }
1588  for (int j = 0; j < qcount_antibar[i]; j++) {
1589  remaining_antiquarks.push_back(-(i + 1));
1590  }
1591  }
1592  assert(remaining_quarks.size() == 2);
1593  assert(remaining_antiquarks.size() == 2);
1594 
1595  const std::array<double, 2> mstr = {0.5 * sqrtsAB_, 0.5 * sqrtsAB_};
1596 
1597  // randomly select two quark-antiquark pairs
1598  if (random::uniform_int(0, 1) == 0) {
1599  std::swap(remaining_quarks[0], remaining_quarks[1]);
1600  }
1601  if (random::uniform_int(0, 1) == 0) {
1602  std::swap(remaining_antiquarks[0], remaining_antiquarks[1]);
1603  }
1604  // Make sure it satisfies kinematical threshold constraint
1605  bool kin_threshold_satisfied = true;
1606  for (int i = 0; i < 2; i++) {
1607  const double mstr_min =
1608  pythia_hadron_->particleData.m0(remaining_quarks[i]) +
1609  pythia_hadron_->particleData.m0(remaining_antiquarks[i]);
1610  if (mstr_min > mstr[i]) {
1611  kin_threshold_satisfied = false;
1612  }
1613  }
1614  if (!kin_threshold_satisfied) {
1615  return false;
1616  }
1617  // Fragment two strings
1618  for (int i = 0; i < 2; i++) {
1619  ParticleList new_intermediate_particles;
1620 
1621  ThreeVector evec = pcom_[i].threevec() / pcom_[i].threevec().abs();
1622  const int nfrag =
1623  fragment_string(remaining_quarks[i], remaining_antiquarks[i], mstr[i],
1624  evec, true, false, new_intermediate_particles);
1625  if (nfrag <= 0) {
1626  NpartString_[i] = 0;
1627  return false;
1628  }
1629  NpartString_[i] =
1630  append_final_state(new_intermediate_particles, ustrcom[i], evec);
1631  }
1633  return true;
1634 }
1635 
1637  ThreeVector &evec_polar, std::array<ThreeVector, 3> &evec_basis) {
1638  assert(std::fabs(evec_polar.sqr() - 1.) < really_small);
1639 
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(std::cos(theta) * std::cos(phi));
1661  evec_basis[1].set_x2(std::cos(theta) * std::sin(phi));
1662  evec_basis[1].set_x3(-std::sin(theta));
1663 
1664  evec_basis[2].set_x1(-std::sin(phi));
1665  evec_basis[2].set_x2(std::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  assert(std::fabs(evec_basis[1] * evec_basis[2]) < really_small);
1681  assert(std::fabs(evec_basis[2] * evec_basis[0]) < really_small);
1682  assert(std::fabs(evec_basis[0] * evec_basis[1]) < really_small);
1683 }
1684 
1686  PPosA_ = (pcom_[0].x0() + evecBasisAB_[0] * pcom_[0].threevec()) * M_SQRT1_2;
1687  PNegA_ = (pcom_[0].x0() - evecBasisAB_[0] * pcom_[0].threevec()) * M_SQRT1_2;
1688  PPosB_ = (pcom_[1].x0() + evecBasisAB_[0] * pcom_[1].threevec()) * M_SQRT1_2;
1689  PNegB_ = (pcom_[1].x0() - evecBasisAB_[0] * pcom_[1].threevec()) * M_SQRT1_2;
1690 }
1691 
1692 void StringProcess::quarks_from_diquark(int diquark, int &q1, int &q2,
1693  int &deg_spin) {
1694  // The 4-digit pdg id should be diquark.
1695  assert((std::abs(diquark) > 1000) && (std::abs(diquark) < 5510) &&
1696  (std::abs(diquark) % 100 < 10));
1697 
1698  // The fourth digit corresponds to the spin degeneracy.
1699  deg_spin = std::abs(diquark) % 10;
1700  // Diquark (anti-diquark) is decomposed into two quarks (antiquarks).
1701  const int sign_anti = diquark > 0 ? 1 : -1;
1702 
1703  // Obtain two quarks (or antiquarks) from the first and second digit.
1704  q1 = sign_anti * (std::abs(diquark) - (std::abs(diquark) % 1000)) / 1000;
1705  q2 = sign_anti * (std::abs(diquark) % 1000 - deg_spin) / 100;
1706 }
1707 
1709  assert((q1 > 0 && q2 > 0) || (q1 < 0 && q2 < 0));
1710  if (std::abs(q1) < std::abs(q2)) {
1711  std::swap(q1, q2);
1712  }
1713  int diquark = std::abs(q1 * 1000 + q2 * 100);
1714  /* Adding spin degeneracy = 2S+1. For identical quarks spin cannot be 0
1715  * because of Pauli exclusion principle, so spin 1 is assumed. Otherwise
1716  * S = 0 with probability 1/4 and S = 1 with probability 3/4. */
1717  diquark += (q1 != q2 && random::uniform_int(0, 3) == 0) ? 1 : 3;
1718  return (q1 < 0) ? -diquark : diquark;
1719 }
1720 
1721 void StringProcess::make_string_ends(const PdgCode &pdg, int &idq1, int &idq2,
1722  double xi) {
1723  std::array<int, 3> quarks = pdg.quark_content();
1724  if (pdg.is_nucleon()) {
1725  // protons and neutrons treated seperately since single quarks is at a
1726  // different position in the PDG code
1727  if (pdg.charge() == 0) { // (anti)neutron
1728  if (random::uniform(0., 1.) < xi) {
1729  idq1 = quarks[0];
1730  idq2 = diquark_from_quarks(quarks[1], quarks[2]);
1731  } else {
1732  idq1 = quarks[1];
1733  idq2 = diquark_from_quarks(quarks[0], quarks[2]);
1734  }
1735  } else { // (anti)proton
1736  if (random::uniform(0., 1.) < xi) {
1737  idq1 = quarks[2];
1738  idq2 = diquark_from_quarks(quarks[0], quarks[1]);
1739  } else {
1740  idq1 = quarks[0];
1741  idq2 = diquark_from_quarks(quarks[1], quarks[2]);
1742  }
1743  }
1744  } else {
1745  if (pdg.is_meson()) {
1746  idq1 = quarks[1];
1747  idq2 = quarks[2];
1748  /* Some mesons with PDG id 11X are actually mixed state of uubar and
1749  * ddbar. have a random selection whether we have uubar or ddbar in this
1750  * case. */
1751  if (idq1 == 1 && idq2 == -1 && random::uniform_int(0, 1) == 0) {
1752  idq1 = 2;
1753  idq2 = -2;
1754  }
1755  } else {
1756  assert(pdg.is_baryon());
1757  // Get random quark to position 0
1758  std::swap(quarks[random::uniform_int(0, 2)], quarks[0]);
1759  idq1 = quarks[0];
1760  idq2 = diquark_from_quarks(quarks[1], quarks[2]);
1761  }
1762  }
1763  // Fulfil the convention: idq1 should be quark or anti-diquark
1764  if (idq1 < 0) {
1765  std::swap(idq1, idq2);
1766  }
1767 }
1768 
1769 int StringProcess::fragment_string(int idq1, int idq2, double mString,
1770  ThreeVector &evecLong, bool flip_string_ends,
1771  bool separate_fragment_baryon,
1772  ParticleList &intermediate_particles) {
1773  pythia_hadron_->event.reset();
1774  intermediate_particles.clear();
1775 
1776  logg[LPythia].debug("initial quark content for fragment_string : ", idq1,
1777  ", ", idq2);
1778  logg[LPythia].debug("initial string mass (GeV) for fragment_string : ",
1779  mString);
1780  // PDG id of quark constituents of string ends
1781  std::array<int, 2> idqIn;
1782  idqIn[0] = idq1;
1783  idqIn[1] = idq2;
1784 
1785  int bstring = 0;
1786  // constituent masses of the string
1787  std::array<double, 2> m_const;
1788 
1789  for (int i = 0; i < 2; i++) {
1790  // evaluate total baryon number of the string times 3
1791  bstring += pythia_hadron_->particleData.baryonNumberType(idqIn[i]);
1792 
1793  m_const[i] = pythia_hadron_->particleData.m0(idqIn[i]);
1794  }
1795  logg[LPythia].debug("baryon number of string times 3 : ", bstring);
1796 
1797  if (flip_string_ends && random::uniform_int(0, 1) == 0) {
1798  /* in the case where we flip the string ends,
1799  * we need to flip the longitudinal unit vector itself
1800  * since it is set to be direction of diquark (anti-quark)
1801  * or anti-diquark. */
1802  evecLong = -evecLong;
1803  }
1804 
1805  if (m_const[0] + m_const[1] > mString) {
1806  throw std::runtime_error("String fragmentation: m1 + m2 > mString");
1807  }
1808  Pythia8::Vec4 pSum = 0.;
1809 
1810  int number_of_fragments = 0;
1811  bool do_string_fragmentation = false;
1812 
1813  /* lightcone momenta p^+ and p^- of the string
1814  * p^{\pm} is defined as (E \pm p_{longitudinal}) / sqrts{2}. */
1815  double ppos_string_new, pneg_string_new;
1816  /* transverse momentum (and magnitude) acquired
1817  * by the the string to be fragmented */
1818  double QTrx_string_new, QTry_string_new, QTrn_string_new;
1819  // transverse mass of the string to be fragmented
1820  double mTrn_string_new;
1821  // mass of the string to be fragmented
1822  double mass_string_new;
1823 
1824  /* Transverse momentum to be added to the most forward hadron
1825  * from PYTHIA fragmentation */
1826  double QTrx_add_pos, QTry_add_pos;
1827  /* Transverse momentum to be added to the most backward hadron
1828  * from PYTHIA fragmentation */
1829  double QTrx_add_neg, QTry_add_neg;
1830 
1831  /* Set those transverse momenta to be zero.
1832  * This is the case when we solely rely on the PYTHIA fragmentation
1833  * procedure without separate fragmentation function.
1834  * In the case of separate fragmentation function for the leading baryon,
1835  * appropriate values will be assigned later. */
1836  QTrx_add_pos = 0.;
1837  QTry_add_pos = 0.;
1838  QTrx_add_neg = 0.;
1839  QTry_add_neg = 0.;
1840 
1841  std::array<ThreeVector, 3> evec_basis;
1842  make_orthonormal_basis(evecLong, evec_basis);
1843 
1844  if (separate_fragment_baryon && (std::abs(bstring) == 3) &&
1845  (mString > m_const[0] + m_const[1] + 1.)) {
1846  /* A separate fragmentation function will be used to the leading baryon,
1847  * if we have a baryonic string and the corresponding option is turned on.
1848  */
1849  int n_frag_prior;
1850  /* PDG id of fragmented hadrons
1851  * before switching to the PYTHIA fragmentation */
1852  std::vector<int> pdgid_frag_prior;
1853  /* four-momenta of fragmented hadrons
1854  * before switching to the PYTHIA fragmentation */
1855  std::vector<FourVector> momentum_frag_prior;
1856 
1857  // Transverse momentum px of the forward end of the string
1858  double QTrx_string_pos;
1859  // Transverse momentum px of the backward end of the string
1860  double QTrx_string_neg;
1861  // Transverse momentum py of the forward end of the string
1862  double QTry_string_pos;
1863  // Transverse momentum py of the backward end of the string
1864  double QTry_string_neg;
1865  /* Absolute value of the transverse momentum of
1866  * the forward end of the string */
1867  double QTrn_string_pos;
1868  /* Absolute value of the transverse momentum of
1869  * the backward end of the string */
1870  double QTrn_string_neg;
1871 
1872  std::array<double, 2> m_trans;
1873 
1874  // How many times we try to fragment leading baryon.
1875  const int niter_max = 10000;
1876  bool found_leading_baryon = false;
1877  for (int iiter = 0; iiter < niter_max; iiter++) {
1878  n_frag_prior = 0;
1879  pdgid_frag_prior.clear();
1880  momentum_frag_prior.clear();
1881  int n_frag = 0;
1882  std::vector<int> pdgid_frag;
1883  std::vector<FourVector> momentum_frag;
1884  // The original string is aligned in the logitudinal direction.
1885  ppos_string_new = mString * M_SQRT1_2;
1886  pneg_string_new = mString * M_SQRT1_2;
1887  // There is no transverse momentum at the original string ends.
1888  QTrx_string_pos = 0.;
1889  QTrx_string_neg = 0.;
1890  QTrx_string_new = 0.;
1891  QTry_string_pos = 0.;
1892  QTry_string_neg = 0.;
1893  QTry_string_new = 0.;
1894  // Constituent flavor at the forward (diquark) end of the string
1895  Pythia8::FlavContainer flav_string_pos =
1896  bstring > 0 ? Pythia8::FlavContainer(idq2)
1897  : Pythia8::FlavContainer(idq1);
1898  // Constituent flavor at the backward (quark) end of the string
1899  Pythia8::FlavContainer flav_string_neg =
1900  bstring > 0 ? Pythia8::FlavContainer(idq1)
1901  : Pythia8::FlavContainer(idq2);
1902  // Whether the first baryon from the forward end is fragmented
1903  bool found_forward_baryon = false;
1904  // Whether the first hadron from the forward end is fragmented
1905  bool done_forward_end = false;
1906  /* Whether energy of the string is depleted and the string
1907  * breaks into final two hadrons. */
1908  bool energy_used_up = false;
1909  while (!found_forward_baryon && !energy_used_up) {
1910  /* Keep fragmenting hadrons until
1911  * the first baryon is fragmented from the forward (diquark) end or
1912  * energy of the string is used up. */
1913  // Randomly select the string end from which the hadron is fragmented.
1914  bool from_forward = random::uniform_int(0, 1) == 0;
1915  /* The separate fragmentation function for the leading baryon kicks in
1916  * only if the first fragmented hadron from the forward (diquark) end
1917  * is a baryon. */
1918  n_frag = fragment_off_hadron(
1919  from_forward,
1920  separate_fragment_baryon && from_forward && !done_forward_end,
1921  evec_basis, ppos_string_new, pneg_string_new, QTrx_string_pos,
1922  QTrx_string_neg, QTry_string_pos, QTry_string_neg, flav_string_pos,
1923  flav_string_neg, pdgid_frag, momentum_frag);
1924  if (n_frag == 0) {
1925  /* If it fails to fragment hadron, start over from
1926  * the initial (baryonic) string configuration. */
1927  break;
1928  } else {
1929  QTrx_string_new = QTrx_string_pos + QTrx_string_neg;
1930  QTry_string_new = QTry_string_pos + QTry_string_neg;
1931  /* Quark (antiquark) constituents of the remaining string are
1932  * different from those of the original string.
1933  * Therefore, the constituent masses have to be updated. */
1934  idqIn[0] = bstring > 0 ? flav_string_neg.id : flav_string_pos.id;
1935  idqIn[1] = bstring > 0 ? flav_string_pos.id : flav_string_neg.id;
1936  for (int i = 0; i < 2; i++) {
1937  m_const[i] = pythia_hadron_->particleData.m0(idqIn[i]);
1938  }
1939  QTrn_string_pos = std::sqrt(QTrx_string_pos * QTrx_string_pos +
1940  QTry_string_pos * QTry_string_pos);
1941  QTrn_string_neg = std::sqrt(QTrx_string_neg * QTrx_string_neg +
1942  QTry_string_neg * QTry_string_neg);
1943  if (bstring > 0) { // in the case of baryonic string
1944  /* Quark is coming from the newly produced backward qqbar pair
1945  * and therefore has transverse momentum, which is opposite to
1946  * that of the fragmented (backward) meson. */
1947  m_trans[0] = std::sqrt(m_const[0] * m_const[0] +
1948  QTrn_string_neg * QTrn_string_neg);
1949  /* Antiquark is coming from the newly produced forward qqbar pair
1950  * and therefore has transverse momentum, which is opposite to
1951  * that of the fragmented (leading) baryon. */
1952  m_trans[1] = std::sqrt(m_const[1] * m_const[1] +
1953  QTrn_string_pos * QTrn_string_pos);
1954  } else { // in the case of anti-baryonic string
1955  /* Quark is coming from the newly produced forward qqbar pair
1956  * and therefore has transverse momentum, which is opposite to
1957  * that of the fragmented (leading) antibaryon. */
1958  m_trans[0] = std::sqrt(m_const[0] * m_const[0] +
1959  QTrn_string_pos * QTrn_string_pos);
1960  /* Antiquark is coming from the newly produced backward qqbar pair
1961  * and therefore has transverse momentum, which is opposite to
1962  * that of the fragmented (backward) meson. */
1963  m_trans[1] = std::sqrt(m_const[1] * m_const[1] +
1964  QTrn_string_neg * QTrn_string_neg);
1965  }
1966  done_forward_end = done_forward_end || from_forward;
1967  found_forward_baryon =
1968  found_forward_baryon ||
1969  (from_forward &&
1970  pythia_hadron_->particleData.isBaryon(pdgid_frag[0]));
1971  }
1972  if (n_frag == 2) {
1973  energy_used_up = true;
1974  }
1975  /* Add PDG id and four-momenta of fragmented hadrons
1976  * to the list if fragmentation is successful. */
1977  n_frag_prior += n_frag;
1978  for (int i_frag = 0; i_frag < n_frag; i_frag++) {
1979  pdgid_frag_prior.push_back(pdgid_frag[i_frag]);
1980  momentum_frag_prior.push_back(momentum_frag[i_frag]);
1981  }
1982  }
1983  if (n_frag == 0) {
1984  continue;
1985  } else {
1986  if (n_frag == 1) {
1987  // Compute transverse mass and momentum of the remaining string.
1988  double mTsqr_string = 2. * ppos_string_new * pneg_string_new;
1989  mTrn_string_new = std::sqrt(mTsqr_string);
1990  QTrn_string_new = std::sqrt(QTrx_string_new * QTrx_string_new +
1991  QTry_string_new * QTry_string_new);
1992  if (mTrn_string_new < QTrn_string_new) {
1993  /* If transverse mass is lower than transverse momentum,
1994  * start over. */
1995  found_leading_baryon = false;
1996  } else {
1997  // Compute mass of the remaining string.
1998  mass_string_new =
1999  std::sqrt(mTsqr_string - QTrn_string_new * QTrn_string_new);
2000  /* Proceed only if the string mass is large enough to call
2001  * PYTHIA fragmentation routine.
2002  * Otherwise, start over. */
2003  if (mass_string_new > m_const[0] + m_const[1]) {
2004  do_string_fragmentation = true;
2005  found_leading_baryon = true;
2006  QTrx_add_pos = QTrx_string_pos;
2007  QTry_add_pos = QTry_string_pos;
2008  QTrx_add_neg = QTrx_string_neg;
2009  QTry_add_neg = QTry_string_neg;
2010  } else {
2011  found_leading_baryon = false;
2012  }
2013  }
2014  } else if (n_frag == 2) {
2015  /* If the string ended up breaking into final two hadrons,
2016  * there is no need to perform PYTHIA fragmentation. */
2017  do_string_fragmentation = false;
2018  found_leading_baryon = true;
2019  }
2020  }
2021 
2022  if (found_leading_baryon) {
2023  break;
2024  }
2025  }
2026  if (found_leading_baryon) {
2027  /* If the kinematics makes sense, add fragmented hadrons so far
2028  * to the intermediate particle list. */
2029  for (int i_frag = 0; i_frag < n_frag_prior; i_frag++) {
2030  logg[LPythia].debug("appending the the fragmented hadron ",
2031  pdgid_frag_prior[i_frag],
2032  " to the intermediate particle list.");
2033 
2034  bool found_ptype = append_intermediate_list(pdgid_frag_prior[i_frag],
2035  momentum_frag_prior[i_frag],
2036  intermediate_particles);
2037  if (!found_ptype) {
2038  logg[LPythia].error("PDG ID ", pdgid_frag_prior[i_frag],
2039  " should exist in ParticleType.");
2040  throw std::runtime_error("string fragmentation failed.");
2041  }
2042  number_of_fragments++;
2043  }
2044  } else {
2045  /* If it is not possible to find the leading baryon with appropriate
2046  * kinematics after trying many times, return failure (no hadron). */
2047  return 0;
2048  }
2049 
2050  if (do_string_fragmentation) {
2051  mTrn_string_new = std::sqrt(2. * ppos_string_new * pneg_string_new);
2052  // lightcone momentum p^+ of the quark constituents on the string ends
2053  std::array<double, 2> ppos_parton;
2054  // lightcone momentum p^- of the quark constituents on the string ends
2055  std::array<double, 2> pneg_parton;
2056 
2057  /* lightcone momenta of the string ends (quark and antiquark)
2058  * First, obtain ppos_parton[0] and ppos_parton[1]
2059  * (p^+ of quark and antiquark) by solving the following equations.
2060  * ppos_string_new = ppos_parton[0] + ppos_parton[1]
2061  * pneg_string_new = 0.5 * m_trans[0] * m_trans[0] / ppos_parton[0] +
2062  * 0.5 * m_trans[1] * m_trans[1] / ppos_parton[1] */
2063  const double pb_const =
2064  (mTrn_string_new * mTrn_string_new + m_trans[0] * m_trans[0] -
2065  m_trans[1] * m_trans[1]) /
2066  (4. * pneg_string_new);
2067  const double pc_const =
2068  0.5 * m_trans[0] * m_trans[0] * ppos_string_new / pneg_string_new;
2069  ppos_parton[0] = pb_const + (bstring > 0 ? -1. : 1.) *
2070  std::sqrt(pb_const * pb_const - pc_const);
2071  ppos_parton[1] = ppos_string_new - ppos_parton[0];
2072  /* Then, compute pneg_parton[0] and pneg_parton[1]
2073  * (p^- of quark and antiquark) from the dispersion relation.
2074  * 2 p^+ p^- = m_transverse^2 */
2075  for (int i = 0; i < 2; i++) {
2076  pneg_parton[i] = 0.5 * m_trans[i] * m_trans[i] / ppos_parton[i];
2077  }
2078 
2079  const int status = 1;
2080  int color, anticolor;
2081  ThreeVector three_mom;
2082  ThreeVector transverse_mom;
2083  Pythia8::Vec4 pquark;
2084 
2085  // quark end of the remaining (mesonic) string
2086  color = 1;
2087  anticolor = 0;
2088  /* In the case of baryonic string,
2089  * Quark is coming from the backward end of the remaining string.
2090  * Transverse momentum of the backward end is subtracted
2091  * at this point to keep the remaining string aligned in
2092  * the original longitudinal direction.
2093  * It will be added to the most backward hadron from
2094  * PYTHIA fragmentation.
2095  * In the case of anti-baryonic string,
2096  * Quark is coming from the forward end of the remaining string.
2097  * Transverse momentum of the forward end is subtracted
2098  * at this point to keep the remaining string aligned in
2099  * the original longitudinal direction.
2100  * It will be added to the most forward hadron from
2101  * PYTHIA fragmentation. */
2102  transverse_mom =
2103  bstring > 0 ? evec_basis[1] * (QTrx_string_neg - QTrx_add_neg) +
2104  evec_basis[2] * (QTry_string_neg - QTry_add_neg)
2105  : evec_basis[1] * (QTrx_string_pos - QTrx_add_pos) +
2106  evec_basis[2] * (QTry_string_pos - QTry_add_pos);
2107  three_mom =
2108  evec_basis[0] * (ppos_parton[0] - pneg_parton[0]) * M_SQRT1_2 +
2109  transverse_mom;
2110  const double E_quark =
2111  std::sqrt(m_const[0] * m_const[0] + three_mom.sqr());
2112  pquark = set_Vec4(E_quark, three_mom);
2113  pSum += pquark;
2114  pythia_hadron_->event.append(idqIn[0], status, color, anticolor, pquark,
2115  m_const[0]);
2116 
2117  // antiquark end of the remaining (mesonic) string
2118  color = 0;
2119  anticolor = 1;
2120  /* In the case of baryonic string,
2121  * Antiquark is coming from the forward end of the remaining string.
2122  * Transverse momentum of the forward end is subtracted
2123  * at this point to keep the remaining string aligned in
2124  * the original longitudinal direction.
2125  * It will be added to the most forward hadron from
2126  * PYTHIA fragmentation.
2127  * In the case of anti-baryonic string,
2128  * Antiquark is coming from the backward end of the remaining string.
2129  * Transverse momentum of the backward end is subtracted
2130  * at this point to keep the remaining string aligned in
2131  * the original longitudinal direction.
2132  * It will be added to the most backward hadron from
2133  * PYTHIA fragmentation. */
2134  transverse_mom =
2135  bstring > 0 ? evec_basis[1] * (QTrx_string_pos - QTrx_add_pos) +
2136  evec_basis[2] * (QTry_string_pos - QTry_add_pos)
2137  : evec_basis[1] * (QTrx_string_neg - QTrx_add_neg) +
2138  evec_basis[2] * (QTry_string_neg - QTry_add_neg);
2139  three_mom =
2140  evec_basis[0] * (ppos_parton[1] - pneg_parton[1]) * M_SQRT1_2 +
2141  transverse_mom;
2142  const double E_antiq =
2143  std::sqrt(m_const[1] * m_const[1] + three_mom.sqr());
2144  pquark = set_Vec4(E_antiq, three_mom);
2145  pSum += pquark;
2146  pythia_hadron_->event.append(idqIn[1], status, color, anticolor, pquark,
2147  m_const[1]);
2148  }
2149  } else {
2150  do_string_fragmentation = true;
2151 
2152  ppos_string_new = mString * M_SQRT1_2;
2153  pneg_string_new = mString * M_SQRT1_2;
2154  QTrx_string_new = 0.;
2155  QTry_string_new = 0.;
2156  QTrn_string_new = 0.;
2157  mTrn_string_new = mString;
2158  mass_string_new = mString;
2159 
2160  /* diquark (anti-quark) with PDG id idq2 is going in the direction of
2161  * evecLong.
2162  * quark with PDG id idq1 is going in the direction opposite to evecLong. */
2163  double sign_direction = 1.;
2164  if (bstring == -3) { // anti-baryonic string
2165  /* anti-diquark with PDG id idq1 is going in the direction of evecLong.
2166  * anti-quark with PDG id idq2 is going in the direction
2167  * opposite to evecLong. */
2168  sign_direction = -1;
2169  }
2170 
2171  // evaluate momenta of quarks
2172  const double pCMquark = pCM(mString, m_const[0], m_const[1]);
2173  const double E1 = std::sqrt(m_const[0] * m_const[0] + pCMquark * pCMquark);
2174  const double E2 = std::sqrt(m_const[1] * m_const[1] + pCMquark * pCMquark);
2175 
2176  ThreeVector direction = sign_direction * evecLong;
2177 
2178  // For status and (anti)color see \iref{Sjostrand:2007gs}.
2179  const int status1 = 1, color1 = 1, anticolor1 = 0;
2180  Pythia8::Vec4 pquark = set_Vec4(E1, -direction * pCMquark);
2181  pSum += pquark;
2182  pythia_hadron_->event.append(idqIn[0], status1, color1, anticolor1, pquark,
2183  m_const[0]);
2184 
2185  const int status2 = 1, color2 = 0, anticolor2 = 1;
2186  pquark = set_Vec4(E2, direction * pCMquark);
2187  pSum += pquark;
2188  pythia_hadron_->event.append(idqIn[1], status2, color2, anticolor2, pquark,
2189  m_const[1]);
2190  }
2191 
2192  if (do_string_fragmentation) {
2193  logg[LPythia].debug("fragmenting a string with ", idqIn[0], ", ", idqIn[1]);
2194  // implement PYTHIA fragmentation
2195  pythia_hadron_->event[0].p(pSum);
2196  pythia_hadron_->event[0].m(pSum.mCalc());
2197  bool successful_hadronization = pythia_hadron_->next();
2198  // update_info();
2199  if (!successful_hadronization) {
2200  return 0;
2201  }
2202 
2203  /* Add transverse momenta of string ends to the most forward and
2204  * backward hadrons from PYTHIA fragmentation. */
2205  bool successful_kinematics = remake_kinematics_fragments(
2206  pythia_hadron_->event, evec_basis, ppos_string_new, pneg_string_new,
2207  QTrx_string_new, QTry_string_new, QTrx_add_pos, QTry_add_pos,
2208  QTrx_add_neg, QTry_add_neg);
2209  if (!successful_kinematics) {
2210  return 0;
2211  }
2212 
2213  for (int ipyth = 0; ipyth < pythia_hadron_->event.size(); ipyth++) {
2214  if (!pythia_hadron_->event[ipyth].isFinal()) {
2215  continue;
2216  }
2217  int pythia_id = pythia_hadron_->event[ipyth].id();
2218  /* K_short and K_long need are converted to K0
2219  * since SMASH only knows K0 */
2220  convert_KaonLS(pythia_id);
2221  FourVector momentum(
2222  pythia_hadron_->event[ipyth].e(), pythia_hadron_->event[ipyth].px(),
2223  pythia_hadron_->event[ipyth].py(), pythia_hadron_->event[ipyth].pz());
2224  logg[LPythia].debug("appending the fragmented hadron ", pythia_id,
2225  " to the intermediate particle list.");
2226  bool found_ptype =
2227  append_intermediate_list(pythia_id, momentum, intermediate_particles);
2228  if (!found_ptype) {
2229  logg[LPythia].warn("PDG ID ", pythia_id,
2230  " does not exist in ParticleType - start over.");
2231  intermediate_particles.clear();
2232  return 0;
2233  }
2234 
2235  number_of_fragments++;
2236  }
2237  }
2238  return number_of_fragments;
2239 }
2240 
2242  bool from_forward, bool separate_fragment_baryon,
2243  std::array<ThreeVector, 3> &evec_basis, double &ppos_string,
2244  double &pneg_string, double &QTrx_string_pos, double &QTrx_string_neg,
2245  double &QTry_string_pos, double &QTry_string_neg,
2246  Pythia8::FlavContainer &flav_string_pos,
2247  Pythia8::FlavContainer &flav_string_neg, std::vector<int> &pdgid_frag,
2248  std::vector<FourVector> &momentum_frag) {
2249  /* How many times we try to find flavor of qqbar pair and corresponding
2250  * hadronic species */
2251  const int n_try = 10;
2252  pdgid_frag.clear();
2253  momentum_frag.clear();
2254 
2255  if (ppos_string < 0. || pneg_string < 0.) {
2256  throw std::runtime_error("string has a negative lightcone momentum.");
2257  }
2258  double mTsqr_string = 2. * ppos_string * pneg_string;
2259  // Transverse mass of the original string
2260  double mTrn_string = std::sqrt(mTsqr_string);
2261  // Total transverse momentum of the original string
2262  double QTrx_string_tot = QTrx_string_pos + QTrx_string_neg;
2263  double QTry_string_tot = QTry_string_pos + QTry_string_neg;
2264  double QTsqr_string_tot = std::fabs(QTrx_string_tot * QTrx_string_tot) +
2265  std::fabs(QTry_string_tot * QTry_string_tot);
2266  double QTrn_string_tot = std::sqrt(QTsqr_string_tot);
2267  if (mTrn_string < QTrn_string_tot) {
2268  return 0;
2269  }
2270  // Mass of the original string
2271  double mass_string = std::sqrt(mTsqr_string - QTsqr_string_tot);
2272  logg[LPythia].debug(" Fragment off one hadron from a string ( ",
2273  flav_string_pos.id, " , ", flav_string_neg.id,
2274  " ) with mass ", mass_string, " GeV.");
2275 
2276  // Take relevant parameters from PYTHIA.
2277  const double sigma_qt_frag = pythia_hadron_->parm("StringPT:sigma");
2278  const double stop_string_mass =
2279  pythia_hadron_->parm("StringFragmentation:stopMass");
2280  const double stop_string_smear =
2281  pythia_hadron_->parm("StringFragmentation:stopSmear");
2282 
2283  // Enhance the width of transverse momentum with certain probability
2284  const double prob_enhance_qt =
2285  pythia_hadron_->parm("StringPT:enhancedFraction");
2286  double fac_enhance_qt;
2287  if (random::uniform(0., 1.) < prob_enhance_qt) {
2288  fac_enhance_qt = pythia_hadron_->parm("StringPT:enhancedWidth");
2289  } else {
2290  fac_enhance_qt = 1.;
2291  }
2292 
2293  /* Sample the transverse momentum of newly created quark-antiquark
2294  * (or diquark-antidiquark) pair.
2295  * Note that one constituent carries QT_new while -QT_new is carried by
2296  * another.
2297  * The former one is taken by the (first) fragmented hadron and
2298  * the later one will be assigned to the remaining string or
2299  * taken by the second fragmented hadron. */
2300  double QTrx_new =
2301  random::normal(0., fac_enhance_qt * sigma_qt_frag * M_SQRT1_2);
2302  double QTry_new =
2303  random::normal(0., fac_enhance_qt * sigma_qt_frag * M_SQRT1_2);
2304  logg[LPythia].debug(" Transverse momentum (", QTrx_new, ", ", QTry_new,
2305  ") GeV selected for the new qqbar pair.");
2306 
2307  /* Determine the transverse momentum of the (first) fragmented hadron.
2308  * Transverse momentum of hadron =
2309  * QT_string (of the string end) +
2310  * QT_new (of one of the quark-antiquark pair).
2311  * If the first hadron is fragmented from the forward (backward) end
2312  * of a string, then transverse momentum carried by the forward (backward)
2313  * end is taken. */
2314  double QTrx_had_1st =
2315  from_forward ? QTrx_string_pos + QTrx_new : QTrx_string_neg + QTrx_new;
2316  double QTry_had_1st =
2317  from_forward ? QTry_string_pos + QTry_new : QTry_string_neg + QTry_new;
2318  double QTrn_had_1st =
2319  std::sqrt(QTrx_had_1st * QTrx_had_1st + QTry_had_1st * QTry_had_1st);
2320 
2321  // PDG id of the (first) fragmented hadron
2322  int pdgid_had_1st = 0;
2323  // Mass of the (first) fragmented hadron
2324  double mass_had_1st = 0.;
2325  /* Constituent flavor of the original string end,
2326  * at which the (first) hadron is fragmented. */
2327  Pythia8::FlavContainer flav_old =
2328  from_forward ? flav_string_pos : flav_string_neg;
2329  /* Constituent flavor of newly created quark-antiquark pair,
2330  * which is taken by the (first) fragmented hadron.
2331  * Antiparticle of this flavor will be assigned to the remaining string,
2332  * or taken by the second fragmented hadron. */
2333  Pythia8::FlavContainer flav_new = Pythia8::FlavContainer(0);
2334  /* Sample flavor of the quark-antiquark (or diquark-antidiquark) pair
2335  * and combine with that of the original string end to find the hadronic
2336  * species. */
2337  for (int i_try = 0; i_try < n_try; i_try++) {
2338  // Sample the new flavor.
2339  flav_new = pythia_stringflav_.pick(flav_old);
2340  // Combine to get the PDG id of hadron.
2341  pdgid_had_1st = pythia_stringflav_.combine(flav_old, flav_new);
2342  if (pdgid_had_1st != 0) {
2343  // If the PDG id is found, determine mass.
2344  mass_had_1st = pythia_hadron_->particleData.mSel(pdgid_had_1st);
2345  logg[LPythia].debug(" number of tries of flavor selection : ",
2346  i_try + 1, " in StringProcess::fragment_off_hadron.");
2347  break;
2348  }
2349  }
2350  if (pdgid_had_1st == 0) {
2351  return 0;
2352  }
2353  logg[LPythia].debug(" New flavor ", flav_new.id,
2354  " selected for the string end with ", flav_old.id);
2355  logg[LPythia].debug(" PDG id ", pdgid_had_1st,
2356  " selected for the (first) fragmented hadron.");
2357  bool had_1st_baryon = pythia_hadron_->particleData.isBaryon(pdgid_had_1st);
2358  // Transverse mass of the (first) fragmented hadron
2359  double mTrn_had_1st =
2360  std::sqrt(mass_had_1st * mass_had_1st + QTrn_had_1st * QTrn_had_1st);
2361  logg[LPythia].debug(" Transverse momentum (", QTrx_had_1st, ", ",
2362  QTry_had_1st,
2363  ") GeV selected for the (first) fragmented hadron.");
2364 
2365  /* Compute the mass threshold to continue string fragmentation.
2366  * This formula is taken from StringFragmentation::energyUsedUp
2367  * in StringFragmentation.cc of PYTHIA 8. */
2368  const double mass_min_to_continue =
2369  (stop_string_mass + pythia_hadron_->particleData.m0(flav_new.id) +
2370  pythia_hadron_->particleData.m0(flav_string_pos.id) +
2371  pythia_hadron_->particleData.m0(flav_string_neg.id)) *
2372  (1. + (2. * random::uniform(0., 1.) - 1.) * stop_string_smear);
2373  /* If the string mass is lower than that threshold,
2374  * the string breaks into the last two hadrons. */
2375  bool string_into_final_two = mass_string < mass_min_to_continue;
2376  if (string_into_final_two) {
2377  logg[LPythia].debug(" The string mass is below the mass threshold ",
2378  mass_min_to_continue,
2379  " GeV : finishing with two hadrons.");
2380  }
2381 
2382  // Lightcone momentum of the (first) fragmented hadron
2383  double ppos_had_1st = 0.;
2384  double pneg_had_1st = 0.;
2385 
2386  /* Whether the string end, at which the (first) hadron is fragmented,
2387  * had a diquark or antidiquark */
2388  bool from_diquark_end =
2389  from_forward ? pythia_hadron_->particleData.isDiquark(flav_string_pos.id)
2390  : pythia_hadron_->particleData.isDiquark(flav_string_neg.id);
2391  // Whether the forward end of the string has a diquark
2392  bool has_diquark_pos =
2393  pythia_hadron_->particleData.isDiquark(flav_string_pos.id);
2394 
2395  int n_frag = 0;
2396  if (string_into_final_two) {
2397  /* In the case of a string breaking into the last two hadrons,
2398  * we determine species, mass and four-momentum of the second hadron. */
2399  // PDG id of the second fragmented hadron
2400  int pdgid_had_2nd = 0.;
2401  // Mass of the second fragmented hadron
2402  double mass_had_2nd = 0.;
2403  /* Constituent flavor of newly created quark-antiquark pair,
2404  * which is taken by the second fragmented hadron. */
2405  Pythia8::FlavContainer flav_new2 = Pythia8::FlavContainer(0);
2406  flav_new2.anti(flav_new);
2407  /* Getting a hadron from diquark and antidiquark does not always work.
2408  * So, if this is the case, start over. */
2409  if (pythia_hadron_->particleData.isDiquark(flav_string_neg.id) &&
2410  pythia_hadron_->particleData.isDiquark(flav_new2.id) && from_forward) {
2411  return 0;
2412  }
2413  if (pythia_hadron_->particleData.isDiquark(flav_string_pos.id) &&
2414  pythia_hadron_->particleData.isDiquark(flav_new2.id) && !from_forward) {
2415  return 0;
2416  }
2417  for (int i_try = 0; i_try < n_try; i_try++) {
2418  // Combine to get the PDG id of the second hadron.
2419  pdgid_had_2nd =
2420  from_forward ? pythia_stringflav_.combine(flav_string_neg, flav_new2)
2421  : pythia_stringflav_.combine(flav_string_pos, flav_new2);
2422  if (pdgid_had_2nd != 0) {
2423  // If the PDG id is found, determine mass.
2424  mass_had_2nd = pythia_hadron_->particleData.mSel(pdgid_had_2nd);
2425  break;
2426  }
2427  }
2428  if (pdgid_had_2nd == 0) {
2429  return 0;
2430  }
2431  logg[LPythia].debug(" PDG id ", pdgid_had_2nd,
2432  " selected for the (second) fragmented hadron.");
2433  bool had_2nd_baryon = pythia_hadron_->particleData.isBaryon(pdgid_had_2nd);
2434 
2435  /* Determine transverse momentum carried by the second hadron.
2436  * If the first hadron fragmented from the forward (backward) end
2437  * of a string, transvere momentum at the backward (forward) end will
2438  * contribute.
2439  * Transverse momentum of newly created constituent
2440  * must be added as well. */
2441  double QTrx_had_2nd =
2442  from_forward ? QTrx_string_neg - QTrx_new : QTrx_string_pos - QTrx_new;
2443  double QTry_had_2nd =
2444  from_forward ? QTry_string_neg - QTry_new : QTry_string_pos - QTry_new;
2445  double QTrn_had_2nd =
2446  std::sqrt(QTrx_had_2nd * QTrx_had_2nd + QTry_had_2nd * QTry_had_2nd);
2447  double mTrn_had_2nd =
2448  std::sqrt(mass_had_2nd * mass_had_2nd + QTrn_had_2nd * QTrn_had_2nd);
2449  logg[LPythia].debug(" Transverse momentum (", QTrx_had_2nd, ", ",
2450  QTry_had_2nd,
2451  ") GeV selected for the (second) fragmented hadron.");
2452 
2453  double ppos_had_2nd = 0.;
2454  double pneg_had_2nd = 0.;
2455 
2456  /* Compute lightcone momenta of the final two hadrons.
2457  * If the fragmentation begins at the forward (backward) end of a string,
2458  * the first (second) hadron is the forward one and the second (first)
2459  * hadron is the backward one. */
2460  bool found_kinematics =
2461  from_forward
2463  separate_fragment_baryon && has_diquark_pos && had_1st_baryon,
2464  ppos_string, pneg_string, mTrn_had_1st, mTrn_had_2nd,
2465  ppos_had_1st, ppos_had_2nd, pneg_had_1st, pneg_had_2nd)
2467  separate_fragment_baryon && has_diquark_pos && had_2nd_baryon,
2468  ppos_string, pneg_string, mTrn_had_2nd, mTrn_had_1st,
2469  ppos_had_2nd, ppos_had_1st, pneg_had_2nd, pneg_had_1st);
2470  if (!found_kinematics) {
2471  return 0;
2472  }
2473 
2474  // The entire string breaks into hadrons, so there is no momentum left.
2475  ppos_string = 0.;
2476  pneg_string = 0.;
2477  QTrx_string_pos = 0.;
2478  QTry_string_pos = 0.;
2479 
2480  // Add the first hadron to the list.
2481  pdgid_frag.push_back(pdgid_had_1st);
2482  FourVector mom_had_1st = FourVector(
2483  (ppos_had_1st + pneg_had_1st) * M_SQRT1_2,
2484  evec_basis[0] * (ppos_had_1st - pneg_had_1st) * M_SQRT1_2 +
2485  evec_basis[1] * QTrx_had_1st + evec_basis[2] * QTry_had_1st);
2486  momentum_frag.push_back(mom_had_1st);
2487 
2488  // Add the second hadron to the list.
2489  pdgid_frag.push_back(pdgid_had_2nd);
2490  FourVector mom_had_2nd = FourVector(
2491  (ppos_had_2nd + pneg_had_2nd) * M_SQRT1_2,
2492  evec_basis[0] * (ppos_had_2nd - pneg_had_2nd) * M_SQRT1_2 +
2493  evec_basis[1] * QTrx_had_2nd + evec_basis[2] * QTry_had_2nd);
2494  momentum_frag.push_back(mom_had_2nd);
2495 
2496  n_frag += 2;
2497  } else {
2498  /* If the string mass is large enough (larger than the threshold),
2499  * perform the normal fragmentation.
2500  * Different sets of parameters for the LUND fragmentation function
2501  * are used, depending on whether the (first) fragmented hadrons is
2502  * the leading baryon. */
2503  double stringz_a_use, stringz_b_use;
2504  /* If the firstly fragmented hadron from the diquark end is a baryon,
2505  * it can be considered to be the leading baryon. */
2506  if (separate_fragment_baryon && from_diquark_end && had_1st_baryon) {
2507  stringz_a_use = stringz_a_leading_;
2508  stringz_b_use = stringz_b_leading_;
2509  } else {
2510  stringz_a_use = stringz_a_produce_;
2511  stringz_b_use = stringz_b_produce_;
2512  }
2513 
2514  /* Sample the lightcone momentum fraction from
2515  * the LUND fragmentation function. */
2516  double xfrac = sample_zLund(stringz_a_use, stringz_b_use, mTrn_had_1st);
2517  if (from_forward) {
2518  /* If the (first) hadron is fragmented from the forward end,
2519  * it is the lightcone momentum fraction of p^+ */
2520  ppos_had_1st = xfrac * ppos_string;
2521  pneg_had_1st = 0.5 * mTrn_had_1st * mTrn_had_1st / ppos_had_1st;
2522  if (pneg_had_1st > pneg_string) {
2523  return 0;
2524  }
2525  } else {
2526  /* If the (first) hadron is fragmented from the backward end,
2527  * it is the lightcone momentum fraction of p^- */
2528  pneg_had_1st = xfrac * pneg_string;
2529  ppos_had_1st = 0.5 * mTrn_had_1st * mTrn_had_1st / pneg_had_1st;
2530  if (ppos_had_1st > ppos_string) {
2531  return 0;
2532  }
2533  }
2534 
2535  // Add PDG id and four-momentum of the (first) hadron to the list.
2536  pdgid_frag.push_back(pdgid_had_1st);
2537  FourVector mom_had_1st = FourVector(
2538  (ppos_had_1st + pneg_had_1st) * M_SQRT1_2,
2539  evec_basis[0] * (ppos_had_1st - pneg_had_1st) * M_SQRT1_2 +
2540  evec_basis[1] * QTrx_had_1st + evec_basis[2] * QTry_had_1st);
2541  momentum_frag.push_back(mom_had_1st);
2542 
2543  // Update lightcone momentum of the string.
2544  ppos_string -= ppos_had_1st;
2545  pneg_string -= pneg_had_1st;
2546  /* Update flavor and transverse momentum of the string end,
2547  * from which the (first) hadron is fragmented.
2548  * Flavor of the new string end is antiparticle of
2549  * the constituent taken by the hadron.
2550  * Transverse momentum of the new string end is opposite to that
2551  * of the constituent taken by the hadron. */
2552  if (from_forward) {
2553  /* Update the forward end of the string
2554  * if hadron is fragmented from there. */
2555  flav_string_pos.anti(flav_new);
2556  QTrx_string_pos = -QTrx_new;
2557  QTry_string_pos = -QTry_new;
2558  } else {
2559  /* Update the backward end of the string
2560  * if hadron is fragmented from there. */
2561  flav_string_neg.anti(flav_new);
2562  QTrx_string_neg = -QTrx_new;
2563  QTry_string_neg = -QTry_new;
2564  }
2565 
2566  n_frag += 1;
2567  }
2568 
2569  return n_frag;
2570 }
2571 
2573  const int baryon_number =
2574  pythia_hadron_->particleData.baryonNumberType(idq1) +
2575  pythia_hadron_->particleData.baryonNumberType(idq2);
2576 
2577  int pdgid_hadron = 0;
2578  /* PDG id of the leading baryon from valence quark constituent.
2579  * First, try with the PYTHIA machinary. */
2580  Pythia8::FlavContainer flav1 = Pythia8::FlavContainer(idq1);
2581  Pythia8::FlavContainer flav2 = Pythia8::FlavContainer(idq2);
2582  const int n_try = 10;
2583  for (int i_try = 0; i_try < n_try; i_try++) {
2584  pdgid_hadron = pythia_stringflav_.combine(flav1, flav2);
2585  if (pdgid_hadron != 0) {
2586  return pdgid_hadron;
2587  }
2588  }
2589 
2590  /* If PYTHIA machinary does not work, determine type of the leading baryon
2591  * based on the quantum numbers and mass. */
2592 
2593  // net quark number of d, u, s, c and b flavors
2594  std::array<int, 5> frag_net_q;
2595  /* Evaluate total net quark number of baryon (antibaryon)
2596  * from the valence quark constituents. */
2597  for (int iq = 0; iq < 5; iq++) {
2598  int nq1 =
2599  pythia_hadron_->particleData.nQuarksInCode(std::abs(idq1), iq + 1);
2600  int nq2 =
2601  pythia_hadron_->particleData.nQuarksInCode(std::abs(idq2), iq + 1);
2602  nq1 = idq1 > 0 ? nq1 : -nq1;
2603  nq2 = idq2 > 0 ? nq2 : -nq2;
2604  frag_net_q[iq] = nq1 + nq2;
2605  }
2606  const int frag_iso3 = frag_net_q[1] - frag_net_q[0];
2607  const int frag_strange = -frag_net_q[2];
2608  const int frag_charm = frag_net_q[3];
2609  const int frag_bottom = -frag_net_q[4];
2610  logg[LPythia].debug(" conserved charges : iso3 = ", frag_iso3,
2611  ", strangeness = ", frag_strange,
2612  ", charmness = ", frag_charm,
2613  ", bottomness = ", frag_bottom);
2614 
2615  std::vector<int> pdgid_possible;
2616  std::vector<double> weight_possible;
2617  std::vector<double> weight_summed;
2618  /* loop over hadronic species
2619  * Any hadron with the same valence quark contents is allowed and
2620  * the probability goes like spin degeneracy over mass. */
2621  for (auto &ptype : ParticleType::list_all()) {
2622  if (!ptype.is_hadron()) {
2623  continue;
2624  }
2625  const int pdgid = ptype.pdgcode().get_decimal();
2626  if ((pythia_hadron_->particleData.isParticle(pdgid)) &&
2627  (baryon_number == 3 * ptype.pdgcode().baryon_number()) &&
2628  (frag_iso3 == ptype.pdgcode().isospin3()) &&
2629  (frag_strange == ptype.pdgcode().strangeness()) &&
2630  (frag_charm == ptype.pdgcode().charmness()) &&
2631  (frag_bottom == ptype.pdgcode().bottomness())) {
2632  const int spin_degeneracy = ptype.pdgcode().spin_degeneracy();
2633  const double mass_pole = ptype.mass();
2634  const double weight = static_cast<double>(spin_degeneracy) / mass_pole;
2635  pdgid_possible.push_back(pdgid);
2636  weight_possible.push_back(weight);
2637 
2638  logg[LPythia].debug(" PDG id ", pdgid, " is possible with weight ",
2639  weight);
2640  }
2641  }
2642  const int n_possible = pdgid_possible.size();
2643  weight_summed.push_back(0.);
2644  for (int i = 0; i < n_possible; i++) {
2645  weight_summed.push_back(weight_summed[i] + weight_possible[i]);
2646  }
2647 
2648  /* Sample baryon (antibaryon) specie,
2649  * which is fragmented from the leading diquark (anti-diquark). */
2650  const double uspc = random::uniform(0., weight_summed[n_possible]);
2651  for (int i = 0; i < n_possible; i++) {
2652  if ((uspc >= weight_summed[i]) && (uspc < weight_summed[i + 1])) {
2653  return pdgid_possible[i];
2654  }
2655  }
2656 
2657  return 0;
2658 }
2659 
2660 int StringProcess::get_resonance_from_quark(int idq1, int idq2, double mass) {
2661  // if the mass is too low, return 0 (failure).
2662  if (mass < pion_mass) {
2663  return 0;
2664  }
2665 
2666  /* It checks whether one has a valid input
2667  * the string ends. */
2668 
2669  // idq1 is supposed to be a quark or anti-diquark.
2670  bool end1_is_quark = idq1 > 0 && pythia_hadron_->particleData.isQuark(idq1);
2671  bool end1_is_antidiq =
2672  idq1 < 0 && pythia_hadron_->particleData.isDiquark(idq1);
2673  // idq2 is supposed to be a anti-quark or diquark.
2674  bool end2_is_antiq = idq2 < 0 && pythia_hadron_->particleData.isQuark(idq2);
2675  bool end2_is_diquark =
2676  idq2 > 0 && pythia_hadron_->particleData.isDiquark(idq2);
2677 
2678  int baryon;
2679  if (end1_is_quark) {
2680  if (end2_is_antiq) {
2681  // we have a mesonic resonance from a quark-antiquark pair.
2682  baryon = 0;
2683  } else if (end2_is_diquark) {
2684  // we have a baryonic resonance from a quark-diquark pair.
2685  baryon = 1;
2686  } else {
2687  return 0;
2688  }
2689  } else if (end1_is_antidiq) {
2690  if (end2_is_antiq) {
2691  // we have a antibaryonic resonance from a antiquark-antidiquark pair.
2692  baryon = -1;
2693  } else {
2694  return 0;
2695  }
2696  } else {
2697  return 0;
2698  }
2699 
2700  /* array for the net quark numbers of the constituents.
2701  * net_qnumber[0, 1, 2, 3 and 4] correspond respectively to
2702  * d, u, s, c, b quark flavors. */
2703  std::array<int, 5> net_qnumber;
2704  for (int iflav = 0; iflav < 5; iflav++) {
2705  net_qnumber[iflav] = 0;
2706 
2707  int qnumber1 =
2708  pythia_hadron_->particleData.nQuarksInCode(std::abs(idq1), iflav + 1);
2709  if (idq1 < 0) {
2710  // anti-diquark gets an extra minus sign.
2711  qnumber1 = -qnumber1;
2712  }
2713  net_qnumber[iflav] += qnumber1;
2714 
2715  int qnumber2 =
2716  pythia_hadron_->particleData.nQuarksInCode(std::abs(idq2), iflav + 1);
2717  if (idq2 < 0) {
2718  // anti-quark gets an extra minus sign.
2719  qnumber2 = -qnumber2;
2720  }
2721  net_qnumber[iflav] += qnumber2;
2722  }
2723 
2724  // List of PDG ids of resonances with the same quantum number.
2725  std::vector<int> pdgid_possible;
2726  // Corresponding mass differences.
2727  std::vector<double> mass_diff;
2728  for (auto &ptype : ParticleType::list_all()) {
2729  if (!ptype.is_hadron() || ptype.is_stable() ||
2730  ptype.pdgcode().baryon_number() != baryon) {
2731  // Only resonances with the same baryon number are considered.
2732  continue;
2733  }
2734  const int pdgid = ptype.pdgcode().get_decimal();
2735  const double mass_min = ptype.min_mass_spectral();
2736  if (mass < mass_min) {
2737  // A resoance with mass lower than its minimum threshold is not allowed.
2738  continue;
2739  }
2740 
2741  if (ptype.pdgcode().isospin3() != net_qnumber[1] - net_qnumber[0]) {
2742  // check isospin3.
2743  continue;
2744  }
2745  if (ptype.pdgcode().strangeness() != -net_qnumber[2]) {
2746  // check strangeness.
2747  continue;
2748  }
2749  if (ptype.pdgcode().charmness() != net_qnumber[3]) {
2750  // check charmness.
2751  continue;
2752  }
2753  if (ptype.pdgcode().bottomness() != -net_qnumber[4]) {
2754  // check bottomness.
2755  continue;
2756  }
2757 
2758  const double mass_pole = ptype.mass();
2759  // Add the PDG id and mass difference to the vector array.
2760  pdgid_possible.push_back(pdgid);
2761  mass_diff.push_back(mass - mass_pole);
2762  }
2763 
2764  const int n_res = pdgid_possible.size();
2765  if (n_res == 0) {
2766  // If there is no possible resonance found, return 0 (failure).
2767  return 0;
2768  }
2769 
2770  int ires_closest = 0;
2771  double mass_diff_min = std::fabs(mass_diff[0]);
2772  /* Find a resonance whose pole mass is closest to
2773  * the input mass. */
2774  for (int ires = 1; ires < n_res; ires++) {
2775  if (std::fabs(mass_diff[ires]) < mass_diff_min) {
2776  ires_closest = ires;
2777  mass_diff_min = mass_diff[ires];
2778  }
2779  }
2780  logg[LPythia].debug("Quark constituents ", idq1, " and ", idq2, " with mass ",
2781  mass, " (GeV) turned into a resonance ",
2782  pdgid_possible[ires_closest]);
2783  return pdgid_possible[ires_closest];
2784 }
2785 
2787  bool separate_fragment_hadron, double ppos_string, double pneg_string,
2788  double mTrn_had_forward, double mTrn_had_backward, double &ppos_had_forward,
2789  double &ppos_had_backward, double &pneg_had_forward,
2790  double &pneg_had_backward) {
2791  const double mTsqr_string = 2. * ppos_string * pneg_string;
2792  if (mTsqr_string < 0.) {
2793  return false;
2794  }
2795  const double mTrn_string = std::sqrt(mTsqr_string);
2796  if (mTrn_string < mTrn_had_forward + mTrn_had_backward) {
2797  return false;
2798  }
2799 
2800  // square of transvere mass of the forward hadron
2801  const double mTsqr_had_forward = mTrn_had_forward * mTrn_had_forward;
2802  // square of transvere mass of the backward hadron
2803  const double mTsqr_had_backward = mTrn_had_backward * mTrn_had_backward;
2804 
2805  /* The following part determines lightcone momentum fraction of p^+
2806  * carried by each hadron.
2807  * Lightcone momenta of the forward and backward hadrons are
2808  * p^+ forward = (xe_pos + xpz_pos) * p^+ string,
2809  * p^- forward = (xe_pos - xpz_pos) * p^- string,
2810  * p^+ backward = (xe_neg - xpz_pos) * p^+ string and
2811  * p^- backward = (xe_neg + xpz_pos) * p^- string.
2812  * where xe_pos and xe_neg satisfy xe_pos + xe_neg = 1.
2813  * Then evaluate xe_pos, xe_neg and xpz_pos in terms of
2814  * the transverse masses of hadrons and string. */
2815 
2816  // Express xe_pos and xe_neg in terms of the transverse masses.
2817  const double xm_diff =
2818  (mTsqr_had_forward - mTsqr_had_backward) / mTsqr_string;
2819  const double xe_pos = 0.5 * (1. + xm_diff);
2820  const double xe_neg = 0.5 * (1. - xm_diff);
2821 
2822  // Express xpz_pos in terms of the transverse masses.
2823  const double lambda_sqr =
2824  pow_int(mTsqr_string - mTsqr_had_forward - mTsqr_had_backward, 2) -
2825  4. * mTsqr_had_forward * mTsqr_had_backward;
2826  if (lambda_sqr <= 0.) {
2827  return false;
2828  }
2829  const double lambda = std::sqrt(lambda_sqr);
2830  const double b_lund =
2831  separate_fragment_hadron ? stringz_b_leading_ : stringz_b_produce_;
2832  /* The probability to flip sign of xpz_pos is taken from
2833  * StringFragmentation::finalTwo in StringFragmentation.cc
2834  * of PYTHIA 8. */
2835  const double prob_reverse =
2836  std::exp(-b_lund * lambda) / (1. + std::exp(-b_lund * lambda));
2837  double xpz_pos = 0.5 * lambda / mTsqr_string;
2838  if (random::uniform(0., 1.) < prob_reverse) {
2839  xpz_pos = -xpz_pos;
2840  }
2841 
2842  ppos_had_forward = (xe_pos + xpz_pos) * ppos_string;
2843  ppos_had_backward = (xe_neg - xpz_pos) * ppos_string;
2844 
2845  pneg_had_forward = 0.5 * mTsqr_had_forward / ppos_had_forward;
2846  pneg_had_backward = 0.5 * mTsqr_had_backward / ppos_had_backward;
2847 
2848  return true;
2849 }
2850 
2851 double StringProcess::sample_zLund(double a, double b, double mTrn) {
2852  // the lightcone momentum fraction x
2853  double xfrac = 0.;
2854  bool xfrac_accepted = false;
2855  /* First sample the inverse 1/x of the lightcone momentum fraction.
2856  * Then, obtain x itself.
2857  * The probability distribution function for the inverse of x is
2858  * PDF(u = 1/x) = (1/u) * (1 - 1/u)^a * exp(-b * mTrn^2 * u)
2859  * with 1 < u < infinity.
2860  * The rejection method can be used with an envelop function
2861  * ENV(u) = exp(-b * mTrn^2 * u). */
2862  while (!xfrac_accepted) {
2863  const double fac_env = b * mTrn * mTrn;
2864  const double u_aux = random::uniform(0., 1.);
2865  /* Sample u = 1/x according to the envelop function
2866  * ENV(u) = exp(-b * mTrn^2 * u). */
2867  const double xfrac_inv = 1. - std::log(u_aux) / fac_env;
2868  assert(xfrac_inv >= 1.);
2869  /* Evaluate the ratio of the real probability distribution function to
2870  * the envelop function. */
2871  const double xf_ratio = std::pow(1. - 1. / xfrac_inv, a) / xfrac_inv;
2872  // Determine whether the sampled value will be accepted.
2873  if (random::uniform(0., 1.) <= xf_ratio) {
2874  /* If the sampled value of 1/x is accepted,
2875  * obtain the value of x. */
2876  xfrac = 1. / xfrac_inv;
2877  xfrac_accepted = true;
2878  }
2879  }
2880  return xfrac;
2881 }
2882 
2884  Pythia8::Event &event_fragments, std::array<ThreeVector, 3> &evec_basis,
2885  double ppos_string, double pneg_string, double QTrx_string,
2886  double QTry_string, double QTrx_add_pos, double QTry_add_pos,
2887  double QTrx_add_neg, double QTry_add_neg) {
2888  logg[LPythia].debug("Correcting the kinematics of fragmented hadrons...");
2889 
2890  if (ppos_string < 0. || pneg_string < 0.) {
2891  logg[LPythia].debug(
2892  " wrong lightcone momenta of string : ppos_string (GeV) = ",
2893  ppos_string, " pneg_string (GeV) = ", pneg_string);
2894  return false;
2895  }
2896  // Momentum rapidity of the final string
2897  const double yrapid_string = 0.5 * std::log(ppos_string / pneg_string);
2898  logg[LOutput].debug("Momentum-space rapidity of the string should be ",
2899  yrapid_string);
2900 
2901  // Transverse mass of the final string
2902  const double mTrn_string = std::sqrt(2. * ppos_string * pneg_string);
2903  logg[LOutput].debug("Transvere mass (GeV) of the string should be ",
2904  mTrn_string);
2905  // Transverse momentum of the final string
2906  const double QTrn_string =
2907  std::sqrt(QTrx_string * QTrx_string + QTry_string * QTry_string);
2908  if (mTrn_string < QTrn_string) {
2909  logg[LOutput].debug(
2910  " wrong transverse mass of string : mTrn_string (GeV) = ", mTrn_string,
2911  " QTrn_string (GeV) = ", QTrn_string);
2912  return false;
2913  }
2914  const double msqr_string =
2915  mTrn_string * mTrn_string - QTrn_string * QTrn_string;
2916  // Mass of the final string
2917  const double mass_string = std::sqrt(msqr_string);
2918  logg[LOutput].debug("The string mass (GeV) should be ", mass_string);
2919 
2920  /* If there is no transverse momentum to be added to the string ends,
2921  * skip the entire procedure and return. */
2922  if (std::fabs(QTrx_add_pos) < small_number * mass_string &&
2923  std::fabs(QTry_add_pos) < small_number * mass_string &&
2924  std::fabs(QTrx_add_neg) < small_number * mass_string &&
2925  std::fabs(QTry_add_neg) < small_number * mass_string) {
2926  logg[LOutput].debug(" no need to add transverse momenta - skipping.");
2927  return true;
2928  }
2929 
2930  FourVector ptot_string_ini = FourVector(0., 0., 0., 0.);
2931  // Compute total four-momentum of the initial string.
2932  for (int ipyth = 1; ipyth < event_fragments.size(); ipyth++) {
2933  if (!event_fragments[ipyth].isFinal()) {
2934  continue;
2935  }
2936 
2937  FourVector p_frag =
2938  FourVector(event_fragments[ipyth].e(), event_fragments[ipyth].px(),
2939  event_fragments[ipyth].py(), event_fragments[ipyth].pz());
2940  ptot_string_ini += p_frag;
2941  }
2942  const double E_string_ini = ptot_string_ini.x0();
2943  const double pz_string_ini = ptot_string_ini.threevec() * evec_basis[0];
2944  const double ppos_string_ini = (E_string_ini + pz_string_ini) * M_SQRT1_2;
2945  const double pneg_string_ini = (E_string_ini - pz_string_ini) * M_SQRT1_2;
2946  // Compute the momentum rapidity of the initial string.
2947  const double yrapid_string_ini =
2948  0.5 * std::log(ppos_string_ini / pneg_string_ini);
2949  /* Then, boost into the frame in which string is at rest in the
2950  * longitudinal direction. */
2951  shift_rapidity_event(event_fragments, evec_basis, 1., -yrapid_string_ini);
2952 
2953  int ip_forward = 0;
2954  int ip_backward = 0;
2955  double y_forward = 0.;
2956  double y_backward = 0.;
2957  ptot_string_ini = FourVector(0., 0., 0., 0.);
2958  // Find the most forward and backward hadrons based on the momentum rapidity.
2959  for (int ipyth = 1; ipyth < event_fragments.size(); ipyth++) {
2960  if (!event_fragments[ipyth].isFinal()) {
2961  continue;
2962  }
2963 
2964  FourVector p_frag =
2965  FourVector(event_fragments[ipyth].e(), event_fragments[ipyth].px(),
2966  event_fragments[ipyth].py(), event_fragments[ipyth].pz());
2967  ptot_string_ini += p_frag;
2968 
2969  const double E_frag = p_frag.x0();
2970  const double pl_frag = p_frag.threevec() * evec_basis[0];
2971  double y_current = 0.5 * std::log((E_frag + pl_frag) / (E_frag - pl_frag));
2972  if (y_current > y_forward) {
2973  ip_forward = ipyth;
2974  y_forward = y_current;
2975  }
2976  if (y_current < y_backward) {
2977  ip_backward = ipyth;
2978  y_backward = y_current;
2979  }
2980  }
2981  logg[LOutput].debug(" The most forward hadron is ip_forward = ", ip_forward,
2982  " with rapidity ", y_forward);
2983  logg[LOutput].debug(" The most backward hadron is ip_backward = ",
2984  ip_backward, " with rapidity ", y_backward);
2985 
2986  const double px_string_ini = ptot_string_ini.threevec() * evec_basis[1];
2987  const double py_string_ini = ptot_string_ini.threevec() * evec_basis[2];
2988 
2989  /* Check if the transverse momentum px is conserved i.e.,
2990  * px of the initial string + px to be added = px of the final string */
2991  bool correct_px = std::fabs(px_string_ini + QTrx_add_pos + QTrx_add_neg -
2992  QTrx_string) < small_number * mass_string;
2993  if (!correct_px) {
2994  logg[LOutput].debug(
2995  " input transverse momenta in x-axis are not consistent.");
2996  return false;
2997  }
2998  /* Check if the transverse momentum py is conserved i.e.,
2999  * py of the initial string + py to be added = py of the final string */
3000  bool correct_py = std::fabs(py_string_ini + QTry_add_pos + QTry_add_neg -
3001  QTry_string) < small_number * mass_string;
3002  if (!correct_py) {
3003  logg[LOutput].debug(
3004  " input transverse momenta in y-axis are not consistent.");
3005  return false;
3006  }
3007 
3008  Pythia8::Vec4 pvec_string_now =
3009  set_Vec4(ptot_string_ini.x0(), ptot_string_ini.threevec());
3010 
3011  logg[LOutput].debug(
3012  " Adding transverse momentum to the most forward hadron.");
3013  pvec_string_now -= event_fragments[ip_forward].p();
3014  const double mass_frag_pos = event_fragments[ip_forward].p().mCalc();
3015  // Four-momentum of the most forward hadron
3016  FourVector p_old_frag_pos = FourVector(
3017  event_fragments[ip_forward].e(), event_fragments[ip_forward].px(),
3018  event_fragments[ip_forward].py(), event_fragments[ip_forward].pz());
3019  // Add transverse momentum to it.
3020  ThreeVector mom_new_frag_pos = p_old_frag_pos.threevec() +
3021  QTrx_add_pos * evec_basis[1] +
3022  QTry_add_pos * evec_basis[2];
3023  // Re-calculate the energy.
3024  double E_new_frag_pos =
3025  std::sqrt(mom_new_frag_pos.sqr() + mass_frag_pos * mass_frag_pos);
3026  Pythia8::Vec4 pvec_new_frag_pos = set_Vec4(E_new_frag_pos, mom_new_frag_pos);
3027  pvec_string_now += pvec_new_frag_pos;
3028  // Update the event record.
3029  event_fragments[ip_forward].p(pvec_new_frag_pos);
3030 
3031  logg[LOutput].debug(
3032  " Adding transverse momentum to the most backward hadron.");
3033  pvec_string_now -= event_fragments[ip_backward].p();
3034  const double mass_frag_neg = event_fragments[ip_backward].p().mCalc();
3035  // Four-momentum of the most backward hadron
3036  FourVector p_old_frag_neg = FourVector(
3037  event_fragments[ip_backward].e(), event_fragments[ip_backward].px(),
3038  event_fragments[ip_backward].py(), event_fragments[ip_backward].pz());
3039  // Add transverse momentum to it.
3040  ThreeVector mom_new_frag_neg = p_old_frag_neg.threevec() +
3041  QTrx_add_neg * evec_basis[1] +
3042  QTry_add_neg * evec_basis[2];
3043  // Re-calculate the energy.
3044  double E_new_frag_neg =
3045  std::sqrt(mom_new_frag_neg.sqr() + mass_frag_neg * mass_frag_neg);
3046  Pythia8::Vec4 pvec_new_frag_neg = set_Vec4(E_new_frag_neg, mom_new_frag_neg);
3047  pvec_string_now += pvec_new_frag_neg;
3048  // Update the event record.
3049  event_fragments[ip_backward].p(pvec_new_frag_neg);
3050 
3051  // Update the event record with total four-momentum of the current string.
3052  event_fragments[0].p(pvec_string_now);
3053  event_fragments[0].m(pvec_string_now.mCalc());
3054 
3055  // Sum of transverse masses of all fragmented hadrons.
3056  double mTrn_frag_all = 0.;
3057  for (int ipyth = 1; ipyth < event_fragments.size(); ipyth++) {
3058  if (!event_fragments[ipyth].isFinal()) {
3059  continue;
3060  }
3061 
3062  FourVector p_frag =
3063  FourVector(event_fragments[ipyth].e(), event_fragments[ipyth].px(),
3064  event_fragments[ipyth].py(), event_fragments[ipyth].pz());
3065  ptot_string_ini += p_frag;
3066 
3067  const double E_frag = p_frag.x0();
3068  const double pl_frag = p_frag.threevec() * evec_basis[0];
3069  const double ppos_frag = (E_frag + pl_frag) * M_SQRT1_2;
3070  const double pneg_frag = (E_frag - pl_frag) * M_SQRT1_2;
3071  const double mTrn_frag = std::sqrt(2. * ppos_frag * pneg_frag);
3072  mTrn_frag_all += mTrn_frag;
3073  }
3074  logg[LOutput].debug(
3075  "Sum of transverse masses (GeV) of all fragmented hadrons : ",
3076  mTrn_frag_all);
3077  /* If the transverse mass of the (final) string is smaller than
3078  * the sum of transverse masses, kinematics cannot be determined. */
3079  if (mTrn_string < mTrn_frag_all) {
3080  logg[LOutput].debug(" which is larger than mT of the actual string ",
3081  mTrn_string);
3082  return false;
3083  }
3084 
3085  double mass_string_now = pvec_string_now.mCalc();
3086  double msqr_string_now = mass_string_now * mass_string_now;
3087  // Total four-momentum of the current string
3088  FourVector p_string_now =
3089  FourVector(pvec_string_now.e(), pvec_string_now.px(),
3090  pvec_string_now.py(), pvec_string_now.pz());
3091  double E_string_now = p_string_now.x0();
3092  double pz_string_now = p_string_now.threevec() * evec_basis[0];
3093  logg[LOutput].debug("The string mass (GeV) at this point : ",
3094  mass_string_now);
3095  double ppos_string_now = (E_string_now + pz_string_now) * M_SQRT1_2;
3096  double pneg_string_now = (E_string_now - pz_string_now) * M_SQRT1_2;
3097  // Momentum rapidity of the current string
3098  double yrapid_string_now = 0.5 * std::log(ppos_string_now / pneg_string_now);
3099  logg[LOutput].debug("The momentum-space rapidity of string at this point : ",
3100  yrapid_string_now);
3101  logg[LOutput].debug(
3102  "The momentum-space rapidities of hadrons will be changed.");
3103  const int niter_max = 10000;
3104  bool accepted = false;
3105  double fac_all_yrapid = 1.;
3106  /* Rescale momentum rapidities of hadrons by replacing
3107  * y_hadron with y_string_now + fac_yrapid * (y_hadron - y_string_now).
3108  * This is done iteratively by finding the value of fac_yrapid which gives
3109  * the correct string mass. */
3110  for (int iiter = 0; iiter < niter_max; iiter++) {
3111  if (std::fabs(mass_string_now - mass_string) < really_small * mass_string) {
3112  accepted = true;
3113  break;
3114  }
3115  double E_deriv = 0.;
3116  double pz_deriv = 0.;
3117 
3118  /* Have a Taylor series of mass square as a linear function
3119  * of fac_yrapid and find a trial value of fac_yrapid. */
3120  for (int ipyth = 1; ipyth < event_fragments.size(); ipyth++) {
3121  if (!event_fragments[ipyth].isFinal()) {
3122  continue;
3123  }
3124 
3125  FourVector p_frag =
3126  FourVector(event_fragments[ipyth].e(), event_fragments[ipyth].px(),
3127  event_fragments[ipyth].py(), event_fragments[ipyth].pz());
3128  const double E_frag = p_frag.x0();
3129  const double pl_frag = p_frag.threevec() * evec_basis[0];
3130  const double ppos_frag = (E_frag + pl_frag) * M_SQRT1_2;
3131  const double pneg_frag = (E_frag - pl_frag) * M_SQRT1_2;
3132  const double mTrn_frag = std::sqrt(2. * ppos_frag * pneg_frag);
3133  const double y_frag = 0.5 * std::log(ppos_frag / pneg_frag);
3134 
3135  E_deriv += mTrn_frag * (y_frag - yrapid_string_now) * std::sinh(y_frag);
3136  pz_deriv += mTrn_frag * (y_frag - yrapid_string_now) * std::cosh(y_frag);
3137  }
3138  double slope = 2. * (E_string_now * E_deriv - pz_string_now * pz_deriv);
3139  double fac_yrapid = 1. + std::tanh((msqr_string - msqr_string_now) / slope);
3140  fac_all_yrapid *= fac_yrapid;
3141 
3142  // Replace momentum rapidities of hadrons.
3143  shift_rapidity_event(event_fragments, evec_basis, fac_yrapid,
3144  (1. - fac_yrapid) * yrapid_string_now);
3145  // Update the four-momentum and mass of the current string
3146  pvec_string_now = event_fragments[0].p();
3147  mass_string_now = pvec_string_now.mCalc();
3148  msqr_string_now = mass_string_now * mass_string_now;
3149  p_string_now = FourVector(pvec_string_now.e(), pvec_string_now.px(),
3150  pvec_string_now.py(), pvec_string_now.pz());
3151  E_string_now = p_string_now.x0();
3152  pz_string_now = p_string_now.threevec() * evec_basis[0];
3153  ppos_string_now = (E_string_now + pz_string_now) * M_SQRT1_2;
3154  pneg_string_now = (E_string_now - pz_string_now) * M_SQRT1_2;
3155  yrapid_string_now = 0.5 * std::log(ppos_string_now / pneg_string_now);
3156  logg[LOutput].debug(" step ", iiter + 1, " : fac_yrapid = ", fac_yrapid,
3157  " , string mass (GeV) = ", mass_string_now,
3158  " , string rapidity = ", yrapid_string_now);
3159  }
3160 
3161  if (!accepted) {
3162  logg[LOutput].debug(" Too many iterations in rapidity rescaling.");
3163  return false;
3164  }
3165  logg[LOutput].debug(
3166  "The overall factor multiplied to the rapidities of hadrons = ",
3167  fac_all_yrapid);
3168  logg[LOutput].debug("The momentum-space rapidity of string at this point : ",
3169  yrapid_string_now);
3170  const double y_diff = yrapid_string - yrapid_string_now;
3171  logg[LOutput].debug("The hadrons will be boosted by rapidity ", y_diff,
3172  " for the longitudinal momentum conservation.");
3173 
3174  // Boost the hadrons back into the original frame.
3175  shift_rapidity_event(event_fragments, evec_basis, 1., y_diff);
3176 
3177  return true;
3178 }
3179 
3181  double suppression_factor) {
3182  int nbaryon = data.pdgcode().baryon_number();
3183  if (nbaryon == 0) {
3184  // Mesons always get a scaling factor of 1/2 since there is never
3185  // a q-qbar pair at the end of a string so nquark is always 1
3186  data.set_cross_section_scaling_factor(0.5 * suppression_factor);
3187  } else if (data.is_baryon()) {
3188  // Leading baryons get a factor of 2/3 if they carry 2
3189  // and 1/3 if they carry 1 of the strings valence quarks
3190  data.set_cross_section_scaling_factor(suppression_factor * nquark /
3191  (3.0 * nbaryon));
3192  }
3193 }
3194 
3195 std::pair<int, int> StringProcess::find_leading(int nq1, int nq2,
3196  ParticleList &list) {
3197  assert(list.size() >= 2);
3198  int end = list.size() - 1;
3199  int i1, i2;
3200  for (i1 = 0;
3201  i1 <= end && !list[i1].pdgcode().contains_enough_valence_quarks(nq1);
3202  i1++) {
3203  }
3204  for (i2 = end;
3205  i2 >= 0 && !list[i2].pdgcode().contains_enough_valence_quarks(nq2);
3206  i2--) {
3207  }
3208  std::pair<int, int> indices(i1, i2);
3209  return indices;
3210 }
3211 
3213  ParticleList &outgoing_particles,
3214  const ThreeVector &evecLong,
3215  double suppression_factor) {
3216  // Set each particle's cross section scaling factor to 0 first
3217  for (ParticleData &data : outgoing_particles) {
3218  data.set_cross_section_scaling_factor(0.0);
3219  }
3220  // sort outgoing particles according to the longitudinal velocity
3221  std::sort(outgoing_particles.begin(), outgoing_particles.end(),
3222  [&](ParticleData i, ParticleData j) {
3223  return i.momentum().velocity() * evecLong >
3224  j.momentum().velocity() * evecLong;
3225  });
3226  int nq1, nq2; // number of quarks at both ends of the string
3227  switch (baryon_string) {
3228  case 0:
3229  nq1 = -1;
3230  nq2 = 1;
3231  break;
3232  case 1:
3233  nq1 = 2;
3234  nq2 = 1;
3235  break;
3236  case -1:
3237  nq1 = -2;
3238  nq2 = -1;
3239  break;
3240  default:
3241  throw std::runtime_error("string is neither mesonic nor baryonic");
3242  }
3243  // Try to find nq1 on one string end and nq2 on the other string end and the
3244  // other way around. When the leading particles are close to the string ends,
3245  // the quarks are assumed to be distributed this way.
3246  std::pair<int, int> i = find_leading(nq1, nq2, outgoing_particles);
3247  std::pair<int, int> j = find_leading(nq2, nq1, outgoing_particles);
3248  if (baryon_string == 0 && i.second - i.first < j.second - j.first) {
3249  assign_scaling_factor(nq2, outgoing_particles[j.first], suppression_factor);
3250  assign_scaling_factor(nq1, outgoing_particles[j.second],
3251  suppression_factor);
3252  } else {
3253  assign_scaling_factor(nq1, outgoing_particles[i.first], suppression_factor);
3254  assign_scaling_factor(nq2, outgoing_particles[i.second],
3255  suppression_factor);
3256  }
3257 }
3258 
3260  PdgCode pdg_mapped(0x0);
3261 
3262  if (pdg.baryon_number() == 1) { // baryon
3263  pdg_mapped = pdg.charge() > 0 ? PdgCode(pdg::p) : PdgCode(pdg::n);
3264  } else if (pdg.baryon_number() == -1) { // antibaryon
3265  pdg_mapped = pdg.charge() < 0 ? PdgCode(-pdg::p) : PdgCode(-pdg::n);
3266  } else if (pdg.is_hadron()) { // meson
3267  if (pdg.charge() >= 0) {
3268  pdg_mapped = PdgCode(pdg::pi_p);
3269  } else {
3270  pdg_mapped = PdgCode(pdg::pi_m);
3271  }
3272  } else if (pdg.is_lepton()) { // lepton
3273  pdg_mapped = pdg.charge() < 0 ? PdgCode(0x11) : PdgCode(-0x11);
3274  } else {
3275  throw std::runtime_error("StringProcess::pdg_map_for_pythia failed.");
3276  }
3277 
3278  return pdg_mapped.get_decimal();
3279 }
3280 
3281 } // namespace smash
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
FourVector lorentz_boost(const ThreeVector &v) const
Returns the FourVector boosted with velocity v.
Definition: fourvector.cc:17
ThreeVector threevec() const
Definition: fourvector.h:329
double x0() const
Definition: fourvector.h:313
ThreeVector velocity() const
Get the velocity (3-vector divided by zero component).
Definition: fourvector.h:333
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:58
PdgCode pdgcode() const
Get the pdgcode of the particle.
Definition: particledata.h:87
void set_4momentum(const FourVector &momentum_vector)
Set the particle's 4-momentum directly.
Definition: particledata.h:164
bool is_baryon() const
Definition: particledata.h:94
void set_cross_section_scaling_factor(const double &xsec_scal)
Set the particle's initial cross_section_scaling_factor.
Definition: particledata.h:293
void set_formation_time(const double &form_time)
Set the absolute formation time.
Definition: particledata.h:251
static const ParticleType & find(PdgCode pdgcode)
Returns the ParticleType object for the given pdgcode.
Definition: particletype.cc:99
static const ParticleTypeList & list_all()
Definition: particletype.cc:51
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
Definition: pdgcode.h:111
int baryon_number() const
Definition: pdgcode.h:381
bool is_meson() const
Definition: pdgcode.h:394
int net_quark_number(const int quark) const
Returns the net number of quarks with given flavour number For public use, see strangeness(),...
Definition: pdgcode.cc:31
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:671
bool is_lepton() const
Definition: pdgcode.h:375
int32_t get_decimal() const
Definition: pdgcode.h:769
bool is_baryon() const
Definition: pdgcode.h:391
bool is_nucleon() const
Definition: pdgcode.h:397
bool is_hadron() const
Definition: pdgcode.h:370
int charge() const
The charge of the particle.
Definition: pdgcode.h:567
Pythia8::StringFlav pythia_stringflav_
An object for the flavor selection in string fragmentation in the case of separate fragmentation func...
bool remake_kinematics_fragments(Pythia8::Event &event_fragments, std::array< ThreeVector, 3 > &evec_basis, double ppos_string, double pneg_string, double QTrx_string, double QTry_string, double QTrx_add_pos, double QTry_add_pos, double QTrx_add_neg, double QTry_add_neg)
bool next_SDiff(bool is_AB_to_AX)
Single-diffractive process is based on single pomeron exchange described in Ingelman:1984ns .
pythia_map hard_map_
Map object to contain the different pythia objects.
static FourVector reorient(Pythia8::Particle &particle, std::array< ThreeVector, 3 > &evec_basis)
compute the four-momentum properly oriented in the lab frame.
Pythia8::SigmaTotal pythia_sigmatot_
An object to compute cross-sections.
double pow_fgluon_beta_
parameter for the gluon distribution function
Definition: stringprocess.h:87
std::array< int, 2 > NpartString_
number of particles fragmented from strings
Definition: stringprocess.h:80
Pythia8::Event event_intermediate_
event record for intermediate partonic state in the hard string routine
std::array< PdgCode, 2 > PDGcodes_
PdgCodes of incoming particles.
Definition: stringprocess.h:63
double time_formation_const_
constant proper time in the case of constant formation time [fm]
double PNegB_
backward lightcone momentum p^{-} of incoming particle B in CM-frame [GeV]
Definition: stringprocess.h:55
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.
double pow_fquark_beta_
parameter for the quark distribution function
Definition: stringprocess.h:97
bool next_NDiffSoft()
Soft Non-diffractive process is modelled in accordance with dual-topological approach Capella:1978ig ...
bool next_DDiff()
Double-diffractive process ( A + B -> X + X ) is similar to the single-diffractive process,...
static int pdg_map_for_pythia(PdgCode &pdg)
Take pdg code and map onto particle specie which can be handled by PYTHIA.
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...
FourVector ucomAB_
velocity four vector of the center of mass in the lab frame
Definition: stringprocess.h:69
ThreeVector vcomAB_
velocity three vector of the center of mass in the lab frame
Definition: stringprocess.h:71
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.
void compute_incoming_lightcone_momenta()
compute the lightcone momenta of incoming particles where the longitudinal direction is set to be sam...
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 exploiting PYTHIA 8....
double popcorn_rate_
popcorn rate
double pmin_gluon_lightcone_
the minimum lightcone momentum scale carried by a gluon [GeV]
Definition: stringprocess.h:82
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...
static std::pair< int, int > find_leading(int nq1, int nq2, ParticleList &list)
Find the leading string fragments.
double PPosA_
forward lightcone momentum p^{+} of incoming particle A in CM-frame [GeV]
Definition: stringprocess.h:49
double sqrtsAB_
sqrt of Mandelstam variable s of collision [GeV]
Definition: stringprocess.h:61
ParticleList final_state_
final state array which must be accessed after the collision
double PNegA_
backward lightcone momentum p^{-} of incoming particle A in CM-frame [GeV]
Definition: stringprocess.h:53
static double sample_zLund(double a, double b, double mTrn)
Sample lightcone momentum fraction according to the LUND fragmentation function.
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...
bool use_monash_tune_
Whether to use the monash tune Skands:2014pea for all string processes.
double soft_t_form_
factor to be multiplied to formation times in soft strings
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.
std::unique_ptr< Pythia8::Pythia > pythia_hadron_
PYTHIA object used in fragmentation.
double massA_
mass of incoming particle A [GeV]
Definition: stringprocess.h:57
void common_setup_pythia(Pythia8::Pythia *pythia_in, double strange_supp, double diquark_supp, double popcorn_rate, double stringz_a, double stringz_b, double string_sigma_T)
Common setup of PYTHIA objects for soft and hard string routines.
double PPosB_
forward lightcone momentum p^{+} of incoming particle B in CM-frame [GeV]
Definition: stringprocess.h:51
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: stringprocess.h:76
int fragment_off_hadron(bool from_forward, bool separate_fragment_baryon, std::array< ThreeVector, 3 > &evec_basis, double &ppos_string, double &pneg_string, double &QTrx_string_pos, double &QTrx_string_neg, double &QTry_string_pos, double &QTry_string_neg, Pythia8::FlavContainer &flav_string_pos, Pythia8::FlavContainer &flav_string_neg, std::vector< int > &pdgid_frag, std::vector< FourVector > &momentum_frag)
Fragment one hadron from a given string configuration if the string mass is above threshold (given by...
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...
double string_sigma_T_
transverse momentum spread in string fragmentation
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
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.
static void convert_KaonLS(int &pythia_id)
convert Kaon-L or Kaon-S into K0 or Anti-K0
double additional_xsec_supp_
additional cross-section suppression factor to take coherence effect into account.
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.
double strange_supp_
strange quark suppression factor
static int diquark_from_quarks(int q1, int q2)
Construct diquark from two quarks.
bool make_lightcone_final_two(bool separate_fragment_hadron, double ppos_string, double pneg_string, double mTrn_had_forward, double mTrn_had_backward, double &ppos_had_forward, double &ppos_had_backward, double &pneg_had_forward, double &pneg_had_backward)
Determines lightcone momenta of two final hadrons fragmented from a string in the same way as StringF...
void replace_constituent(Pythia8::Particle &particle, std::array< int, 5 > &excess_constituent)
Convert a partonic PYTHIA particle into the desired species and update the excess of constituents.
double kappa_tension_string_
string tension [GeV/fm]
double pow_fquark_alpha_
parameter for the quark distribution function
Definition: stringprocess.h:92
void shift_rapidity_event(Pythia8::Event &event, std::array< ThreeVector, 3 > &evec_basis, double factor_yrapid, double diff_yrapid)
Shift the momentum rapidity of all particles in a given event record.
double stringz_b_leading_
parameter (StringZ:bLund) for the fragmentation function of leading baryon in soft non-diffractive st...
static bool append_intermediate_list(int pdgid, FourVector momentum, ParticleList &intermediate_particles)
append new particle from PYTHIA to a specific particle list
bool mass_dependent_formation_times_
Whether the formation time should depend on the mass of the fragment according to Andersson:1983ia e...
double stringz_b_produce_
parameter (StringZ:bLund) for the fragmentation function of other (produced) hadrons in soft non-diff...
bool next_BBbarAnn()
Baryon-antibaryon annihilation process Based on what UrQMD Bass:1998ca , Bleicher:1999xi does,...
int NpartFinal_
total number of final state particles
Definition: stringprocess.h:78
static Pythia8::Vec4 set_Vec4(double energy, const ThreeVector &mom)
Easy setter of Pythia Vec4 from SMASH.
double stringz_a_leading_
parameter (StringZ:aLund) for the fragmentation function of leading baryon in soft non-diffractive st...
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.
void init(const ParticleList &incoming, double tcoll)
initialization feed intial particles, time of collision and gamma factor of the center of mass.
double sigma_qperp_
Transverse momentum spread of the excited strings.
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 constituents that need to be converted...
int get_resonance_from_quark(int idq1, int idq2, double mass)
bool next_NDiffHard()
Hard Non-diffractive process is based on PYTHIA 8 with partonic showers and interactions.
bool separate_fragment_baryon_
Whether to use a separate fragmentation function for leading baryons.
std::array< FourVector, 2 > plab_
momenta of incoming particles in the lab frame [GeV]
Definition: stringprocess.h:65
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 stringz_a_leading, double stringz_b_leading, double stringz_a, double stringz_b, double string_sigma_T, double factor_t_form, bool mass_dependent_formation_times, double prob_proton_to_d_uu, bool separate_fragment_baryon, double popcorn_rate, bool use_monash_tune)
Constructor, initializes pythia.
std::array< FourVector, 2 > pcom_
momenta of incoming particles in the center of mass frame [GeV]
Definition: stringprocess.h:67
double time_collision_
time of collision in the computational frame [fm]
int get_hadrontype_from_quark(int idq1, int idq2)
Determines hadron type from valence quark constituents.
double diquark_supp_
diquark suppression factor
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...
double massB_
mass of incoming particle B [GeV]
Definition: stringprocess.h:59
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...
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 ...
double stringz_a_produce_
parameter (StringZ:aLund) for the fragmentation function of other (produced) hadrons in soft non-diff...
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_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,...
static void quarks_from_diquark(int diquark, int &q1, int &q2, int &deg_spin)
find two quarks from a diquark.
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:31
double abs() const
Definition: threevector.h:268
double sqr() const
Definition: threevector.h:266
double x3() const
Definition: threevector.h:185
Discrete distribution with weight given by probability vector.
Definition: random.h:258
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
constexpr int pi_p
π⁺.
constexpr int p
Proton.
constexpr int n
Neutron.
constexpr int pi_m
π⁻.
T power(T n, T xMin, T xMax)
Draws a random number according to a power-law distribution ~ x^n.
Definition: random.h:203
T beta_a0(T xmin, T b)
Draws a random number from a beta-distribution with a = 0.
Definition: random.h:351
T beta(T a, T b)
Draws a random number from a beta-distribution, where probability density of is .
Definition: random.h:329
double normal(const T &mean, const T &sigma)
Returns a random number drawn from a normal distribution.
Definition: random.h:250
T uniform_int(T min, T max)
Definition: random.h:100
T uniform(T min, T max)
Definition: random.h:88
Definition: action.h:24
T pCM(const T sqrts, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:79
T pCM_sqr(const T sqrts, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:91
constexpr int maximum_rndm_seed_in_pythia
The maximum value of the random seed used in PYTHIA.
Definition: constants.h:103
constexpr double small_number
Physical error tolerance.
Definition: constants.h:51
constexpr T pow_int(const T base, unsigned const exponent)
Efficient template for calculating integer powers using squaring.
Definition: pow.h:23
static constexpr int LOutput
constexpr double pion_mass
Pion mass in GeV.
Definition: constants.h:65
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:37
constexpr double kaon_mass
Kaon mass in GeV.
Definition: constants.h:72
static constexpr int LPythia
Definition: stringprocess.h:26