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