220 const std::vector<bool>& nucleon_has_interacted,
int N_tot,
int N_proj)
221 : coll_crit_(config.take({
"Collision_Term",
"Collision_Criterion"},
224 config.take({
"Collision_Term",
"Elastic_Cross_Section"}, -1.)),
226 isotropic_(config.take({
"Collision_Term",
"Isotropic"},
false)),
238 {
"Collision_Term",
"String_Parameters",
"Formation_Time"}, 1.)) {
241 throw std::invalid_argument(
242 "The stochastic collision criterion is only supported for elastic (and " 243 "isotropic)\n2-to-2 reactions of one particle species. Change you " 244 "config accordingly.");
247 const auto& log = logger<LogArea::FindScatter>();
248 log.info(
"Constant elastic isotropic cross-section mode:",
" using ",
252 auto subconfig = config[
"Collision_Term"][
"String_Parameters"];
255 subconfig.take({
"Gluon_Beta"}, 0.5),
256 subconfig.take({
"Gluon_Pmin"}, 0.001),
257 subconfig.take({
"Quark_Alpha"}, 2.0),
258 subconfig.take({
"Quark_Beta"}, 7.0),
259 subconfig.take({
"Strange_Supp"}, 0.16),
260 subconfig.take({
"Diquark_Supp"}, 0.036),
261 subconfig.take({
"Sigma_Perp"}, 0.42),
262 subconfig.take({
"StringZ_A_Leading"}, 0.2),
263 subconfig.take({
"StringZ_B_Leading"}, 2.0),
264 subconfig.take({
"StringZ_A"}, 2.0), subconfig.take({
"StringZ_B"}, 0.55),
265 subconfig.take({
"String_Sigma_T"}, 0.5),
266 subconfig.take({
"Form_Time_Factor"}, 1.0),
267 subconfig.take({
"Mass_Dependent_Formation_Times"},
false),
268 subconfig.take({
"Prob_proton_to_d_uu"}, 1. / 3.),
269 subconfig.take({
"Separate_Fragment_Baryon"},
true),
270 subconfig.take({
"Popcorn_Rate"}, 0.15));
276 const std::vector<FourVector>& beam_momentum,
const double cell_vol)
const {
277 const auto& log = logger<LogArea::FindScatter>();
284 assert(data_a.
id() >= 0);
285 assert(data_b.
id() >= 0);
295 const double time_until_collision =
299 if (time_until_collision < 0. || time_until_collision >= dt) {
304 ScatterActionPtr act = make_unique<ScatterAction>(
322 const double xs = act->cross_section() *
fm2_mb;
325 const double m1 = act->incoming_particles()[0].effective_mass();
326 const double m1_sqr = m1 * m1;
327 const double m2 = act->incoming_particles()[1].effective_mass();
328 const double m2_sqr = m2 * m2;
329 const double e1 = act->incoming_particles()[0].momentum().x0();
330 const double e2 = act->incoming_particles()[1].momentum().x0();
331 const double m_s = act->mandelstam_s();
332 const double lambda = (m_s - m1_sqr - m2_sqr) * (m_s - m1_sqr - m2_sqr) -
333 4. * m1_sqr * m2_sqr;
334 const double v_rel = std::sqrt(lambda) / (2. * e1 * e2);
337 const double p_22 = xs * v_rel * dt / (cell_vol *
testparticles_);
339 log.debug(
"Stochastic collison criterion parameters:\np_22 = ", p_22,
340 ", xs = ", xs,
", v_rel = ", v_rel,
", dt = ", dt,
344 std::stringstream err;
345 err <<
"Probability larger than 1 for stochastic rates. ( P = " << p_22
346 <<
" )\nUse smaller timesteps.";
347 throw std::runtime_error(err.str());
352 if (random_no > p_22) {
358 if (data_a.id_process() > 0 && data_a.id_process() == data_b.id_process()) {
359 log.debug(
"Skipping collided particles at time ", data_a.position().x0(),
360 " due to process ", data_a.id_process(),
"\n ", data_a,
365 const double distance_squared = act->transverse_distance_sqr();
378 double cross_section_criterion = act->cross_section() *
fm2_mb * M_1_PI /
382 cross_section_criterion *= data_a.xsec_scaling_factor(time_until_collision);
383 cross_section_criterion *= data_b.xsec_scaling_factor(time_until_collision);
386 if (distance_squared >= cross_section_criterion) {
390 log.debug(
"particle distance squared: ", distance_squared,
"\n ", data_a,
396 return std::move(act);
400 const ParticleList& search_list,
double dt,
const double cell_vol,
401 const std::vector<FourVector>& beam_momentum)
const {
402 std::vector<ActionPtr> actions;
405 if (p1.id() < p2.id()) {
409 actions.push_back(std::move(act));
418 const ParticleList& search_list,
const ParticleList& neighbors_list,
419 double dt,
const std::vector<FourVector>& beam_momentum)
const {
420 std::vector<ActionPtr> actions;
427 assert(p1.id() != p2.id());
431 actions.push_back(std::move(act));
439 const ParticleList& search_list,
const Particles& surrounding_list,
440 double dt,
const std::vector<FourVector>& beam_momentum)
const {
441 std::vector<ActionPtr> actions;
449 auto result = std::find_if(
450 search_list.begin(), search_list.end(),
452 if (result != search_list.end()) {
459 actions.push_back(std::move(act));
467 constexpr
double time = 0.0;
470 const size_t N_pairs = N_isotypes * (N_isotypes - 1) / 2;
472 std::cout << N_isotypes <<
" iso-particle types." << std::endl;
473 std::cout <<
"They can make " << N_pairs <<
" pairs." << std::endl;
474 std::vector<double> momentum_scan_list = {0.1, 0.3, 0.5, 1.0,
475 2.0, 3.0, 5.0, 10.0};
478 if (&A_isotype > &B_isotype) {
481 bool any_nonzero_cs =
false;
482 std::vector<std::string> r_list;
485 if (A_type > B_type) {
489 for (
auto mom : momentum_scan_list) {
490 A.set_4momentum(A.pole_mass(), mom, 0.0, 0.0);
492 ScatterActionPtr act = make_unique<ScatterAction>(
501 const double total_cs = act->cross_section();
502 if (total_cs <= 0.0) {
505 any_nonzero_cs =
true;
506 for (
const auto& channel : act->collision_channels()) {
507 const auto type = channel->get_type();
511 r = A_type->name() + B_type->name() + std::string(
" → strings");
515 ? std::string(
" (el)")
517 ? std::string(
" (inel)")
518 : std::string(
" (?)");
519 r = A_type->name() + B_type->name() + std::string(
" → ") +
520 channel->particle_types()[0]->name() +
521 channel->particle_types()[1]->name() + r_type;
529 std::sort(r_list.begin(), r_list.end());
530 r_list.erase(std::unique(r_list.begin(), r_list.end()), r_list.end());
531 if (any_nonzero_cs) {
532 for (
auto r : r_list) {
534 if (r_list.back() != r) {
538 std::cout << std::endl;
565 : name_(name), cross_section_(cross_section), mass_(mass) {}
568 namespace decaytree {
616 Node(
const std::string& name,
double weight,
617 ParticleTypePtrList&& initial_particles,
618 ParticleTypePtrList&& final_particles, ParticleTypePtrList&& state,
619 std::vector<Node>&& children)
622 initial_particles_(
std::move(initial_particles)),
623 final_particles_(
std::move(final_particles)),
624 state_(
std::move(state)),
625 children_(
std::move(children)) {}
639 ParticleTypePtrList&& initial_particles,
640 ParticleTypePtrList&& final_particles) {
642 ParticleTypePtrList state(state_);
643 for (
const auto&
p : initial_particles) {
644 state.erase(std::find(state.begin(), state.end(),
p));
646 for (
const auto&
p : final_particles) {
650 std::sort(state.begin(), state.end(),
652 return a->
name() < b->name();
655 Node new_node(name, weight, std::move(initial_particles),
656 std::move(final_particles), std::move(state), {});
657 children_.emplace_back(std::move(new_node));
658 return children_.back();
662 void print()
const { print_helper(0); }
668 std::vector<FinalStateCrossSection> result;
669 final_state_cross_sections_helper(0, result,
"", 1.);
681 for (uint64_t i = 0; i < depth; i++) {
684 std::cout << name_ <<
" " << weight_ << std::endl;
685 for (
const auto& child : children_) {
686 child.print_helper(depth + 1);
702 uint64_t depth, std::vector<FinalStateCrossSection>& result,
703 const std::string& name,
double weight,
704 bool show_intermediate_states =
false)
const {
712 std::string new_name;
715 if (show_intermediate_states) {
717 if (!new_name.empty()) {
725 for (
const auto& s : state_) {
726 new_name += s->name();
729 if (show_intermediate_states) {
733 if (children_.empty()) {
737 for (
const auto& child : children_) {
738 child.final_state_cross_sections_helper(depth + 1, result, new_name,
739 weight, show_intermediate_states);
753 const DecayBranchPtr& decay,
754 ParticleTypePtrList& final_state) {
755 std::stringstream name;
756 name <<
"[" << res_name <<
"->";
757 for (
const auto&
p : decay->particle_types()) {
759 final_state.push_back(
p);
782 uint32_t n_unstable = 0;
783 double sqrts_minus_masses = sqrts;
788 sqrts_minus_masses -= ptype->
mass();
791 n_unstable != 0 ? 1. /
static_cast<double>(n_unstable) : 1.;
795 const double sqrts_decay = sqrts_minus_masses + ptype->
mass();
796 bool can_decay =
false;
801 double final_state_mass = 0.;
802 for (
const auto&
p : decay->particle_types()) {
803 final_state_mass +=
p->mass();
805 if (final_state_mass > sqrts_decay) {
810 ParticleTypePtrList parts;
812 auto& new_node = node.
add_action(name, norm * decay->weight(), {ptype},
833 static void deduplicate(std::vector<FinalStateCrossSection>& final_state_xs) {
834 std::sort(final_state_xs.begin(), final_state_xs.end(),
837 auto current = final_state_xs.begin();
838 while (current != final_state_xs.end()) {
839 auto adjacent = std::adjacent_find(
840 current, final_state_xs.end(),
842 return a.
name_ == b.name_;
845 if (adjacent != final_state_xs.end()) {
846 adjacent->cross_section_ += (adjacent + 1)->cross_section_;
847 final_state_xs.erase(adjacent + 1);
854 bool final_state, std::vector<double>& plab)
const {
855 typedef std::vector<std::pair<double, double>> xs_saver;
856 std::map<std::string, xs_saver> xs_dump;
857 std::map<std::string, double> outgoing_total_mass;
860 int n_momentum_points = 200;
861 constexpr
double momentum_step = 0.02;
868 if (plab.size() > 0) {
869 n_momentum_points = plab.size();
871 std::sort(plab.begin(), plab.end());
872 plab.erase(std::unique(plab.begin(), plab.end()), plab.end());
874 for (
int i = 0; i < n_momentum_points; i++) {
876 if (plab.size() > 0) {
879 momentum = momentum_step * (i + 1);
881 a_data.set_4momentum(m_a, momentum, 0.0, 0.0);
883 const double sqrts = (a_data.momentum() + b_data.
momentum()).abs();
884 const ParticleList incoming = {a_data, b_data};
885 ScatterActionPtr act = make_unique<ScatterAction>(
894 {&a, &b}, {&a, &b}, {});
895 const CollisionBranchList& processes = act->collision_channels();
896 for (
const auto& process : processes) {
897 const double xs = process->weight();
902 std::stringstream process_description_stream;
903 process_description_stream << *process;
904 const std::string& description = process_description_stream.str();
906 for (
const auto& ptype : process->particle_types()) {
907 m_tot += ptype->mass();
909 outgoing_total_mass[description] = m_tot;
910 if (!xs_dump[description].empty() &&
911 std::abs(xs_dump[description].back().first - sqrts) <
913 xs_dump[description].back().second += xs;
915 xs_dump[description].push_back(std::make_pair(sqrts, xs));
918 std::stringstream process_description_stream;
919 process_description_stream << *process;
920 const std::string& description = process_description_stream.str();
921 ParticleTypePtrList initial_particles = {&a, &b};
922 ParticleTypePtrList final_particles = process->particle_types();
924 tree.add_action(description, xs, std::move(initial_particles),
925 std::move(final_particles));
929 xs_dump[
"total"].push_back(std::make_pair(sqrts, act->cross_section()));
931 outgoing_total_mass[
"total"] = -1.0;
934 auto final_state_xs = tree.final_state_cross_sections();
936 for (
const auto&
p : final_state_xs) {
944 outgoing_total_mass[
p.name_] =
p.mass_;
945 xs_dump[
p.name_].push_back(std::make_pair(sqrts,
p.cross_section_));
952 for (
auto it = begin(xs_dump); it != end(xs_dump);) {
954 const xs_saver& xs = (*it).second;
956 for (
const auto&
p : xs) {
960 it = xs_dump.erase(it);
967 std::vector<std::string> all_channels;
968 for (
const auto channel : xs_dump) {
969 all_channels.push_back(channel.first);
971 std::sort(all_channels.begin(), all_channels.end(),
972 [&](
const std::string& str_a,
const std::string& str_b) {
973 return outgoing_total_mass[str_a] < outgoing_total_mass[str_b];
977 std::cout <<
"# Dumping partial " << a.
name() << b.
name()
978 <<
" cross-sections in mb, energies in GeV" << std::endl;
979 std::cout <<
" sqrt_s";
983 for (
const auto channel : all_channels) {
986 std::cout << std::endl;
989 for (
int i = 0; i < n_momentum_points; i++) {
991 if (plab.size() > 0) {
994 momentum = momentum_step * (i + 1);
996 a_data.set_4momentum(m_a, momentum, 0.0, 0.0);
998 const double sqrts = (a_data.momentum() + b_data.
momentum()).abs();
999 std::printf(
"%9.6f", sqrts);
1000 for (
const auto channel : all_channels) {
1001 const xs_saver energy_and_xs = xs_dump[channel];
1003 for (; j < energy_and_xs.size() && energy_and_xs[j].first < sqrts; j++) {
1006 if (j < energy_and_xs.size() &&
1007 std::abs(energy_and_xs[j].first - sqrts) <
really_small) {
1008 xs = energy_and_xs[j].second;
1010 std::printf(
"%16.6f", xs);
static void add_decays(Node &node, double sqrts)
Add nodes for all decays possible from the given node and all of its children.
ActionList find_actions_in_cell(const ParticleList &search_list, double dt, const double cell_vol, const std::vector< FourVector > &beam_momentum) const override
Search for all the possible collisions within one cell.
std::string name_
Name of the final state.
double mass_
Total mass of final state particles.
ActionList find_actions_with_surrounding_particles(const ParticleList &search_list, const Particles &surrounding_list, double dt, const std::vector< FourVector > &beam_momentum) const override
Search for all the possible secondary collisions between the outgoing particles and the rest...
static void deduplicate(std::vector< FinalStateCrossSection > &final_state_xs)
Deduplicate the final-state cross sections by summing.
const int testparticles_
Number of test particles.
constexpr double really_small
Numerical error tolerance.
(Default) geometric criterion.
const ReactionsBitSet incl_set_
List of included 2<->2 reactions.
bool is_string_soft_process(ProcessType p)
Check if a given process type is a soft string excitation.
bool is_constant_elastic_isotropic() const
If there is only one particle sort, no decays (only elastic scatterings are possible), scatterings are isotropic and cross-section fixed to elastic_parameter_ independently on momenta, then maximal cross-section is elastic_parameter_.
const bool two_to_one_
Enable 2->1 processes.
static std::string make_decay_name(const std::string &res_name, const DecayBranchPtr &decay, ParticleTypePtrList &final_state)
Generate name for decay and update final state.
const double low_snn_cut_
Elastic collsions between two nucleons with sqrt_s below low_snn_cut_ are excluded.
std::string fill_left(const std::string &s, size_t width, char fill= ' ')
Fill string with characters to the left until the given width is reached.
const double string_formation_time_
Parameter for formation time.
void isoclean(std::string &s)
Remove ⁺, ⁻, ⁰ from string.
Collection of useful constants that are known at compile time.
const bool strings_with_probability_
Decide whether to implement string fragmentation based on a probability.
T pCM_from_s(const T s, const T mass_a, const T mass_b) noexcept
double max_transverse_distance_sqr(int testparticles) const
The maximal distance over which particles can interact, related to the number of test particles and t...
constexpr double fm2_mb
mb <-> fm^2 conversion factor.
2->2 inelastic scattering
std::vector< Node > children_
Possible actions after this action.
const std::vector< bool > & nucleon_has_interacted_
Parameter to record whether the nucleon has experienced a collision or not.
Represent a final-state cross section.
Interface to the SMASH configuration files.
std::unique_ptr< StringProcess > string_process_interface_
Class that deals with strings, interfacing Pythia.
const bool strings_switch_
Switch to turn off string excitation.
void dump_cross_sections(const ParticleType &a, const ParticleType &b, double m_a, double m_b, bool final_state, std::vector< double > &plab) const
Print out partial cross-sections of all processes that can occur in the collision of a(mass = m_a) an...
const DecayBranchList & decay_mode_list() const
ParticleTypePtrList initial_particles_
Initial-state particle types in this action.
std::string name_
Name for printing.
void final_state_cross_sections_helper(uint64_t depth, std::vector< FinalStateCrossSection > &result, const std::string &name, double weight, bool show_intermediate_states=false) const
Internal helper function for final_state_cross_sections, to be called recursively to calculate all fi...
elastic scattering: particles remain the same, only momenta change
double s_from_plab(double plab, double m_P, double m_T)
Convert p_lab to Mandelstam-s for a fixed-target setup, with a projectile of mass m_P and momentum pl...
double weight_
Weight (cross section or branching ratio).
const std::string & name() const
Particle type contains the static properties of a particle species.
ScatterActionsFinder(Configuration config, const ExperimentParameters ¶meters, const std::vector< bool > &nucleon_has_interacted, int N_tot, int N_proj)
Constructor of the finder with the given parameters.
void set_4momentum(const FourVector &momentum_vector)
Set the particle's 4-momentum directly.
IsoParticleType is a class to represent isospin multiplets.
const NNbarTreatment nnbar_treatment_
Switch for NNbar reactions.
ActionPtr check_collision(const ParticleData &data_a, const ParticleData &data_b, double dt, const std::vector< FourVector > &beam_momentum={}, const double cell_vol=0.0) const
Check for a single pair of particles (id_a, id_b) if a collision will happen in the next timestep and...
hard string process involving 2->2 QCD process by PYTHIA.
FinalStateCrossSection(const std::string &name, double cross_section, double mass)
Construct a final-state cross section.
std::vector< FinalStateCrossSection > final_state_cross_sections() const
ParticleTypePtrList final_particles_
Final-state particle types in this action.
void dump_reactions() const
Prints out all the 2-> n (n > 1) reactions with non-zero cross-sections between all possible pairs of...
ActionList find_actions_with_neighbors(const ParticleList &search_list, const ParticleList &neighbors_list, double dt, const std::vector< FourVector > &beam_momentum) const override
Search for all the possible collisions among the neighboring cells.
double pole_mass() const
Get the particle's pole mass ("on-shell").
const bool use_AQM_
Switch to control whether to use AQM or not.
const int N_proj_
Record the number of the nucleons in the projectile.
const bool isotropic_
Do all collisions isotropically.
void print_helper(uint64_t depth) const
Internal helper function for print, to be called recursively to print all nodes.
const int N_tot_
Record the total number of the nucleons in the two colliding nuclei.
A pointer-like interface to global references to ParticleType objects.
Node of a decay tree, representing a possible action (2-to-2 or 1-to-2).
ParticleTypePtrList state_
Particle types corresponding to the global state after this action.
void print() const
Print the decay tree starting with this node.
The Particles class abstracts the storage and manipulation of particles.
double collision_time(const ParticleData &p1, const ParticleData &p2, double dt, const std::vector< FourVector > &beam_momentum) const
Determine the collision time of the two particles.
const DecayModes & decay_modes() const
Node(const std::string &name, double weight, ParticleTypePtrList &&initial_particles, ParticleTypePtrList &&final_particles, ParticleTypePtrList &&state, std::vector< Node > &&children)
int32_t id() const
Get the id of the particle.
double cross_section_
Corresponding cross section in mb.
Node & add_action(const std::string &name, double weight, ParticleTypePtrList &&initial_particles, ParticleTypePtrList &&final_particles)
Add an action to the children of this node.
Helper structure for Experiment.
ParticleData contains the dynamic information of a certain particle.
const double elastic_parameter_
Elastic cross section parameter (in mb).
const FourVector & momentum() const
Get the particle's 4-momentum.
static const IsoParticleTypeList & list_all()
Returns a list of all IsoParticleTypes.
const CollisionCriterion coll_crit_
Specifies which collision criterion is used.