Version: SMASH-1.7
scatteractionsfinder.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2019
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
11 
12 #include <algorithm>
13 #include <map>
14 #include <vector>
15 
16 #include "smash/configuration.h"
17 #include "smash/constants.h"
18 #include "smash/crosssections.h"
19 #include "smash/cxx14compat.h"
20 #include "smash/decaymodes.h"
22 #include "smash/isoparticletype.h"
23 #include "smash/logging.h"
24 #include "smash/macros.h"
25 #include "smash/particles.h"
26 #include "smash/scatteraction.h"
28 #include "smash/stringfunctions.h"
29 
30 namespace smash {
219  Configuration config, const ExperimentParameters& parameters,
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.)),
225  testparticles_(parameters.testparticles),
226  isotropic_(config.take({"Collision_Term", "Isotropic"}, false)),
227  two_to_one_(parameters.two_to_one),
228  incl_set_(parameters.included_2to2),
229  low_snn_cut_(parameters.low_snn_cut),
230  strings_switch_(parameters.strings_switch),
231  use_AQM_(parameters.use_AQM),
232  strings_with_probability_(parameters.strings_with_probability),
233  nnbar_treatment_(parameters.nnbar_treatment),
234  nucleon_has_interacted_(nucleon_has_interacted),
235  N_tot_(N_tot),
236  N_proj_(N_proj),
237  string_formation_time_(config.take(
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.");
245  }
247  const auto& log = logger<LogArea::FindScatter>();
248  log.info("Constant elastic isotropic cross-section mode:", " using ",
249  elastic_parameter_, " mb as maximal cross-section.");
250  }
251  if (strings_switch_) {
252  auto subconfig = config["Collision_Term"]["String_Parameters"];
253  string_process_interface_ = make_unique<StringProcess>(
254  subconfig.take({"String_Tension"}, 1.0), string_formation_time_,
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));
271  }
272 }
273 
275  const ParticleData& data_a, const ParticleData& data_b, double dt,
276  const std::vector<FourVector>& beam_momentum, const double cell_vol) const {
277  const auto& log = logger<LogArea::FindScatter>();
278 
279  /* If the two particles
280  * 1) belong to the two colliding nuclei
281  * 2) are within the same nucleus
282  * 3) both of them have never experienced any collisons,
283  * then the collision between them are banned. */
284  assert(data_a.id() >= 0);
285  assert(data_b.id() >= 0);
286  if (data_a.id() < N_tot_ && data_b.id() < N_tot_ &&
287  ((data_a.id() < N_proj_ && data_b.id() < N_proj_) ||
288  (data_a.id() >= N_proj_ && data_b.id() >= N_proj_)) &&
289  !(nucleon_has_interacted_[data_a.id()] ||
290  nucleon_has_interacted_[data_b.id()])) {
291  return nullptr;
292  }
293 
294  // Determine time of collision.
295  const double time_until_collision =
296  collision_time(data_a, data_b, dt, beam_momentum);
297 
298  // Check that collision happens in this timestep.
299  if (time_until_collision < 0. || time_until_collision >= dt) {
300  return nullptr;
301  }
302 
303  // Create ScatterAction object.
304  ScatterActionPtr act = make_unique<ScatterAction>(
305  data_a, data_b, time_until_collision, isotropic_, string_formation_time_);
306 
307  if (strings_switch_) {
308  act->set_string_interface(string_process_interface_.get());
309  }
310 
312  // No grid or search in cell
313  if (cell_vol < really_small) {
314  return nullptr;
315  }
316 
317  // Add various subprocesses.
318  act->add_all_scatterings(elastic_parameter_, two_to_one_, incl_set_,
321 
322  const double xs = act->cross_section() * fm2_mb;
323 
324  // Relative velocity calculation, see e.g. \iref{Seifert:2017oyb}, eq. (5)
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);
335 
336  // Collision probability, see e.g. \iref{Xu:2004mz}, eq. (11)
337  const double p_22 = xs * v_rel * dt / (cell_vol * testparticles_);
338 
339  log.debug("Stochastic collison criterion parameters:\np_22 = ", p_22,
340  ", xs = ", xs, ", v_rel = ", v_rel, ", dt = ", dt,
341  ", cell_vol = ", cell_vol, ", testparticles = ", testparticles_);
342 
343  if (p_22 > 1.) {
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());
348  }
349 
350  // probability criterion
351  double random_no = random::uniform(0., 1.);
352  if (random_no > p_22) {
353  return nullptr;
354  }
355 
357  // just collided with this particle
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,
361  "\n<-> ", data_b);
362  return nullptr;
363  }
364 
365  const double distance_squared = act->transverse_distance_sqr();
366 
367  // Don't calculate cross section if the particles are very far apart.
368  if (distance_squared >= max_transverse_distance_sqr(testparticles_)) {
369  return nullptr;
370  }
371 
372  // Add various subprocesses.
373  act->add_all_scatterings(elastic_parameter_, two_to_one_, incl_set_,
376 
377  // Cross section for collision criterion
378  double cross_section_criterion = act->cross_section() * fm2_mb * M_1_PI /
379  static_cast<double>(testparticles_);
380 
381  // Take cross section scaling factors into account
382  cross_section_criterion *= data_a.xsec_scaling_factor(time_until_collision);
383  cross_section_criterion *= data_b.xsec_scaling_factor(time_until_collision);
384 
385  // distance criterion according to cross_section
386  if (distance_squared >= cross_section_criterion) {
387  return nullptr;
388  }
389 
390  log.debug("particle distance squared: ", distance_squared, "\n ", data_a,
391  "\n<-> ", data_b);
392  }
393 
394  // Using std::move here is redundant with newer compilers, but required for
395  // supporting GCC 4.8. Once we drop this support, std::move should be removed.
396  return std::move(act);
397 }
398 
400  const ParticleList& search_list, double dt, const double cell_vol,
401  const std::vector<FourVector>& beam_momentum) const {
402  std::vector<ActionPtr> actions;
403  for (const ParticleData& p1 : search_list) {
404  for (const ParticleData& p2 : search_list) {
405  if (p1.id() < p2.id()) {
406  // Check if a collision is possible.
407  ActionPtr act = check_collision(p1, p2, dt, beam_momentum, cell_vol);
408  if (act) {
409  actions.push_back(std::move(act));
410  }
411  }
412  }
413  }
414  return actions;
415 }
416 
418  const ParticleList& search_list, const ParticleList& neighbors_list,
419  double dt, const std::vector<FourVector>& beam_momentum) const {
420  std::vector<ActionPtr> actions;
422  // Only search in cells
423  return actions;
424  }
425  for (const ParticleData& p1 : search_list) {
426  for (const ParticleData& p2 : neighbors_list) {
427  assert(p1.id() != p2.id());
428  // Check if a collision is possible.
429  ActionPtr act = check_collision(p1, p2, dt, beam_momentum);
430  if (act) {
431  actions.push_back(std::move(act));
432  }
433  }
434  }
435  return actions;
436 }
437 
439  const ParticleList& search_list, const Particles& surrounding_list,
440  double dt, const std::vector<FourVector>& beam_momentum) const {
441  std::vector<ActionPtr> actions;
443  // Only search in cells
444  return actions;
445  }
446  for (const ParticleData& p2 : surrounding_list) {
447  /* don't look for collisions if the particle from the surrounding list is
448  * also in the search list */
449  auto result = std::find_if(
450  search_list.begin(), search_list.end(),
451  [&p2](const ParticleData& p) { return p.id() == p2.id(); });
452  if (result != search_list.end()) {
453  continue;
454  }
455  for (const ParticleData& p1 : search_list) {
456  // Check if a collision is possible.
457  ActionPtr act = check_collision(p1, p2, dt, beam_momentum);
458  if (act) {
459  actions.push_back(std::move(act));
460  }
461  }
462  }
463  return actions;
464 }
465 
467  constexpr double time = 0.0;
468 
469  const size_t N_isotypes = IsoParticleType::list_all().size();
470  const size_t N_pairs = N_isotypes * (N_isotypes - 1) / 2;
471 
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};
476  for (const IsoParticleType& A_isotype : IsoParticleType::list_all()) {
477  for (const IsoParticleType& B_isotype : IsoParticleType::list_all()) {
478  if (&A_isotype > &B_isotype) {
479  continue;
480  }
481  bool any_nonzero_cs = false;
482  std::vector<std::string> r_list;
483  for (const ParticleTypePtr A_type : A_isotype.get_states()) {
484  for (const ParticleTypePtr B_type : B_isotype.get_states()) {
485  if (A_type > B_type) {
486  continue;
487  }
488  ParticleData A(*A_type), B(*B_type);
489  for (auto mom : momentum_scan_list) {
490  A.set_4momentum(A.pole_mass(), mom, 0.0, 0.0);
491  B.set_4momentum(B.pole_mass(), -mom, 0.0, 0.0);
492  ScatterActionPtr act = make_unique<ScatterAction>(
493  A, B, time, isotropic_, string_formation_time_);
494  if (strings_switch_) {
495  act->set_string_interface(string_process_interface_.get());
496  }
497  act->add_all_scatterings(elastic_parameter_, two_to_one_, incl_set_,
501  const double total_cs = act->cross_section();
502  if (total_cs <= 0.0) {
503  continue;
504  }
505  any_nonzero_cs = true;
506  for (const auto& channel : act->collision_channels()) {
507  const auto type = channel->get_type();
508  std::string r;
509  if (is_string_soft_process(type) ||
510  type == ProcessType::StringHard) {
511  r = A_type->name() + B_type->name() + std::string(" → strings");
512  } else {
513  std::string r_type =
514  (type == ProcessType::Elastic)
515  ? std::string(" (el)")
516  : (channel->get_type() == ProcessType::TwoToTwo)
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;
522  }
523  isoclean(r);
524  r_list.push_back(r);
525  }
526  }
527  }
528  }
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) {
533  std::cout << r;
534  if (r_list.back() != r) {
535  std::cout << ", ";
536  }
537  }
538  std::cout << std::endl;
539  }
540  }
541  }
542 }
543 
547  std::string name_;
548 
551 
553  double mass_;
554 
563  FinalStateCrossSection(const std::string& name, double cross_section,
564  double mass)
565  : name_(name), cross_section_(cross_section), mass_(mass) {}
566 };
567 
568 namespace decaytree {
569 
581 struct Node {
582  public:
584  std::string name_;
585 
587  double weight_;
588 
590  ParticleTypePtrList initial_particles_;
591 
593  ParticleTypePtrList final_particles_;
594 
596  ParticleTypePtrList state_;
597 
599  std::vector<Node> children_;
600 
602  Node(const Node&) = delete;
604  Node(Node&&) = default;
605 
616  Node(const std::string& name, double weight,
617  ParticleTypePtrList&& initial_particles,
618  ParticleTypePtrList&& final_particles, ParticleTypePtrList&& state,
619  std::vector<Node>&& children)
620  : name_(name),
621  weight_(weight),
622  initial_particles_(std::move(initial_particles)),
623  final_particles_(std::move(final_particles)),
624  state_(std::move(state)),
625  children_(std::move(children)) {}
626 
638  Node& add_action(const std::string& name, double weight,
639  ParticleTypePtrList&& initial_particles,
640  ParticleTypePtrList&& final_particles) {
641  // Copy parent state and update it.
642  ParticleTypePtrList state(state_);
643  for (const auto& p : initial_particles) {
644  state.erase(std::find(state.begin(), state.end(), p));
645  }
646  for (const auto& p : final_particles) {
647  state.push_back(p);
648  }
649  // Sort the state to normalize the output.
650  std::sort(state.begin(), state.end(),
652  return a->name() < b->name();
653  });
654  // Push new node to children.
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();
659  }
660 
662  void print() const { print_helper(0); }
663 
667  std::vector<FinalStateCrossSection> final_state_cross_sections() const {
668  std::vector<FinalStateCrossSection> result;
669  final_state_cross_sections_helper(0, result, "", 1.);
670  return result;
671  }
672 
673  private:
680  void print_helper(uint64_t depth) const {
681  for (uint64_t i = 0; i < depth; i++) {
682  std::cout << " ";
683  }
684  std::cout << name_ << " " << weight_ << std::endl;
685  for (const auto& child : children_) {
686  child.print_helper(depth + 1);
687  }
688  }
689 
702  uint64_t depth, std::vector<FinalStateCrossSection>& result,
703  const std::string& name, double weight,
704  bool show_intermediate_states = false) const {
705  // The first node corresponds to the total cross section and has to be
706  // ignored. The second node corresponds to the partial cross section. All
707  // further nodes correspond to branching ratios.
708  if (depth > 0) {
709  weight *= weight_;
710  }
711 
712  std::string new_name;
713  double mass = 0.;
714 
715  if (show_intermediate_states) {
716  new_name = name;
717  if (!new_name.empty()) {
718  new_name += "->";
719  }
720  new_name += name_;
721  new_name += "{";
722  } else {
723  new_name = "";
724  }
725  for (const auto& s : state_) {
726  new_name += s->name();
727  mass += s->mass();
728  }
729  if (show_intermediate_states) {
730  new_name += "}";
731  }
732 
733  if (children_.empty()) {
734  result.emplace_back(FinalStateCrossSection(new_name, weight, mass));
735  return;
736  }
737  for (const auto& child : children_) {
738  child.final_state_cross_sections_helper(depth + 1, result, new_name,
739  weight, show_intermediate_states);
740  }
741  }
742 };
743 
752 static std::string make_decay_name(const std::string& res_name,
753  const DecayBranchPtr& decay,
754  ParticleTypePtrList& final_state) {
755  std::stringstream name;
756  name << "[" << res_name << "->";
757  for (const auto& p : decay->particle_types()) {
758  name << p->name();
759  final_state.push_back(p);
760  }
761  name << "]";
762  return name.str();
763 }
764 
771 static void add_decays(Node& node, double sqrts) {
772  // If there is more than one unstable particle in the current state, then
773  // there will be redundant paths in the decay tree, corresponding to
774  // reorderings of the decays. To avoid double counting, we normalize by the
775  // number of possible decay orderings. Normalizing by the number of unstable
776  // particles recursively corresponds to normalizing by the factorial that
777  // gives the number of reorderings.
778  //
779  // Ideally, the redundant paths should never be added to the decay tree, but
780  // we never have more than two redundant paths, so it probably does not matter
781  // much.
782  uint32_t n_unstable = 0;
783  double sqrts_minus_masses = sqrts;
784  for (const ParticleTypePtr ptype : node.state_) {
785  if (!ptype->is_stable()) {
786  n_unstable += 1;
787  }
788  sqrts_minus_masses -= ptype->mass();
789  }
790  const double norm =
791  n_unstable != 0 ? 1. / static_cast<double>(n_unstable) : 1.;
792 
793  for (const ParticleTypePtr ptype : node.state_) {
794  if (!ptype->is_stable()) {
795  const double sqrts_decay = sqrts_minus_masses + ptype->mass();
796  bool can_decay = false;
797  for (const auto& decay : ptype->decay_modes().decay_mode_list()) {
798  // Make sure to skip kinematically impossible decays.
799  // In principle, we would have to integrate over the mass of the
800  // resonance, but as an approximation we just assume it at its pole.
801  double final_state_mass = 0.;
802  for (const auto& p : decay->particle_types()) {
803  final_state_mass += p->mass();
804  }
805  if (final_state_mass > sqrts_decay) {
806  continue;
807  }
808  can_decay = true;
809 
810  ParticleTypePtrList parts;
811  const auto name = make_decay_name(ptype->name(), decay, parts);
812  auto& new_node = node.add_action(name, norm * decay->weight(), {ptype},
813  std::move(parts));
814  add_decays(new_node, sqrts_decay);
815  }
816  if (!can_decay) {
817  // Remove final-state cross sections with resonances that cannot
818  // decay due to our "mass = pole mass" approximation.
819  node.weight_ = 0;
820  return;
821  }
822  }
823  }
824 }
825 
826 } // namespace decaytree
827 
833 static void deduplicate(std::vector<FinalStateCrossSection>& final_state_xs) {
834  std::sort(final_state_xs.begin(), final_state_xs.end(),
835  [](const FinalStateCrossSection& a,
836  const FinalStateCrossSection& b) { return a.name_ < b.name_; });
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(),
841  [](const FinalStateCrossSection& a, const FinalStateCrossSection& b) {
842  return a.name_ == b.name_;
843  });
844  current = adjacent;
845  if (adjacent != final_state_xs.end()) {
846  adjacent->cross_section_ += (adjacent + 1)->cross_section_;
847  final_state_xs.erase(adjacent + 1);
848  }
849  }
850 }
851 
853  const ParticleType& a, const ParticleType& b, double m_a, double m_b,
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;
858 
859  ParticleData a_data(a), b_data(b);
860  int n_momentum_points = 200;
861  constexpr double momentum_step = 0.02;
862  /*
863  // Round to output precision.
864  for (auto& p : plab) {
865  p = std::floor((p * 100000) + 0.5) / 100000;
866  }
867  */
868  if (plab.size() > 0) {
869  n_momentum_points = plab.size();
870  // Remove duplicates.
871  std::sort(plab.begin(), plab.end());
872  plab.erase(std::unique(plab.begin(), plab.end()), plab.end());
873  }
874  for (int i = 0; i < n_momentum_points; i++) {
875  double momentum;
876  if (plab.size() > 0) {
877  momentum = pCM_from_s(s_from_plab(plab.at(i), m_a, m_b), m_a, m_b);
878  } else {
879  momentum = momentum_step * (i + 1);
880  }
881  a_data.set_4momentum(m_a, momentum, 0.0, 0.0);
882  b_data.set_4momentum(m_b, -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>(
886  a_data, b_data, 0., isotropic_, string_formation_time_);
887  if (strings_switch_) {
888  act->set_string_interface(string_process_interface_.get());
889  }
890  act->add_all_scatterings(elastic_parameter_, two_to_one_, incl_set_,
893  decaytree::Node tree(a.name() + b.name(), act->cross_section(), {&a, &b},
894  {&a, &b}, {&a, &b}, {});
895  const CollisionBranchList& processes = act->collision_channels();
896  for (const auto& process : processes) {
897  const double xs = process->weight();
898  if (xs <= 0.0) {
899  continue;
900  }
901  if (!final_state) {
902  std::stringstream process_description_stream;
903  process_description_stream << *process;
904  const std::string& description = process_description_stream.str();
905  double m_tot = 0.0;
906  for (const auto& ptype : process->particle_types()) {
907  m_tot += ptype->mass();
908  }
909  outgoing_total_mass[description] = m_tot;
910  if (!xs_dump[description].empty() &&
911  std::abs(xs_dump[description].back().first - sqrts) <
912  really_small) {
913  xs_dump[description].back().second += xs;
914  } else {
915  xs_dump[description].push_back(std::make_pair(sqrts, xs));
916  }
917  } else {
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();
923  auto& process_node =
924  tree.add_action(description, xs, std::move(initial_particles),
925  std::move(final_particles));
926  decaytree::add_decays(process_node, sqrts);
927  }
928  }
929  xs_dump["total"].push_back(std::make_pair(sqrts, act->cross_section()));
930  // Total cross-section should be the first in the list -> negative mass
931  outgoing_total_mass["total"] = -1.0;
932  if (final_state) {
933  // tree.print();
934  auto final_state_xs = tree.final_state_cross_sections();
935  deduplicate(final_state_xs);
936  for (const auto& p : final_state_xs) {
937  // Don't print empty columns.
938  //
939  // FIXME(steinberg): The better fix would be to not have them in the
940  // first place.
941  if (p.name_ == "") {
942  continue;
943  }
944  outgoing_total_mass[p.name_] = p.mass_;
945  xs_dump[p.name_].push_back(std::make_pair(sqrts, p.cross_section_));
946  }
947  }
948  }
949  // Get rid of cross sections that are zero.
950  // (This only happens if their is a resonance in the final state that cannot
951  // decay with our simplified assumptions.)
952  for (auto it = begin(xs_dump); it != end(xs_dump);) {
953  // Sum cross section over all energies.
954  const xs_saver& xs = (*it).second;
955  double sum = 0;
956  for (const auto& p : xs) {
957  sum += p.second;
958  }
959  if (sum == 0.) {
960  it = xs_dump.erase(it);
961  } else {
962  ++it;
963  }
964  }
965 
966  // Nice ordering of channels by summed pole mass of products
967  std::vector<std::string> all_channels;
968  for (const auto channel : xs_dump) {
969  all_channels.push_back(channel.first);
970  }
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];
974  });
975 
976  // Print header
977  std::cout << "# Dumping partial " << a.name() << b.name()
978  << " cross-sections in mb, energies in GeV" << std::endl;
979  std::cout << " sqrt_s";
980  // Align everything to 16 unicode characters.
981  // This should be enough for the longest channel name (7 final-state
982  // particles).
983  for (const auto channel : all_channels) {
984  std::cout << utf8::fill_left(channel, 16, ' ');
985  }
986  std::cout << std::endl;
987 
988  // Print out all partial cross-sections in mb
989  for (int i = 0; i < n_momentum_points; i++) {
990  double momentum;
991  if (plab.size() > 0) {
992  momentum = pCM_from_s(s_from_plab(plab.at(i), m_a, m_b), m_a, m_b);
993  } else {
994  momentum = momentum_step * (i + 1);
995  }
996  a_data.set_4momentum(m_a, momentum, 0.0, 0.0);
997  b_data.set_4momentum(m_b, -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];
1002  size_t j = 0;
1003  for (; j < energy_and_xs.size() && energy_and_xs[j].first < sqrts; j++) {
1004  }
1005  double xs = 0.0;
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;
1009  }
1010  std::printf("%16.6f", xs); // Same alignment as in the header.
1011  }
1012  std::printf("\n");
1013  }
1014 }
1015 
1016 } // namespace smash
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.
Definition: constants.h:37
(Default) geometric criterion.
Stochastic Criteiron.
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.
bool is_stable() const
Definition: particletype.h:236
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.
STL namespace.
T pCM_from_s(const T s, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:66
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.
Definition: constants.h:28
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
Definition: decaymodes.h:63
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 mass() const
Definition: particletype.h:144
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...
Definition: kinematics.h:224
double weight_
Weight (cross section or branching ratio).
const std::string & name() const
Definition: particletype.h:141
Particle type contains the static properties of a particle species.
Definition: particletype.h:97
ScatterActionsFinder(Configuration config, const ExperimentParameters &parameters, 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&#39;s 4-momentum directly.
Definition: particledata.h:145
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.
T uniform(T min, T max)
Definition: random.h:88
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&#39;s pole mass ("on-shell").
Definition: particledata.h:96
constexpr int p
Proton.
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.
Definition: particletype.h:654
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.
Definition: particles.h:33
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.
Definition: particledata.h:70
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.
Definition: particledata.h:52
const double elastic_parameter_
Elastic cross section parameter (in mb).
Definition: action.h:24
const FourVector & momentum() const
Get the particle&#39;s 4-momentum.
Definition: particledata.h:139
static const IsoParticleTypeList & list_all()
Returns a list of all IsoParticleTypes.
const CollisionCriterion coll_crit_
Specifies which collision criterion is used.