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