30 : pmin_gluon_lightcone_(
31 config.take(
InputKeys::collTerm_stringParam_gluonPMin)),
32 pow_fgluon_beta_(config.take(
InputKeys::collTerm_stringParam_gluonBeta)),
34 config.take(
InputKeys::collTerm_stringParam_quarkAlpha)),
35 pow_fquark_beta_(config.take(
InputKeys::collTerm_stringParam_quarkBeta)),
36 sigma_qperp_(config.take(
InputKeys::collTerm_stringParam_sigmaPerp)),
38 config.take(
InputKeys::collTerm_stringParam_stringZALeading)),
40 config.take(
InputKeys::collTerm_stringParam_stringZBLeading)),
41 stringz_a_produce_(config.take(
InputKeys::collTerm_stringParam_stringZA)),
42 stringz_b_produce_(config.take(
InputKeys::collTerm_stringParam_stringZB)),
44 config.take(
InputKeys::collTerm_stringParam_strangeSuppression)),
46 config.take(
InputKeys::collTerm_stringParam_diquarkSuppression)),
47 popcorn_rate_(config.take(
InputKeys::collTerm_stringParam_popcornRate)),
48 damp_popcorn_(config.take(
InputKeys::collTerm_stringParam_dampPopcorn)),
50 config.take(
InputKeys::collTerm_stringParam_stringSigmaT)),
51 kappa_tension_string_(
52 config.take(
InputKeys::collTerm_stringParam_stringTension)),
53 time_formation_const_(
54 config.take(
InputKeys::collTerm_stringParam_formationTime)),
55 soft_t_form_(config.take(
InputKeys::collTerm_stringParam_formTimeFactor)),
56 mass_dependent_formation_times_(config.take(
57 InputKeys::collTerm_stringParam_mDependentFormationTimes)),
59 config.take(
InputKeys::collTerm_stringParam_probabilityPToDUU)),
60 separate_fragment_baryon_(
61 config.take(
InputKeys::collTerm_stringParam_separateFragmentBaryon)),
63 config.take(
InputKeys::collTerm_stringParam_useMonashTune)),
64 additional_xsec_supp_(
65 config.take(
InputKeys::collTerm_stringParam_unformedXsecSuppression)),
67 config.take(
InputKeys::collTerm_stringParam_pythiaSettings)) {
69 pythia_hadron_ = std::make_unique<Pythia8::Pythia>(PYTHIA_XML_DIR,
false);
96 for (
int imu = 0; imu < 3; imu++) {
105 double popcorn_rate,
double stringz_a,
107 double string_sigma_T) {
109 pythia_in->readString(
"ParticleData:modeBreitWigner = 4");
112 pythia_in->readString(
"StringZ:aLund = " +
std::to_string(stringz_a));
113 pythia_in->readString(
"StringZ:bLund = " +
std::to_string(stringz_b));
114 pythia_in->readString(
"BeamRemnants:dampPopcorn = " +
117 pythia_in->readString(
"BeamRemnants:hardRemnantBaryon = on");
118 pythia_in->readString(
"BeamRemnants:aRemnantBaryon = " +
120 pythia_in->readString(
"BeamRemnants:bRemnantBaryon = " +
124 pythia_in->readString(
"StringPT:sigma = " +
std::to_string(string_sigma_T));
126 pythia_in->readString(
"StringFlav:probQQtoQ = " +
129 pythia_in->readString(
"StringFlav:probStoUD = " +
131 pythia_in->readString(
"StringFlav:popcornRate = " +
135 pythia_in->readString(
"PDF:pSet = 13");
136 pythia_in->readString(
"PDF:pSetB = 13");
137 pythia_in->readString(
"PDF:piSet = 1");
138 pythia_in->readString(
"PDF:piSetB = 1");
139 pythia_in->readString(
"Beams:idA = 2212");
140 pythia_in->readString(
"Beams:idB = 2212");
141 pythia_in->readString(
"Beams:eCM = 10.");
144 pythia_in->readString(
"Random:setSeed = on");
146 pythia_in->readString(
"Print:quiet = on");
148 pythia_in->readString(
"HadronLevel:Decay = off");
151 int pdgid = ptype.pdgcode().get_decimal();
152 double mass_pole = ptype.mass();
153 double width_pole = ptype.width_at_pole();
155 if (pythia_in->particleData.isParticle(pdgid)) {
157 pythia_in->particleData.m0(pdgid, mass_pole);
158 pythia_in->particleData.mWidth(pdgid, width_pole);
159 }
else if (pdgid == 310 || pdgid == 130) {
161 pythia_in->particleData.m0(pdgid,
kaon_mass);
162 pythia_in->particleData.mWidth(pdgid, 0.);
167 pythia_in->readString(
"Check:epTolErr = 1e-6");
168 pythia_in->readString(
"Check:epTolWarn = 1e-8");
171 pythia_in->readString(
"Tune:ee = 7");
172 pythia_in->readString(
"Tune:pp = 14");
176 if (!pythia_in->readString(setting)) {
177 throw std::runtime_error(
178 "Failed to apply Pythia setting \"" + setting +
179 "\". Please check that it is a valid Pythia 8 setting.");
185 ParticleList& intermediate_particles,
const FourVector& pString,
186 const ThreeVector& evecLong,
double additional_xsec_supp,
187 bool find_and_scale_leading) {
188 assert(intermediate_particles.size() > 0);
191 for (
const ParticleData& data : intermediate_particles) {
192 bstring += data.pdgcode().baryon_number();
196 if (find_and_scale_leading) {
198 additional_xsec_supp);
202 const FourVector mom_in_string_restframe = particle.momentum();
205 const double gamma_string = 1.0 / particle.inverse_gamma();
208 particle.set_4momentum(p_com);
209 const double tau_prod =
214 const double t_prod_string = tau_prod * gamma_string;
217 t_prod_string * velocity_in_string_restframe);
219 fragment_position = fragment_position.
lorentz_boost(-vstring);
221 particle.set_slow_formation_times(
229 massA_ = incoming[0].effective_mass();
230 massB_ = incoming[1].effective_mass();
232 plab_[0] = incoming[0].momentum();
233 plab_[1] = incoming[1].momentum();
243 to_cm_.toCMframe(pA_lab, pB_lab);
245 const Pythia8::Vec4 pA_cm =
to_cm_ * pA_lab;
246 const Pythia8::Vec4 pB_cm =
to_cm_ * pB_lab;
250 ThreeVector evec_polar(pA_cm.px(), pA_cm.py(), pA_cm.pz());
251 evec_polar /= std::sqrt(evec_polar.
sqr());
263 bool parton_level_success =
false;
288 logg[
LPythia].error(
"Unknown string process required.");
292 if (!parton_level_success) {
311 const Pythia8::Event& string_evt) {
312 ParticleList intermediate_particles;
314 if (string_evt.size() < 3) {
315 logg[
LPythia].error(
"String event too small to hadronize.");
319 bool has_leading_parton =
false;
321 const bool has_junction = string_evt.sizeJunction() > 0;
323 for (
const Pythia8::Particle& particle : string_evt) {
325 has_leading_parton =
true;
330 const bool has_leading_and_is_open = has_leading_parton && !has_junction;
335 for (
int i = 1; i < string_evt.size(); ++i) {
340 for (
int i = 0; i < string_evt.sizeJunction(); ++i) {
342 string_evt.kindJunction(i), string_evt.colJunction(i, 0),
343 string_evt.colJunction(i, 1), string_evt.colJunction(i, 2));
346 const Pythia8::Vec4 p_str = string_evt[0].p();
348 Pythia8::RotBstMatrix to_string_rest;
349 to_string_rest.bstback(p_str);
355 logg[
LPythia].error(
"Pythia fragmentation failed for one string.");
359 if (has_leading_and_is_open) {
364 p_str.e(),
ThreeVector(p_str.px(), p_str.py(), p_str.pz()));
366 const double m2 = pString_smash.
sqr();
369 logg[
LPythia].error(
"String has non-positive invariant mass.");
375 ThreeVector evecLong(p_str.px(), p_str.py(), p_str.pz());
377 const double pabs = evecLong.
abs();
380 evecLong = evecLong / pabs;
390 if (!(particle.isFinal() && particle.isHadron())) {
396 int particle_id = particle.id();
400 intermediate_particles)) {
401 logg[
LPythia].error(
"Unknown hadron in SMASH during hadronization: PDG=",
408 intermediate_particles.back().set_cross_section_scaling_factor(
412 intermediate_particles.back().set_cross_section_scaling_factor(
417 intermediate_particles.back().set_cross_section_scaling_factor(0.0);
421 if (intermediate_particles.empty()) {
422 logg[
LPythia].error(
"Hadronization produced no final hadrons.");
426 const bool should_assign_scaling = has_junction && has_leading_parton;
431 return intermediate_particles;
434 const std::array<int, 2>& ends,
int color_tag,
435 bool use_projectile_axis,
436 bool random_flip_of_endpoints) {
437 const bool flip_endpoints =
440 const int id1 = flip_endpoints ? ends[1] : ends[0];
441 const int id2 = flip_endpoints ? ends[0] : ends[1];
444 const double m_string = p_str.mCalc();
449 const double m1 = pd.m0(id1);
450 const double m2 = pd.m0(id2);
452 const double p_cm =
pCM(m_string, m1, m2);
456 const double E1 = std::sqrt(p_cm * p_cm + m1 * m1);
457 const double E2 = std::sqrt(p_cm * p_cm + m2 * m2);
459 const int ibeam = use_projectile_axis ? 0 : 1;
462 Pythia8::Vec4 beam_axis_cm(mom.
x1(), mom.
x2(), mom.
x3(), mom.
abs());
464 Pythia8::RotBstMatrix to_string_rest;
465 to_string_rest.bstback(p_str);
467 Pythia8::Vec4 beam_axis_rest = to_string_rest * beam_axis_cm;
469 const double norm = std::sqrt(beam_axis_rest.px() * beam_axis_rest.px() +
470 beam_axis_rest.py() * beam_axis_rest.py() +
471 beam_axis_rest.pz() * beam_axis_rest.pz());
476 const double nx = beam_axis_rest.px() / norm;
477 const double ny = beam_axis_rest.py() / norm;
478 const double nz = beam_axis_rest.pz() / norm;
480 const Pythia8::Vec4 p1_rest(nx * p_cm, ny * p_cm, nz * p_cm, E1);
481 const Pythia8::Vec4 p2_rest(-nx * p_cm, -ny * p_cm, -nz * p_cm, E2);
483 Pythia8::RotBstMatrix boost;
486 const Pythia8::Vec4 p1_cm = boost * p1_rest;
487 const Pythia8::Vec4 p2_cm = boost * p2_rest;
494 evt.append(90, -11, 0, 0, p1_cm + p2_cm, (p1_cm + p2_cm).mCalc());
499 const bool id1_is_quark =
pythia_hadron_->particleData.isQuark(id1);
500 const bool id2_is_quark =
pythia_hadron_->particleData.isQuark(id2);
502 const int i1 = evt.append(id1, id1_is_quark ? status_quark : status_diquark,
504 const int i2 = evt.append(id2, id2_is_quark ? status_quark : status_diquark,
509 evt.rotbst(
to_cm_.inverse());
519 double QTrn, QTrx, QTry;
520 double pabscomHX_sqr, massX;
529 if (mstrMin > mstrMax) {
535 QTrn = std::sqrt(QTrx * QTrx + QTry * QTry);
542 const bool foundPabsX = pabscomHX_sqr > QTrn * QTrn;
549 double sign_direction = is_AB_to_AX ? 1. : -1.;
552 const double pL = std::sqrt(pabscomHX_sqr - QTrn * QTrn);
555 const ThreeVector cm_momentum(sign_direction * QTrx, sign_direction * QTry,
556 sign_direction * pL);
558 const FourVector pstrHcom(std::sqrt(pabscomHX_sqr + massH * massH),
560 const FourVector pstrXcom(std::sqrt(pabscomHX_sqr + massX * massX),
571 had_mom.rotbst(
to_cm_.inverse());
582 std::array<std::array<int, 2>, 2> quarks;
591 const double xfracA =
593 const double xfracB =
598 const double QTrn = std::sqrt(QTrx * QTrx + QTry * QTry);
600 const double QPos = -QTrn * QTrn / (2. * xfracB *
PNegB_);
601 const double QNeg = QTrn * QTrn / (2. * xfracA *
PPosA_);
603 const double pz1 = ((
PPosA_ + QPos) - (
PNegA_ + QNeg)) * M_SQRT1_2;
604 const double E1 = ((
PPosA_ + QPos) + (
PNegA_ + QNeg)) * M_SQRT1_2;
605 Pythia8::Vec4 p_str1(QTrx, QTry, pz1, E1);
607 const double pz2 = ((
PPosB_ - QPos) - (
PNegB_ - QNeg)) * M_SQRT1_2;
608 const double E2 = ((
PPosB_ - QPos) + (
PNegB_ - QNeg)) * M_SQRT1_2;
609 Pythia8::Vec4 p_str2(-QTrx, -QTry, pz2, E2);
611 std::swap(quarks[0][0], quarks[0][1]);
614 std::swap(quarks[1][0], quarks[1][1]);
627 std::swap(baryon, antibaryon);
629 if (baryon.
baryon_number() != 1 || antibaryon.baryon_number() != -1) {
630 throw std::invalid_argument(
"Expected baryon-antibaryon pair.");
634 constexpr
int n_q_types = 5;
635 std::vector<int> qcount_bar, qcount_antibar;
636 std::vector<int> n_combinations;
637 bool no_combinations =
true;
638 for (
int i = 0; i < n_q_types; i++) {
640 qcount_antibar.push_back(-antibaryon.net_quark_number(i + 1));
641 const int n_i = qcount_bar[i] * qcount_antibar[i];
642 n_combinations.push_back(n_i);
644 no_combinations =
false;
650 if (no_combinations) {
651 for (
int i = 0; i < 2; i++) {
654 pcom_pyth.rotbst(
to_cm_.inverse());
665 const int q_annihilate = discrete_distr() + 1;
666 qcount_bar[q_annihilate - 1]--;
667 qcount_antibar[q_annihilate - 1]--;
670 std::vector<int> remaining_quarks, remaining_antiquarks;
671 for (
int i = 0; i < n_q_types; i++) {
672 for (
int j = 0; j < qcount_bar[i]; j++) {
673 remaining_quarks.push_back(i + 1);
675 for (
int j = 0; j < qcount_antibar[i]; j++) {
676 remaining_antiquarks.push_back(-(i + 1));
679 assert(remaining_quarks.size() == 2);
680 assert(remaining_antiquarks.size() == 2);
686 std::swap(remaining_quarks[0], remaining_quarks[1]);
689 std::swap(remaining_antiquarks[0], remaining_antiquarks[1]);
692 bool kin_threshold_satisfied =
true;
693 for (
int i = 0; i < 2; i++) {
694 const double mstr_min =
697 if (mstr_min > mstr[i]) {
698 kin_threshold_satisfied =
false;
701 if (!kin_threshold_satisfied) {
705 for (
int i = 0; i < 2; i++) {
707 {remaining_quarks[i], remaining_antiquarks[i]}, 101 + i,
717 std::array<int, 2> endsA;
718 std::array<int, 2> endsB;
721 int idqA1, idqA2, idqB1, idqB2;
725 const int bar_a =
PDGcodes_[0].baryon_number();
726 const int bar_b =
PDGcodes_[1].baryon_number();
729 (bar_a == 0 && bar_b == 1) ||
730 (bar_a == 0 && bar_b == 0)) {
731 endsA = {idqA2, idqB1};
732 endsB = {idqB2, idqA1};
733 }
else if ((bar_a == 0 && bar_b == -1) ||
735 endsA = {idqB2, idqA1};
736 endsB = {idqA2, idqB1};
738 std::stringstream ss;
739 ss <<
"StringProcess::next_NDiffSoft: baryonA = " << bar_a
740 <<
", baryonB = " << bar_b;
741 throw std::runtime_error(ss.str());
754 const double qT2 = qx * qx + qy * qy;
757 const double QPos = -qT2 / (2. * xfracB *
PNegB_);
758 const double QNeg = qT2 / (2. * xfracA *
PPosA_);
759 const double dPPos = -xfracA *
PPosA_ - QPos;
760 const double dPNeg = xfracB *
PNegB_ - QNeg;
763 const double pzA = ((
PPosA_ + dPPos) - (
PNegA_ + dPNeg)) * M_SQRT1_2;
764 const double EA = ((
PPosA_ + dPPos) + (
PNegA_ + dPNeg)) * M_SQRT1_2;
765 Pythia8::Vec4 p_strA(qx, qy, pzA, EA);
768 const double pzB = ((
PPosB_ - dPPos) - (
PNegB_ - dPNeg)) * M_SQRT1_2;
769 const double EB = ((
PPosB_ - dPPos) + (
PNegB_ - dPNeg)) * M_SQRT1_2;
770 Pythia8::Vec4 p_strB(-qx, -qy, pzB, EB);
776 Pythia8::Pythia& pythia) {
777 const Pythia8::Event&
event = pythia.event;
779 auto find_final_copy = [&](
int iPos) ->
int {
780 if (iPos <= 0 || iPos >= event.size())
782 if (event[iPos].isFinal())
785 const int id =
event[iPos].id();
786 const auto ds =
event[iPos].daughterListRecursive();
788 if (j > 0 && j < event.size() &&
event[j].isFinal() &&
795 auto tag_from_beam = [&](
const Pythia8::BeamParticle& beam,
796 std::vector<bool>& isValenceFinal) ->
void {
797 for (
int i = 0; i < beam.size(); ++i) {
798 if (!beam[i].isValence()) {
802 const int j = find_final_copy(beam[i].iPos());
804 if (j >= 0 && (event[j].isQuark() || event[j].isDiquark())) {
805 isValenceFinal[j] =
true;
809 auto tag_from_unresolved_diff_systems =
810 [&](std::vector<bool>& isValenceFinal) ->
void {
811 for (
int i = 1; i <
event.size(); ++i) {
812 if (event[i].statusAbs() != 15) {
816 std::vector<int> endpoint_daughters;
817 bool resolved_system =
false;
819 for (
int d : event[i].daughterList()) {
820 if (d <= 0 || d >= event.size()) {
824 if (event[d].isQuark() ||
event[d].isDiquark()) {
825 endpoint_daughters.push_back(d);
829 if (!event[d].isGluon()) {
830 resolved_system =
true;
834 if (resolved_system || endpoint_daughters.empty()) {
838 bool already_tagged =
false;
839 for (
int j : endpoint_daughters) {
840 if (isValenceFinal[j]) {
841 already_tagged =
true;
846 if (already_tagged) {
850 for (
int j : endpoint_daughters) {
851 isValenceFinal[j] =
true;
856 std::vector<bool> isValenceFinal(event.size(),
false);
857 tag_from_beam(pythia.beamA, isValenceFinal);
858 tag_from_beam(pythia.beamB, isValenceFinal);
860 tag_from_unresolved_diff_systems(isValenceFinal);
862 return isValenceFinal;
871 for (
const auto& part : event) {
872 if (part.isQuark() || part.isDiquark()) {
882 if (idx1 < 0 || idx2 < 0) {
888 Pythia8::RotBstMatrix toRest;
889 toRest.toCMframe(event[idx1].
p(), event[idx2].
p());
890 event.rotbst(toRest);
893 std::vector<int> hadrons;
894 hadrons.reserve(event.size());
896 for (
int i = 0; i <
event.size(); ++i) {
897 if (event[i].isFinal() && event[i].isHadron()) {
898 hadrons.push_back(i);
902 std::sort(hadrons.begin(), hadrons.end(),
903 [&](
int a,
int b) { return event[a].pz() < event[b].pz(); });
906 const int forward_endpoint =
907 event[idx1].pz() >
event[idx2].pz() ? idx1 : idx2;
908 const int backward_endpoint = forward_endpoint == idx1 ? idx2 : idx1;
910 auto matches_endpoint = [&](
int endpoint,
int hadron) {
911 const auto& end =
event[endpoint];
912 const auto& h =
event[hadron];
921 if (end.isDiquark()) {
922 return pd.isBaryon(h.id()) && h.id() * end.id() > 0;
926 if (pd.isBaryon(h.id())) {
927 return h.id() * end.id() > 0;
934 auto find_edge_hadron = [&](
int endpoint,
bool forward) ->
int {
938 for (
auto it = hadrons.rbegin(); it != hadrons.rend(); ++it) {
939 if (matches_endpoint(endpoint, *it)) {
944 for (
int h : hadrons) {
945 if (matches_endpoint(endpoint, h)) {
954 auto tag_endpoint = [&](
int endpoint,
bool forward) {
960 const int hadron = find_edge_hadron(endpoint, forward);
963 event[hadron].status(
968 auto tag_if_diquark = [&](
int endpoint,
bool forward) {
969 if (event[endpoint].isDiquark()) {
970 tag_endpoint(endpoint, forward);
974 auto tag_if_quark = [&](
int endpoint,
bool forward) {
975 if (!event[endpoint].isDiquark()) {
976 tag_endpoint(endpoint, forward);
982 tag_if_diquark(backward_endpoint,
false);
983 tag_if_diquark(forward_endpoint,
true);
986 tag_if_quark(backward_endpoint,
false);
987 tag_if_quark(forward_endpoint,
true);
990 event.rotbst(toRest.inverse());
997 std::array<int, 2> pdg_for_pythia;
998 std::array<std::array<int, 5>, 2> excess_quark;
999 std::array<std::array<int, 5>, 2> excess_antiq;
1000 for (
int i = 0; i < 2; i++) {
1001 for (
int j = 0; j < 5; j++) {
1002 excess_quark[i][j] = 0;
1003 excess_antiq[i][j] = 0;
1009 " is mapped onto ", pdg_for_pythia[i]);
1016 logg[
LPythia].debug(
" excess_quark[", i,
"] = (", excess_quark[i][0],
1017 ", ", excess_quark[i][1],
", ", excess_quark[i][2],
1018 ", ", excess_quark[i][3],
", ", excess_quark[i][4],
1020 logg[
LPythia].debug(
" excess_antiq[", i,
"] = (", excess_antiq[i][0],
1021 ", ", excess_antiq[i][1],
", ", excess_antiq[i][2],
1022 ", ", excess_antiq[i][3],
", ", excess_antiq[i][4],
1026 std::pair<int, int> idAB{pdg_for_pythia[0], pdg_for_pythia[1]};
1031 hard_map_[idAB] = std::make_unique<Pythia8::Pythia>(PYTHIA_XML_DIR,
false);
1032 hard_map_[idAB]->readString(
"SoftQCD:nonDiffractive = on");
1033 hard_map_[idAB]->readString(
"SoftQCD:singleDiffractiveXB = on");
1034 hard_map_[idAB]->readString(
"SoftQCD:singleDiffractiveAX = on");
1035 hard_map_[idAB]->readString(
"SoftQCD:doubleDiffractive = on");
1036 hard_map_[idAB]->readString(
"HadronLevel:all = off");
1040 hard_map_[idAB]->settings.flag(
"Beams:allowVariableEnergy",
true);
1041 hard_map_[idAB]->settings.mode(
"Beams:idA", idAB.first);
1042 hard_map_[idAB]->settings.mode(
"Beams:idB", idAB.second);
1045 logg[
LPythia].debug(
"Pythia object initialized with ", pdg_for_pythia[0],
1046 " + ", pdg_for_pythia[1],
" at CM energy [GeV] ",
1049 throw std::runtime_error(
"Pythia failed to initialize.");
1054 logg[
LPythia].debug(
"hard_map_[", idAB.first,
"][", idAB.second,
1055 "] : rndm is initialized with seed ", seed_new);
1060 bool final_state_success =
false;
1075 final_state_success =
hard_map_[idAB]->next(1);
1078 final_state_success =
hard_map_[idAB]->next(5);
1081 final_state_success =
hard_map_[idAB]->next(4);
1084 final_state_success =
hard_map_[idAB]->next(3);
1087 logg[
LPythia].error(
"Unknown string process required.");
1088 final_state_success =
false;
1092 logg[
LPythia].debug(
"Pythia final state computed, success = ",
1093 final_state_success);
1094 if (!final_state_success) {
1101 if (!
p.isQuark() && !
p.isDiquark() && !
p.isGluon()) {
1105 if (valence_tags[
p.index()] && (
p.isQuark() ||
p.isDiquark())) {
1106 p.statusCode(
static_cast<int>(
p.isDiquark()
1113 ParticleList new_intermediate_particles;
1114 ParticleList new_non_hadron_particles;
1115 Pythia8::Vec4 pSum = 0.;
1120 for (
int i = 0; i <
hard_map_[idAB]->event.size(); i++) {
1121 if (
hard_map_[idAB]->event[i].isFinal()) {
1122 const int pdgid =
hard_map_[idAB]->event[i].id();
1123 Pythia8::Vec4 pquark =
hard_map_[idAB]->event[i].p();
1124 const double mass = pquark.mCalc();
1125 const int status =
hard_map_[idAB]->event[i].status();
1126 const int color =
hard_map_[idAB]->event[i].col();
1127 const int anticolor =
hard_map_[idAB]->event[i].acol();
1135 for (
int i = 0; i <
hard_map_[idAB]->event.sizeJunction(); i++) {
1136 const int kind =
hard_map_[idAB]->event.kindJunction(i);
1137 std::array<int, 3> col;
1138 for (
int j = 0; j < 3; j++) {
1139 col[j] =
hard_map_[idAB]->event.colJunction(i, j);
1148 const int elastic_side =
1152 if (elastic_side >= 0 &&
1153 PDGcodes_[elastic_side].get_decimal() != pdg_for_pythia[elastic_side]) {
1154 const int mapped_id = pdg_for_pythia[elastic_side];
1155 const int actual_id =
PDGcodes_[elastic_side].get_decimal();
1157 int elastic_index = -1;
1162 if (
p.status() == 14 &&
p.id() == mapped_id) {
1168 if (elastic_index < 0) {
1169 logg[
LPythia].warn(
"Could not find elastic mapped hadron ", mapped_id,
1170 " to replace by ", actual_id);
1176 std::vector<int> recoil_indices;
1177 Pythia8::Vec4 p_recoil_old;
1180 if (i == elastic_index)
1185 recoil_indices.push_back(i);
1186 p_recoil_old +=
p.p();
1189 if (recoil_indices.empty()) {
1192 "Could not find recoil system for elastic mapped hadron ", mapped_id);
1196 Pythia8::Vec4 p_elastic_new = elastic.p();
1197 Pythia8::Vec4 p_recoil_new = p_recoil_old;
1199 const double m_elastic_new =
1202 const double m_recoil_system = p_recoil_old.mCalc();
1204 if (!Pythia8::pShift(p_elastic_new, p_recoil_new, m_elastic_new,
1207 "Could not shift momenta when remapping elastic hadron from ",
1208 mapped_id,
" to ", actual_id,
".");
1212 Pythia8::RotBstMatrix recoil_bst;
1213 recoil_bst.bstback(p_recoil_old);
1214 recoil_bst.bst(p_recoil_new);
1216 elastic.id(actual_id);
1217 elastic.m(m_elastic_new);
1218 elastic.p(p_elastic_new);
1220 for (
const int idx : recoil_indices) {
1223 Pythia8::Vec4 p_new = recoiler.p();
1224 p_new.rotbst(recoil_bst);
1227 excess_quark[elastic_side] = {0, 0, 0, 0, 0};
1228 excess_antiq[elastic_side] = {0, 0, 0, 0, 0};
1233 bool correct_constituents =
1235 if (!correct_constituents) {
1236 logg[
LPythia].debug(
"failed to find correct partonic constituents.");
1242 while (ipart < npart) {
1246 !
hard_map_[idAB]->particleData.isOctetHadron(pdgid)) {
1247 logg[
LPythia].debug(
"PDG ID from Pythia: ", pdgid);
1251 logg[
LPythia].debug(
"4-momentum from Pythia: ", momentum);
1256 " does not exist in ParticleType - start over.");
1266 for (
ParticleData& non_hadron : new_non_hadron_particles) {
1267 non_hadron.set_cross_section_scaling_factor(1.);
1271 logg[
LPythia].debug(
"Hard non-diff: partonic process gives ",
1273 bool find_forward_string =
true;
1275 Pythia8::Event string_event;
1291 find_forward_string = !find_forward_string;
1298 int right_endpoint_id) {
1299 constexpr
double fragmentation_margin = 2.0 *
pion_mass;
1303 const bool left_is_quark = particle_data.isQuark(left_endpoint_id);
1304 const bool right_is_quark = particle_data.isQuark(right_endpoint_id);
1305 const bool left_is_diquark = particle_data.isDiquark(left_endpoint_id);
1306 const bool right_is_diquark = particle_data.isDiquark(right_endpoint_id);
1308 if (!(left_is_quark || left_is_diquark) ||
1309 !(right_is_quark || right_is_diquark)) {
1310 throw std::invalid_argument(
1311 "String threshold estimate requires quark or diquark endpoints.");
1314 if (left_is_diquark && right_is_diquark) {
1315 const int lightest_left_baryon =
1317 const int lightest_right_baryon =
1320 return particle_data.m0(lightest_left_baryon) +
1321 particle_data.m0(lightest_right_baryon) + fragmentation_margin;
1324 const int lightest_hadron =
1332 const int id =
p.id();
1333 const bool is_anti = (
id < 0);
1336 const bool is_quark = pd.isQuark(
id);
1337 const bool is_diquark = pd.isDiquark(
id);
1339 if ((is_quark && !is_anti) || (is_diquark && is_anti)) {
1342 }
else if ((is_quark && is_anti) || (is_diquark && !is_anti)) {
1346 throw std::runtime_error(
"Cannot set color: not a quark or diquark (id=" +
1351 const double string_mass =
event[0].mCalc();
1354 std::vector<int> endpoints;
1355 endpoints.reserve(event.size());
1357 for (
const Pythia8::Particle&
p : event) {
1358 if (!
p.isFinal() ||
p.isGluon() || !
p.isParton()) {
1362 const int id =
p.id();
1364 if (!particle_data.isQuark(
id) && !particle_data.isDiquark(
id)) {
1365 logg[
LPythia].error(
"Colored non-quark/diquark in threshold check: id=",
1366 id,
", col=",
p.col(),
", acol=",
p.acol());
1370 endpoints.push_back(
id);
1374 if (endpoints.size() < 2) {
1379 if (endpoints.size() == 2) {
1384 if (endpoints.size() == 3) {
1385 for (std::size_t i = 0; i < endpoints.size(); ++i) {
1386 for (std::size_t j = i + 1; j < endpoints.size(); ++j) {
1387 const int id_i = endpoints[i];
1388 const int id_j = endpoints[j];
1390 if (!particle_data.isQuark(id_i) || !particle_data.isQuark(id_j)) {
1394 if (id_i * id_j < 0) {
1400 const std::size_t k = 3 - i - j;
1401 const int remaining_id = endpoints[k];
1403 return string_mass >
1409 "Cannot reduce 3 string endpoints to quark-diquark "
1411 endpoints[0],
", ", endpoints[1],
", ", endpoints[2]);
1416 "Unexpected number of string endpoints in threshold check: ",
1422 std::array<int, 5>& excess_quark,
1423 std::array<int, 5>& excess_antiq) {
1426 std::array<int, 3> qcontent_actual = pdg_actual.
quark_content();
1427 std::array<int, 3> qcontent_mapped = pdg_mapped.
quark_content();
1429 excess_quark = {0, 0, 0, 0, 0};
1430 excess_antiq = {0, 0, 0, 0, 0};
1431 for (
int i = 0; i < 3; i++) {
1432 if (qcontent_actual[i] > 0) {
1433 int j = qcontent_actual[i] - 1;
1434 excess_quark[j] += 1;
1437 if (qcontent_mapped[i] > 0) {
1438 int j = qcontent_mapped[i] - 1;
1439 excess_quark[j] -= 1;
1442 if (qcontent_actual[i] < 0) {
1443 int j = std::abs(qcontent_actual[i]) - 1;
1444 excess_antiq[j] += 1;
1447 if (qcontent_mapped[i] < 0) {
1448 int j = std::abs(qcontent_mapped[i]) - 1;
1449 excess_antiq[j] -= 1;
1455 Pythia8::Particle& particle, std::array<int, 5>& excess_constituent) {
1457 if (!particle.isQuark() && !particle.isDiquark()) {
1462 const std::array<int, 5> excess_null = {0, 0, 0, 0, 0};
1463 if (excess_constituent == excess_null) {
1468 std::array<int, 2> pdgid = {0, 0};
1471 if (particle.isQuark()) {
1473 pdgid[0] = particle.id();
1474 }
else if (particle.isDiquark()) {
1479 for (
int iq = 0; iq < nq; iq++) {
1480 int jq = std::abs(pdgid[iq]) - 1;
1482 std::vector<int> k_found;
1485 if (excess_constituent[jq] < 0) {
1486 for (
int k = 0; k < 5; k++) {
1488 if (k != jq && excess_constituent[k] > 0) {
1489 k_found.push_back(k);
1495 if (k_found.size() > 0) {
1498 k_select = k_found[l];
1501 pdgid[iq] = pdgid[iq] > 0 ? k_select + 1 : -(k_select + 1);
1502 excess_constituent[jq] += 1;
1503 excess_constituent[k_select] -= 1;
1508 if (particle.isQuark()) {
1509 pdgid_new = pdgid[0];
1510 }
else if (particle.isDiquark()) {
1511 if (std::abs(pdgid[0]) < std::abs(pdgid[1])) {
1512 std::swap(pdgid[0], pdgid[1]);
1515 pdgid_new = std::abs(pdgid[0]) * 1000 + std::abs(pdgid[1]) * 100;
1516 if (std::abs(pdgid[0]) == std::abs(pdgid[1])) {
1519 pdgid_new += spin_deg;
1522 if (particle.id() < 0) {
1526 logg[
LPythia].debug(
" parton id = ", particle.id(),
" is converted to ",
1530 Pythia8::Vec4 pquark = particle.p();
1532 double e_new = std::sqrt(mass_new * mass_new + pquark.pAbs() * pquark.pAbs());
1534 particle.id(pdgid_new);
1536 particle.m(mass_new);
1540 Pythia8::Event& event_intermediate, std::array<int, 5>& nquark_total,
1541 std::array<int, 5>& nantiq_total) {
1542 for (
int iflav = 0; iflav < 5; iflav++) {
1543 nquark_total[iflav] = 0;
1544 nantiq_total[iflav] = 0;
1547 for (
int ip = 1; ip < event_intermediate.size(); ip++) {
1548 if (!event_intermediate[ip].isFinal()) {
1551 const int pdgid = event_intermediate[ip].id();
1554 for (
int iflav = 0; iflav < 5; iflav++) {
1555 nquark_total[iflav] +=
1560 for (
int iflav = 0; iflav < 5; iflav++) {
1561 nantiq_total[iflav] +=
pythia_hadron_->particleData.nQuarksInCode(
1562 std::abs(pdgid), iflav + 1);
1569 Pythia8::Event& event_intermediate, std::array<int, 5>& nquark_total,
1570 std::array<int, 5>& nantiq_total,
bool sign_constituent,
1571 std::array<std::array<int, 5>, 2>& excess_constituent) {
1572 Pythia8::Vec4 pSum = event_intermediate[0].p();
1578 for (
int iflav = 0; iflav < 5; iflav++) {
1585 excess_constituent[0][iflav] + excess_constituent[1][iflav];
1586 if (sign_constituent) {
1587 nquark_final += nquark_total[iflav];
1589 nquark_final += nantiq_total[iflav];
1594 bool enough_quark = nquark_final >= 0;
1597 if (!enough_quark) {
1598 logg[
LPythia].debug(
" not enough constituents with flavor ", iflav + 1,
1599 " : try to split a gluon to qqbar.");
1600 for (
int ic = 0; ic < std::abs(nquark_final); ic++) {
1604 if (excess_constituent[0][iflav] < 0) {
1613 for (
int ip = 2; ip < event_intermediate.size(); ip++) {
1614 if (!event_intermediate[ip].isFinal() ||
1615 !event_intermediate[ip].isGluon()) {
1619 const double y_gluon_current = event_intermediate[ip].y();
1620 const double y_gluon_forward = event_intermediate[iforward].y();
1621 if ((ih_mod == 0 && y_gluon_current > y_gluon_forward) ||
1622 (ih_mod == 1 && y_gluon_current < y_gluon_forward)) {
1627 if (!event_intermediate[iforward].isGluon()) {
1628 logg[
LPythia].debug(
"There is no gluon to split into qqbar.");
1633 Pythia8::Vec4 pgluon = event_intermediate[iforward].p();
1635 const int pdgid = iflav + 1;
1637 const int status = event_intermediate[iforward].status();
1641 const int col = event_intermediate[iforward].col();
1642 const int acol = event_intermediate[iforward].acol();
1645 std::array<double, 2> px_quark;
1646 std::array<double, 2> py_quark;
1647 std::array<double, 2> pz_quark;
1649 std::array<double, 2> e_quark;
1651 std::array<Pythia8::Vec4, 2> pquark;
1653 const double sigma_qt_frag =
pythia_hadron_->parm(
"StringPT:sigma");
1658 for (
int isign = 0; isign < 2; isign++) {
1662 px_quark[isign] = 0.5 * pgluon.px() + (isign == 0 ? 1. : -1.) * qx;
1663 py_quark[isign] = 0.5 * pgluon.py() + (isign == 0 ? 1. : -1.) * qy;
1664 pz_quark[isign] = 0.5 * pgluon.pz();
1666 std::sqrt(mass * mass + px_quark[isign] * px_quark[isign] +
1667 py_quark[isign] * py_quark[isign] +
1668 pz_quark[isign] * pz_quark[isign]);
1669 pquark[isign] = Pythia8::Vec4(px_quark[isign], py_quark[isign],
1670 pz_quark[isign], e_quark[isign]);
1675 pSum += pquark[0] + pquark[1] - pgluon;
1677 event_intermediate.append(pdgid, status, col, 0, pquark[0], mass);
1678 event_intermediate.append(-pdgid, status, 0, acol, pquark[1], mass);
1680 event_intermediate.remove(iforward, iforward);
1682 logg[
LPythia].debug(
" gluon at iforward = ", iforward,
1683 " is splitted into ", pdgid,
",", -pdgid,
1687 nquark_total[iflav] += 1;
1688 nantiq_total[iflav] += 1;
1695 event_intermediate[0].p(pSum);
1696 event_intermediate[0].m(pSum.mCalc());
1702 std::array<int, 5>& nquark_total,
1703 std::array<std::array<int, 5>, 2>& excess_quark,
1704 std::array<std::array<int, 5>, 2>& excess_antiq) {
1705 for (
int iflav = 0; iflav < 5; iflav++) {
1712 nquark_total[iflav] + excess_quark[0][iflav] + excess_quark[1][iflav];
1716 bool enough_quark = nquark_final >= 0;
1718 if (!enough_quark) {
1719 logg[
LPythia].debug(
" not enough constituents with flavor ", iflav + 1,
1720 " : try to modify excess of constituents.");
1721 for (
int ic = 0; ic < std::abs(nquark_final); ic++) {
1725 if (excess_quark[0][iflav] < 0) {
1733 excess_quark[ih_mod][iflav] += 1;
1734 excess_antiq[ih_mod][iflav] += 1;
1741 for (
int jflav = 0; jflav < 5; jflav++) {
1743 if (jflav != iflav && excess_quark[ih_mod][jflav] > 0) {
1746 excess_quark[ih_mod][jflav] -= 1;
1747 excess_antiq[ih_mod][jflav] -= 1;
1759 Pythia8::Event& event_intermediate,
1760 std::array<std::array<int, 5>, 2>& excess_quark,
1761 std::array<std::array<int, 5>, 2>& excess_antiq) {
1762 Pythia8::Vec4 pSum = event_intermediate[0].p();
1763 const double energy_init = pSum.e();
1764 logg[
LPythia].debug(
" initial total energy [GeV] : ", energy_init);
1767 std::array<int, 5> nquark_total;
1768 std::array<int, 5> nantiq_total;
1773 event_intermediate, nquark_total, nantiq_total,
true, excess_quark);
1775 event_intermediate, nquark_total, nantiq_total,
false, excess_antiq);
1779 if (!split_for_quark || !split_for_antiq) {
1785 for (
int iflav = 0; iflav < 5; iflav++) {
1786 if (nquark_total[iflav] + excess_quark[0][iflav] + excess_quark[1][iflav] <
1788 logg[
LPythia].debug(
"Not enough quark constituents of flavor ",
1793 if (nantiq_total[iflav] + excess_antiq[0][iflav] + excess_antiq[1][iflav] <
1795 logg[
LPythia].debug(
"Not enough antiquark constituents of flavor ",
1801 for (
int ih = 0; ih < 2; ih++) {
1802 logg[
LPythia].debug(
" initial excess_quark[", ih,
"] = (",
1803 excess_quark[ih][0],
", ", excess_quark[ih][1],
", ",
1804 excess_quark[ih][2],
", ", excess_quark[ih][3],
", ",
1805 excess_quark[ih][4],
")");
1806 logg[
LPythia].debug(
" initial excess_antiq[", ih,
"] = (",
1807 excess_antiq[ih][0],
", ", excess_antiq[ih][1],
", ",
1808 excess_antiq[ih][2],
", ", excess_antiq[ih][3],
", ",
1809 excess_antiq[ih][4],
")");
1812 bool recovered_quarks =
false;
1813 while (!recovered_quarks) {
1816 std::array<bool, 2> find_forward = {
true,
false};
1817 const std::array<int, 5> excess_null = {0, 0, 0, 0, 0};
1818 std::array<int, 5> excess_total = excess_null;
1820 for (
int ih = 0; ih < 2; ih++) {
1821 int nfrag = event_intermediate.size();
1822 for (
int np_end = 0; np_end < nfrag - 1; np_end++) {
1829 pSum -= event_intermediate[iforward].p();
1831 if (event_intermediate[iforward].
id() > 0) {
1834 " excess_quark[", ih,
"] = (", excess_quark[ih][0],
", ",
1835 excess_quark[ih][1],
", ", excess_quark[ih][2],
", ",
1836 excess_quark[ih][3],
", ", excess_quark[ih][4],
")");
1840 " excess_antiq[", ih,
"] = (", excess_antiq[ih][0],
", ",
1841 excess_antiq[ih][1],
", ", excess_antiq[ih][2],
", ",
1842 excess_antiq[ih][3],
", ", excess_antiq[ih][4],
")");
1845 const int pdgid = event_intermediate[iforward].id();
1846 Pythia8::Vec4 pquark = event_intermediate[iforward].p();
1847 const double mass = pquark.mCalc();
1849 const int status = event_intermediate[iforward].status();
1850 const int color = event_intermediate[iforward].col();
1851 const int anticolor = event_intermediate[iforward].acol();
1854 event_intermediate.append(pdgid, status, color, anticolor, pquark,
1857 event_intermediate.remove(iforward, iforward);
1863 for (
int j = 0; j < 5; j++) {
1864 excess_total[j] += (excess_quark[ih][j] - excess_antiq[ih][j]);
1870 recovered_quarks = excess_total == excess_null;
1872 logg[
LPythia].debug(
" valence quark contents of hadons are recovered.");
1874 logg[
LPythia].debug(
" current total energy [GeV] : ", pSum.e());
1878 if (std::abs(pSum.e() - energy_init) <=
1883 double energy_current = pSum.e();
1885 for (
int i = 1; i < event_intermediate.size(); i++) {
1886 slope += event_intermediate[i].pAbs2() / event_intermediate[i].e();
1889 const double rescale_factor = 1. + (energy_init - energy_current) / slope;
1891 for (
int i = 1; i < event_intermediate.size(); i++) {
1892 const double px = rescale_factor * event_intermediate[i].px();
1893 const double py = rescale_factor * event_intermediate[i].py();
1894 const double pz = rescale_factor * event_intermediate[i].pz();
1895 const double pabs = rescale_factor * event_intermediate[i].pAbs();
1896 const double mass = event_intermediate[i].m();
1898 event_intermediate[i].px(px);
1899 event_intermediate[i].py(py);
1900 event_intermediate[i].pz(pz);
1901 event_intermediate[i].e(std::sqrt(mass * mass + pabs * pabs));
1902 pSum += event_intermediate[i].p();
1904 logg[
LPythia].debug(
" parton momenta are rescaled by factor of ",
1908 logg[
LPythia].debug(
" final total energy [GeV] : ", pSum.e());
1911 event_intermediate[0].p(pSum);
1912 event_intermediate[0].m(pSum.mCalc());
1918 Pythia8::Event& event_intermediate,
1919 Pythia8::Event& event_hadronize) {
1920 Pythia8::Vec4 pSum = 0.;
1921 event_hadronize.reset();
1925 logg[
LPythia].debug(
"Hard non-diff: iforward = ", iforward,
"(",
1926 event_intermediate[iforward].
id(),
")");
1928 pSum += event_intermediate[iforward].p();
1929 event_hadronize.append(event_intermediate[iforward]);
1931 int col_to_find = event_intermediate[iforward].acol();
1932 int acol_to_find = event_intermediate[iforward].col();
1933 event_intermediate.remove(iforward, iforward);
1934 logg[
LPythia].debug(
"Hard non-diff: event_intermediate reduces in size to ",
1935 event_intermediate.size());
1938 while (col_to_find != 0 || acol_to_find != 0) {
1939 logg[
LPythia].debug(
" col_to_find = ", col_to_find,
1940 ", acol_to_find = ", acol_to_find);
1943 for (
int i = 1; i < event_intermediate.size(); i++) {
1944 const int pdgid = event_intermediate[i].id();
1946 col_to_find != 0 && col_to_find == event_intermediate[i].col();
1948 acol_to_find != 0 && acol_to_find == event_intermediate[i].acol();
1950 logg[
LPythia].debug(
" col_to_find ", col_to_find,
" from i ", i,
"(",
1954 logg[
LPythia].debug(
" acol_to_find ", acol_to_find,
" from i ", i,
"(",
1958 if (found_col && !found_acol) {
1960 col_to_find = event_intermediate[i].acol();
1962 }
else if (!found_col && found_acol) {
1964 acol_to_find = event_intermediate[i].col();
1966 }
else if (found_col && found_acol) {
1975 event_intermediate.list();
1976 event_intermediate.listJunctions();
1977 event_hadronize.list();
1978 event_hadronize.listJunctions();
1979 if (col_to_find != 0) {
1980 logg[
LPythia].error(
"No parton with col = ", col_to_find);
1982 if (acol_to_find != 0) {
1983 logg[
LPythia].error(
"No parton with acol = ", acol_to_find);
1985 throw std::runtime_error(
"Hard string could not be identified.");
1987 pSum += event_intermediate[ifound].p();
1989 event_hadronize.append(event_intermediate[ifound]);
1992 event_intermediate.remove(ifound, ifound);
1994 "Hard non-diff: event_intermediate reduces in size to ",
1995 event_intermediate.size());
2001 event_hadronize[0].p(pSum);
2002 event_hadronize[0].m(pSum.mCalc());
2006 Pythia8::Event& event_intermediate,
2007 Pythia8::Event& event_hadronize) {
2008 event_hadronize.reset();
2016 const int kind = event_intermediate.kindJunction(0);
2017 bool sign_color = kind % 2 == 1;
2018 std::vector<int> col;
2019 for (
int j = 0; j < 3; j++) {
2020 col.push_back(event_intermediate.colJunction(0, j));
2022 event_hadronize.appendJunction(kind, col[0], col[1], col[2]);
2023 event_intermediate.eraseJunction(0);
2024 logg[
LPythia].debug(
"junction (", col[0],
", ", col[1],
", ", col[2],
2025 ") with kind ", kind,
" will be handled.");
2027 bool found_string =
false;
2028 while (!found_string) {
2031 found_string =
true;
2032 for (
unsigned int j = 0; j < col.size(); j++) {
2033 found_string = found_string && col[j] == 0;
2035 if (!found_string) {
2038 logg[
LPythia].debug(
" still has leg(s) unfinished.");
2039 sign_color = !sign_color;
2040 std::vector<int> junction_to_move;
2041 for (
int i = 0; i < event_intermediate.sizeJunction(); i++) {
2042 const int kind_new = event_intermediate.kindJunction(i);
2046 if (sign_color != (kind_new % 2 == 1)) {
2050 std::array<int, 3> col_new;
2051 for (
int k = 0; k < 3; k++) {
2052 col_new[k] = event_intermediate.colJunction(i, k);
2055 int n_legs_connected = 0;
2057 for (
unsigned int j = 0; j < col.size(); j++) {
2061 for (
int k = 0; k < 3; k++) {
2062 if (col[j] == col_new[k]) {
2063 n_legs_connected += 1;
2071 if (n_legs_connected > 0) {
2072 for (
int k = 0; k < 3; k++) {
2073 if (col_new[k] != 0) {
2074 col.push_back(col_new[k]);
2078 event_intermediate.colJunction(i, 0),
", ",
2079 event_intermediate.colJunction(i, 1),
", ",
2080 event_intermediate.colJunction(i, 2),
2081 ") with kind ", kind_new,
" will be added.");
2082 junction_to_move.push_back(i);
2088 for (
unsigned int i = 0; i < junction_to_move.size(); i++) {
2089 unsigned int imove = junction_to_move[i] - i;
2090 const int kind_add = event_intermediate.kindJunction(imove);
2091 std::array<int, 3> col_add;
2092 for (
int k = 0; k < 3; k++) {
2093 col_add[k] = event_intermediate.colJunction(imove, k);
2096 event_hadronize.appendJunction(kind_add, col_add[0], col_add[1],
2099 event_intermediate.eraseJunction(imove);
2104 Pythia8::Vec4 pSum = event_hadronize[0].p();
2105 find_forward_string = pSum.pz() > 0.;
2109 Pythia8::Event& event_intermediate,
2110 Pythia8::Event& event_hadronize) {
2111 Pythia8::Vec4 pSum = event_hadronize[0].p();
2112 for (
unsigned int j = 0; j < col.size(); j++) {
2116 bool found_leg =
false;
2117 while (!found_leg) {
2119 for (
int i = 1; i < event_intermediate.size(); i++) {
2120 const int pdgid = event_intermediate[i].id();
2121 if (sign_color && col[j] == event_intermediate[i].col()) {
2122 logg[
LPythia].debug(
" col[", j,
"] = ", col[j],
" from i ", i,
"(",
2125 col[j] = event_intermediate[i].acol();
2127 }
else if (!sign_color && col[j] == event_intermediate[i].acol()) {
2128 logg[
LPythia].debug(
" acol[", j,
"] = ", col[j],
" from i ", i,
"(",
2131 col[j] = event_intermediate[i].col();
2138 if (event_intermediate.sizeJunction() == 0) {
2139 event_intermediate.list();
2140 event_intermediate.listJunctions();
2141 event_hadronize.list();
2142 event_hadronize.listJunctions();
2143 logg[
LPythia].error(
"No parton with col = ", col[j],
2144 " connected with junction leg ", j);
2145 throw std::runtime_error(
"Hard string could not be identified.");
2148 pSum += event_intermediate[ifound].p();
2150 event_hadronize.append(event_intermediate[ifound]);
2152 event_intermediate.remove(ifound, ifound);
2154 "Hard non-diff: event_intermediate reduces in size to ",
2155 event_intermediate.size());
2165 event_hadronize[0].p(pSum);
2166 event_hadronize[0].m(pSum.mCalc());
2170 ThreeVector& evec_polar, std::array<ThreeVector, 3>& evec_basis) {
2173 if (std::abs(evec_polar.
x3()) < (1. - 1.0e-8)) {
2178 evec_basis[0] = evec_polar;
2180 theta = std::acos(evec_basis[0].x3());
2182 ex = evec_basis[0].x1();
2183 ey = evec_basis[0].x2();
2184 et = std::sqrt(ex * ex + ey * ey);
2186 phi = std::acos(ex / et);
2188 phi = -std::acos(ex / et);
2193 evec_basis[1].set_x1(std::cos(theta) * std::cos(phi));
2194 evec_basis[1].set_x2(std::cos(theta) * std::sin(phi));
2195 evec_basis[1].set_x3(-std::sin(theta));
2197 evec_basis[2].set_x1(-std::sin(phi));
2198 evec_basis[2].set_x2(std::cos(phi));
2199 evec_basis[2].set_x3(0.);
2202 if (evec_polar.
x3() > 0.) {
2213 assert(std::fabs(evec_basis[1] * evec_basis[2]) <
really_small);
2214 assert(std::fabs(evec_basis[2] * evec_basis[0]) <
really_small);
2215 assert(std::fabs(evec_basis[0] * evec_basis[1]) <
really_small);
2228 assert((std::abs(diquark) > 1000) && (std::abs(diquark) < 5510) &&
2229 (std::abs(diquark) % 100 < 10));
2232 deg_spin = std::abs(diquark) % 10;
2234 const int sign_anti = diquark > 0 ? 1 : -1;
2237 q1 = sign_anti * (std::abs(diquark) - (std::abs(diquark) % 1000)) / 1000;
2238 q2 = sign_anti * (std::abs(diquark) % 1000 - deg_spin) / 100;
2242 assert((q1 > 0 && q2 > 0) || (q1 < 0 && q2 < 0));
2243 if (std::abs(q1) < std::abs(q2)) {
2246 int diquark = std::abs(q1 * 1000 + q2 * 100);
2251 return (q1 < 0) ? -diquark : diquark;
2298 std::swap(idq1, idq2);
2303 double suppression_factor) {
2318 ParticleList& list) {
2319 assert(list.size() >= 2);
2320 int end = list.size() - 1;
2323 i1 <= end && !list[i1].pdgcode().contains_enough_valence_quarks(nq1);
2327 i2 >= 0 && !list[i2].pdgcode().contains_enough_valence_quarks(nq2);
2330 std::pair<int, int> indices(i1, i2);
2335 ParticleList& outgoing_particles,
2337 double suppression_factor) {
2340 data.set_cross_section_scaling_factor(0.0);
2343 std::sort(outgoing_particles.begin(), outgoing_particles.end(),
2345 return i.momentum().velocity() * evecLong >
2346 j.momentum().velocity() * evecLong;
2349 switch (baryon_string) {
2363 throw std::runtime_error(
"string is neither mesonic nor baryonic");
2368 std::pair<int, int> i =
find_leading(nq1, nq2, outgoing_particles);
2369 std::pair<int, int> j =
find_leading(nq2, nq1, outgoing_particles);
2370 if (baryon_string == 0 && i.second - i.first < j.second - j.first) {
2373 suppression_factor);
2377 suppression_factor);
2397 throw std::runtime_error(
"StringProcess::pdg_map_for_pythia failed.");
Interface to the SMASH configuration files.
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
double sqr() const
calculate the square of the vector (which is a scalar)
FourVector lorentz_boost(const ThreeVector &v) const
Returns the FourVector boosted with velocity v.
ThreeVector velocity() const
Get the velocity (3-vector divided by zero component).
ParticleData contains the dynamic information of a certain particle.
PdgCode pdgcode() const
Get the pdgcode of the particle.
void set_4momentum(const FourVector &momentum_vector)
Set the particle's 4-momentum directly.
void set_formation_time(double form_time)
Set the absolute formation time.
void set_cross_section_scaling_factor(const double &xsec_scal)
Set the particle's initial cross_section_scaling_factor.
static const ParticleType & find(PdgCode pdgcode)
Returns the ParticleType object for the given pdgcode.
static const ParticleTypeList & list_all()
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
int baryon_number() const
int net_quark_number(const int quark) const
Returns the net number of quarks with given flavour number For public use, see strangeness(),...
std::array< int, 3 > quark_content() const
The return is always an array of three numbers, which are pdgcodes of quarks: 1 - d,...
static PdgCode from_decimal(const int pdgcode_decimal)
Construct PDG code from decimal number.
int32_t get_decimal() const
int charge() const
The charge of the particle.
Pythia8::StringFlav pythia_stringflav_
An object for the flavor selection in string fragmentation in the case of separate fragmentation func...
bool next_SDiff(bool is_AB_to_AX)
Single-diffractive process is based on single pomeron exchange described in Ingelman:1984ns .
pythia_map hard_map_
Map object to contain the different pythia objects.
Pythia8::SigmaTotal pythia_sigmatot_
An object to compute cross-sections.
double pow_fgluon_beta_
parameter for the gluon distribution function
@ LeadingQuark
Custom status assigned to leading (valence) quarks.
@ NonLeadingParton
Standard PYTHIA status for non-leading partons that should be hadronized.
@ LeadingDiquark
Standard PYTHIA beam-remnant status used for leading diquarks.
std::vector< Pythia8::Event > string_parton_events_
PYTHIA event records containing string partons to be hadronized.
Pythia8::Event event_intermediate_
event record for intermediate partonic state in the hard string routine
std::array< PdgCode, 2 > PDGcodes_
PdgCodes of incoming particles.
double time_formation_const_
constant proper time in the case of constant formation time [fm]
double PNegB_
backward lightcone momentum p^{-} of incoming particle B in CM-frame [GeV]
static FourVector make_smash_4vec(const Pythia8::Vec4 &p)
Convert a PYTHIA four-vector into a SMASH four-vector.
void find_junction_leg(bool sign_color, std::vector< int > &col, Pythia8::Event &event_intermediate, Pythia8::Event &event_hadronize)
Identify partons, which are associated with junction legs, from a given PYTHIA event record.
double pow_fquark_beta_
parameter for the quark distribution function
bool next_NDiffSoft()
Soft Non-diffractive process is modelled in accordance with dual-topological approach Capella:1978ig ...
bool next_DDiff()
Double-diffractive process ( A + B -> X + X ) is similar to the single-diffractive process,...
static int pdg_map_for_pythia(PdgCode &pdg)
Take pdg code and map onto particle specie which can be handled by PYTHIA.
double prob_proton_to_d_uu_
Probability of splitting a nucleon into the quark flavour it has only once and a diquark it has twice...
Pythia8::RotBstMatrix to_cm_
Rotation/boost matrix to transform particles to the center-of-mass frame.
FourVector ucomAB_
velocity four vector of the center of mass in the lab frame
ThreeVector vcomAB_
velocity three vector of the center of mass in the lab frame
void compute_incoming_lightcone_momenta()
compute the lightcone momenta of incoming particles where the longitudinal direction is set to be sam...
double popcorn_rate_
popcorn rate
double pmin_gluon_lightcone_
the minimum lightcone momentum scale carried by a gluon [GeV]
bool splitting_gluon_qqbar(Pythia8::Event &event_intermediate, std::array< int, 5 > &nquark_total, std::array< int, 5 > &nantiq_total, bool sign_constituent, std::array< std::array< int, 5 >, 2 > &excess_constituent)
Take total number of quarks and check if the system has enough constituents that need to be converted...
static std::pair< int, int > find_leading(int nq1, int nq2, ParticleList &list)
Find the leading string fragments.
double PPosA_
forward lightcone momentum p^{+} of incoming particle A in CM-frame [GeV]
std::optional< ParticleList > hadronize(const Pythia8::Event &string_evt)
Hadronize a single partonic string configuration using Pythia8 and convert the produced hadrons into ...
double sqrtsAB_
sqrt of Mandelstam variable s of collision [GeV]
ParticleList final_state_
final state array which must be accessed after the collision
bool is_leading_from_quark(const Pythia8::Particle &p)
Check whether a particle is tagged as originating from a leading quark.
double PNegA_
backward lightcone momentum p^{-} of incoming particle A in CM-frame [GeV]
bool append_string(const Pythia8::Vec4 &p_str, const std::array< int, 2 > &ends, int color_tag, bool use_projectile_axis, bool random_flip_of_endpoints=false)
Append a single two-endpoint string as an independent PYTHIA event.
bool use_monash_tune_
Whether to use the monash tune Skands:2014pea for all string processes.
double soft_t_form_
factor to be multiplied to formation times in soft strings
static void make_string_ends(const PdgCode &pdgcode_in, int &idq1, int &idq2, double xi)
make a random selection to determine partonic contents at the string ends.
std::unique_ptr< Pythia8::Pythia > pythia_hadron_
PYTHIA object used in fragmentation.
double massA_
mass of incoming particle A [GeV]
bool next_Hard(ProcessType type)
Hard Non-diffractive process is based on PYTHIA 8 with partonic showers and interactions.
void common_setup_pythia(Pythia8::Pythia *pythia_in, double strange_supp, double diquark_supp, double popcorn_rate, double stringz_a, double stringz_b, double string_sigma_T)
Common setup of PYTHIA objects for soft and hard string routines.
double PPosB_
forward lightcone momentum p^{+} of incoming particle B in CM-frame [GeV]
std::array< ThreeVector, 3 > evecBasisAB_
Orthonormal basis vectors in the center of mass frame, where the 0th one is parallel to momentum of i...
std::vector< std::string > pythia_settings_
Additional Pythia 8 settings passed to each internal Pythia instance.
std::optional< double > mpi_initialization_sqrts_
Optional center-of-mass energy used to initialize MPI-capable Pythia objects.
bool restore_constituent(Pythia8::Event &event_intermediate, std::array< std::array< int, 5 >, 2 > &excess_quark, std::array< std::array< int, 5 >, 2 > &excess_antiq)
Take the intermediate partonic state from PYTHIA event with mapped hadrons and convert constituents i...
double string_sigma_T_
transverse momentum spread in string fragmentation
static void make_orthonormal_basis(ThreeVector &evec_polar, std::array< ThreeVector, 3 > &evec_basis)
compute three orthonormal basis vectors from unit vector in the longitudinal direction
int get_index_forward(bool find_forward, int np_end, Pythia8::Event &event)
Obtain index of the most forward or backward particle in a given PYTHIA event record.
bool is_leading_parton(const Pythia8::Particle &p)
Check whether a particle is tagged as a leading parton.
static void convert_KaonLS(int &pythia_id)
convert Kaon-L or Kaon-S into K0 or Anti-K0
double additional_xsec_supp_
additional cross-section suppression factor to take coherence effect into account.
static void assign_all_scaling_factors(int baryon_string, ParticleList &outgoing_particles, const ThreeVector &evecLong, double suppression_factor)
Assign a cross section scaling factor to all outgoing particles.
double damp_popcorn_
damp popcorn meson from diquark remnant endpoint rate
double strange_supp_
strange quark suppression factor
bool string_above_threshold(const Pythia8::Event &event)
Check whether all strings in a PYTHIA event are above fragmentation threshold.
static int diquark_from_quarks(int q1, int q2)
Construct diquark from two quarks.
std::vector< bool > compute_beam_valence_flags(Pythia8::Pythia &pythia)
Compute flags identifying beam valence partons (quarks or diquarks) that act as leading partons after...
void replace_constituent(Pythia8::Particle &particle, std::array< int, 5 > &excess_constituent)
Convert a partonic PYTHIA particle into the desired species and update the excess of constituents.
double kappa_tension_string_
string tension [GeV/fm]
double pow_fquark_alpha_
parameter for the quark distribution function
bool is_leading(const Pythia8::Particle &p)
Check whether a particle is tagged as originating from a leading endpoint.
double stringz_b_leading_
parameter (StringZ:bLund) for the fragmentation function of leading baryon in soft non-diffractive st...
static bool append_intermediate_list(int pdgid, FourVector momentum, ParticleList &intermediate_particles)
append new particle from PYTHIA to a specific particle list
int leading_hadron_status_from_endpoint(const Pythia8::Particle &end)
Determine the custom leading-hadron status code from a string endpoint.
bool next(ProcessType type)
Generate the next string process for a given process type.
bool mass_dependent_formation_times_
Whether the formation time should depend on the mass of the fragment according to Andersson:1983ia e...
double stringz_b_produce_
parameter (StringZ:bLund) for the fragmentation function of other (produced) hadrons in soft non-diff...
bool next_BBbarAnn()
Baryon-antibaryon annihilation process Based on what UrQMD Bass:1998ca , Bleicher:1999xi does,...
void set_color_by_type(Pythia8::Particle &p, int color)
Set the color or anticolor index of a particle according to its type.
StringProcess(Configuration &config)
Constructor, initializes PYTHIA.
void tag_leading_hadrons(Pythia8::Event &event)
Tag leading hadrons in a hadronized string.
double stringz_a_leading_
parameter (StringZ:aLund) for the fragmentation function of leading baryon in soft non-diffractive st...
void init(const ParticleList &incoming, double tcoll)
initialization feed intial particles, time of collision and gamma factor of the center of mass.
bool is_leading_from_diquark(const Pythia8::Particle &p)
Check whether a particle is tagged as originating from a leading diquark.
double sigma_qperp_
Transverse momentum spread of the excited strings.
void rearrange_excess(std::array< int, 5 > &nquark_total, std::array< std::array< int, 5 >, 2 > &excess_quark, std::array< std::array< int, 5 >, 2 > &excess_antiq)
Take total number of quarks and check if the system has enough constituents that need to be converted...
static Pythia8::Vec4 make_pythia_4vec(const FourVector &p)
Convert a SMASH four-vector into a PYTHIA four-vector.
bool separate_fragment_baryon_
Whether to use a separate fragmentation function for leading baryons.
std::array< FourVector, 2 > plab_
momenta of incoming particles in the lab frame [GeV]
std::array< FourVector, 2 > pcom_
momenta of incoming particles in the center of mass frame [GeV]
double time_collision_
time of collision in the computational frame [fm]
void form_intermediate_particles(ParticleList &intermediate_particles, const FourVector &pString, const ThreeVector &evecLong, double additional_xsec_supp=1.0, bool find_and_scale_leading=true)
Set formation times and cross-section scaling factors for fragmented hadrons as described in Andersso...
double diquark_supp_
diquark suppression factor
static void find_excess_constituent(PdgCode &pdg_actual, PdgCode &pdg_mapped, std::array< int, 5 > &excess_quark, std::array< int, 5 > &excess_antiq)
Compare the valence quark contents of the actual and mapped hadrons and evaluate how many more consti...
double estimate_string_threshold(int p_left, int p_right)
Estimate the minimum invariant mass required for a string to fragment.
double massB_
mass of incoming particle B [GeV]
void compose_string_parton(bool find_forward_string, Pythia8::Event &event_intermediate, Pythia8::Event &event_hadronize)
Identify a set of partons, which are connected to form a color-neutral string, from a given PYTHIA ev...
void find_total_number_constituent(Pythia8::Event &event_intermediate, std::array< int, 5 > &nquark_total, std::array< int, 5 > &nantiq_total)
Compute how many quarks and antiquarks we have in the system, and update the correspoing arrays with ...
double stringz_a_produce_
parameter (StringZ:aLund) for the fragmentation function of other (produced) hadrons in soft non-diff...
static void assign_scaling_factor(int nquark, ParticleData &data, double suppression_factor)
Assign a cross section scaling factor to the given particle.
void compose_string_junction(bool &find_forward_string, Pythia8::Event &event_intermediate, Pythia8::Event &event_hadronize)
Identify a set of partons and junction(s), which are connected to form a color-neutral string,...
static void quarks_from_diquark(int diquark, int &q1, int &q2, int °_spin)
find two quarks from a diquark.
The ThreeVector class represents a physical three-vector with the components .
Discrete distribution with weight given by probability vector.
Collection of useful constants that are known at compile time.
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > & logg
An array that stores all pre-configured Logger objects.
T power(T n, T xMin, T xMax)
Sample from a power-law probability density proportional to |x|^n.
T beta_a0(T xmin, T b)
Draws a random number from a beta-distribution with a = 0.
T beta(T a, T b)
Draws a random number from a beta-distribution, where probability density of is .
double normal(const T &mean, const T &sigma)
Returns a random number drawn from a normal distribution.
T uniform_int(T min, T max)
T pCM(const T sqrts, const T mass_a, const T mass_b) noexcept
T pCM_sqr(const T sqrts, const T mass_a, const T mass_b) noexcept
constexpr int maximum_rndm_seed_in_pythia
The maximum value of the random seed used in PYTHIA.
ProcessType
ProcessTypes are used to identify the type of the process.
@ StringHardSingleDiffractiveAX
See here for a short description.
@ StringSoftDoubleDiffractive
See here for a short description.
@ StringSoftSingleDiffractiveXB
See here for a short description.
@ StringHardNonDiffractive
See here for a short description.
@ StringSoftAnnihilation
See here for a short description.
@ StringSoftNonDiffractive
See here for a short description.
@ StringSoftSingleDiffractiveAX
See here for a short description.
@ StringHardSingleDiffractiveXB
See here for a short description.
@ StringHardDoubleDiffractive
See here for a short description.
std::string to_string(ThermodynamicQuantity quantity)
Convert a ThermodynamicQuantity enum value to its corresponding string.
constexpr double pion_mass
Pion mass in GeV.
constexpr double really_small
Numerical error tolerance.
constexpr double kaon_mass
Kaon mass in GeV.
static constexpr int LPythia