Version: SMASH-3.4
action.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/action.h"
11 
12 #include <assert.h>
13 
14 #include <algorithm>
15 #include <sstream>
16 
17 #include "smash/angles.h"
18 #include "smash/constants.h"
19 #include "smash/interpolation.h"
20 #include "smash/logging.h"
21 #include "smash/pauliblocking.h"
23 #include "smash/processbranch.h"
24 #include "smash/quantumnumbers.h"
25 
26 namespace smash {
27 
28 /// Destructor
29 Action::~Action() = default;
30 static constexpr int LPauliBlocking = LogArea::PauliBlocking::id;
31 
32 bool Action::is_valid(const Particles &particles) const {
33  return std::all_of(
35  [&particles](const ParticleData &p) { return particles.is_valid(p); });
36 }
37 
38 bool Action::is_pauli_blocked(const std::vector<Particles> &ensembles,
39  const PauliBlocker &p_bl) const {
40  // Wall-crossing actions should never be blocked: currently
41  // if the action is blocked, a particle continues to propagate in a straight
42  // line. This would simply bring it out of the box.
44  return false;
45  }
46  for (const auto &p : outgoing_particles_) {
47  if (p.is_baryon()) {
48  const auto f =
49  p_bl.phasespace_dens(p.position().threevec(), p.momentum().threevec(),
50  ensembles, p.pdgcode(), incoming_particles_);
51  if (f > random::uniform(0., 1.)) {
52  logg[LPauliBlocking].debug("Action ", *this,
53  " is pauli-blocked with f = ", f);
54  return true;
55  }
56  }
57  }
58  return false;
59 }
60 
61 const ParticleList &Action::incoming_particles() const {
62  return incoming_particles_;
63 }
64 
65 void Action::update_incoming(const Particles &particles) {
66  for (auto &p : incoming_particles_) {
67  p = particles.lookup(p);
68  }
69 }
70 
72  // Estimate for the interaction point in the calculational frame
73  ThreeVector interaction_point = ThreeVector(0., 0., 0.);
74  std::vector<ThreeVector> propagated_positions;
75  for (const auto &part : incoming_particles_) {
76  ThreeVector propagated_position =
77  part.position().threevec() +
78  part.velocity() * (time_of_execution_ - part.position().x0());
79  propagated_positions.push_back(propagated_position);
80  interaction_point += propagated_position;
81  }
82  interaction_point /= incoming_particles_.size();
83  /*
84  * In case of periodic boundaries interaction point is not necessarily
85  * (x1 + x2)/2. Consider only one dimension, e.g. x, the rest are analogous.
86  * Instead of x, there can be x + k * L, where k is any integer and L
87  * is period.Interaction point is either. Therefore, interaction point is
88  * (x1 + k * L + x2 + m * L) / 2 = (x1 + x2) / 2 + n * L / 2. We need
89  * this interaction point to be with [0, L], so n can be {-1, 0, 1}.
90  * Which n to choose? Our guiding principle is that n should be such that
91  * interaction point is closest to interacting particles.
92  */
93  if (box_length_ > 0 && stochastic_position_idx_ < 0) {
94  assert(incoming_particles_.size() == 2);
95  const ThreeVector r = propagated_positions[0] - propagated_positions[1];
96  for (int i = 0; i < 3; i++) {
97  const double d = std::abs(r[i]);
98  if (d > 0.5 * box_length_) {
99  if (interaction_point[i] >= 0.5 * box_length_) {
100  interaction_point[i] -= 0.5 * box_length_;
101  } else {
102  interaction_point[i] += 0.5 * box_length_;
103  }
104  }
105  }
106  }
107  /* In case of scatterings via the stochastic criterion, use postion of random
108  * incoming particle to prevent density hotspots in grid cell centers. */
109  if (stochastic_position_idx_ >= 0) {
111  }
112  return FourVector(time_of_execution_, interaction_point);
113 }
114 
115 std::pair<FourVector, FourVector> Action::get_potential_at_interaction_point()
116  const {
118  FourVector UB = FourVector();
119  FourVector UI3 = FourVector();
120  /* Check:
121  * Lattice is turned on. */
122  if (UB_lat_pointer != nullptr) {
123  UB_lat_pointer->value_at(r, UB);
124  }
125  if (UI3_lat_pointer != nullptr) {
126  UI3_lat_pointer->value_at(r, UI3);
127  }
128  return std::make_pair(UB, UI3);
129 }
130 
131 double Action::perform(Particles *particles, uint32_t id_process) {
132  assert(id_process != 0);
133  double energy_violation = 0.;
135  /* Store the history info. Wall crossing and fluidization don't change the
136  * last collision a particle went through. */
137  if ((process_type_ != ProcessType::Wall) &&
139  p.set_history(p.get_history().collisions_per_particle + 1, id_process,
141  }
142  }
143 
144  /* For elastic collisions and box wall crossings it is not necessary to remove
145  * particles from the list and insert new ones, it is enough to update their
146  * properties. */
147  const bool replace = (process_type_ != ProcessType::Elastic) &&
150  particles->update(incoming_particles_, outgoing_particles_, replace);
151 
152  logg[LAction].debug("Particle map now has ", particles->size(), " elements.");
153 
154  /* Check the conservation laws if the modifications of the total kinetic
155  * energy of the outgoing particles by the mean field potentials are not
156  * taken into account. */
157  if (UB_lat_pointer == nullptr && UI3_lat_pointer == nullptr) {
158  energy_violation = check_conservation(id_process);
159  }
160  return energy_violation;
161 }
162 
165  /* scale_B returns the difference of the total force scales of the skyrme
166  * potential between the initial and final states. */
167  double scale_B = 0.0;
168  /* scale_I3 returns the difference of the total force scales of the symmetry
169  * potential between the initial and final states. */
170  double scale_I3 = 0.0;
171  for (const auto &p_in : incoming_particles_) {
172  // Get the force scale of the incoming particle.
173  const auto scale =
174  ((pot_pointer != nullptr) ? pot_pointer->force_scale(p_in.type())
175  : std::make_pair(0.0, 0));
176  scale_B += scale.first;
177  scale_I3 += scale.second * p_in.type().isospin3_rel();
178  }
179  for (const auto &p_out : outgoing_particles_) {
180  // Get the force scale of the outgoing particle.
181  const auto scale = ((pot_pointer != nullptr)
183  : std::make_pair(0.0, 0));
184  scale_B -= scale.first;
185  scale_I3 -= scale.second * type_of_pout(p_out).isospin3_rel();
186  }
187  /* Rescale to get the potential difference between the
188  * initial and final state, and thus get the total momentum
189  * of the outgoing particles*/
190  return total_momentum() + potentials.first * scale_B +
191  potentials.second * scale_I3;
192 }
193 
195  /* Find incoming particle with largest formation time i.e. the last formed
196  * incoming particle. If all particles form at the same time, take the one
197  * with the lowest cross section scaling factor */
198  ParticleList::iterator last_formed_in_part;
199  bool all_incoming_same_formation_time =
201  [&](const ParticleData &data_comp) {
202  return std::abs(incoming_particles_[0].formation_time() -
203  data_comp.formation_time()) < really_small;
204  });
205  if (all_incoming_same_formation_time) {
206  last_formed_in_part =
207  std::min_element(incoming_particles_.begin(), incoming_particles_.end(),
208  [](const ParticleData &a, const ParticleData &b) {
209  return a.initial_xsec_scaling_factor() <
210  b.initial_xsec_scaling_factor();
211  });
212  } else {
213  last_formed_in_part =
214  std::max_element(incoming_particles_.begin(), incoming_particles_.end(),
215  [](const ParticleData &a, const ParticleData &b) {
216  return a.formation_time() < b.formation_time();
217  });
218  }
219 
220  const double form_time_begin = last_formed_in_part->begin_formation_time();
221  const double sc = last_formed_in_part->initial_xsec_scaling_factor();
222 
223  if (last_formed_in_part->formation_time() > time_of_execution_) {
224  for (ParticleData &new_particle : outgoing_particles_) {
225  if (new_particle.initial_xsec_scaling_factor() < 1.0) {
226  /* The new cross section scaling factor will be the product of the
227  * cross section scaling factor of the ingoing particles and of the
228  * outgoing ones (since the outgoing ones are also string fragments
229  * and thus take time to form). */
230  double sc_out = new_particle.initial_xsec_scaling_factor();
231  new_particle.set_cross_section_scaling_factor(sc * sc_out);
232  if (last_formed_in_part->formation_time() >
233  new_particle.formation_time()) {
234  /* If the unformed incoming particles' formation time is larger than
235  * the current outgoing particle's formation time, then the latter
236  * is overwritten by the former*/
237  new_particle.set_slow_formation_times(
238  time_of_execution_, last_formed_in_part->formation_time());
239  }
240  } else {
241  // not a string product
242  new_particle.set_slow_formation_times(
243  form_time_begin, last_formed_in_part->formation_time());
244  new_particle.set_cross_section_scaling_factor(sc);
245  }
246  }
247  } else {
248  for (ParticleData &new_particle : outgoing_particles_) {
249  if (new_particle.initial_xsec_scaling_factor() == 1.0) {
250  new_particle.set_formation_time(time_of_execution_);
251  }
252  }
253  }
254 }
255 
256 std::pair<double, double> Action::sample_masses(
257  const double kinetic_energy_cm) const {
258  const ParticleType &t_a = outgoing_particles_[0].type();
259  const ParticleType &t_b = outgoing_particles_[1].type();
260  // start with pole masses
261  std::pair<double, double> masses = {t_a.mass(), t_b.mass()};
262 
263  if (kinetic_energy_cm < t_a.min_mass_kinematic() + t_b.min_mass_kinematic()) {
264  const std::string reaction = incoming_particles_[0].type().name() +
265  incoming_particles_[1].type().name() + "→" +
266  t_a.name() + t_b.name();
268  reaction + ": not enough energy, " + std::to_string(kinetic_energy_cm) +
269  " < " + std::to_string(t_a.min_mass_kinematic()) + " + " +
271  }
272 
273  /* If one of the particles is a resonance, sample its mass. */
274  if (!t_a.is_stable() && t_b.is_stable()) {
275  masses.first = t_a.sample_resonance_mass(t_b.mass(), kinetic_energy_cm);
276  } else if (!t_b.is_stable() && t_a.is_stable()) {
277  masses.second = t_b.sample_resonance_mass(t_a.mass(), kinetic_energy_cm);
278  } else if (!t_a.is_stable() && !t_b.is_stable()) {
279  // two resonances in final state
280  masses = sample_two_resonance_masses(t_a, t_b, kinetic_energy_cm);
281  }
282  return masses;
283 }
284 
285 void Action::sample_angles(std::pair<double, double> masses,
286  const double kinetic_energy_cm) {
289 
290  const double pcm = pCM(kinetic_energy_cm, masses.first, masses.second);
291  if (!(pcm > 0.0)) {
292  logg[LAction].warn("Particle: ", p_a->pdgcode(), " radial momentum: ", pcm);
293  logg[LAction].warn("Ektot: ", kinetic_energy_cm, " m_a: ", masses.first,
294  " m_b: ", masses.second);
295  }
296  /* Here we assume an isotropic angular distribution. */
297  Angles phitheta;
298  phitheta.distribute_isotropically();
299 
300  p_a->set_4momentum(masses.first, phitheta.threevec() * pcm);
301  p_b->set_4momentum(masses.second, -phitheta.threevec() * pcm);
302  /* Debug message is printed before boost, so that p_a and p_b are
303  * the momenta in the center of mass frame and thus opposite to
304  * each other.*/
305  logg[LAction].debug("p_a: ", *p_a, "\np_b: ", *p_b);
306 }
307 
309  /* This function only operates on 2-particle final states. */
310  assert(outgoing_particles_.size() == 2);
312  const double cm_kin_energy = p_tot.abs();
313  // first sample the masses
314  const std::pair<double, double> masses = sample_masses(cm_kin_energy);
315  // after the masses are fixed (and thus also pcm), sample the angles
316  sample_angles(masses, cm_kin_energy);
317 }
318 
320  const size_t n = outgoing_particles_.size();
321  if (n < 3) {
322  throw std::invalid_argument(
323  "sample_manybody_phasespace: number of outgoing particles should be 3 "
324  "or more");
325  }
326 
327  ParticleTypePtrList types(n);
328  for (size_t i = 0; i < n; i++) {
329  types[i] = &outgoing_particles_[i].type();
330  }
331  std::vector<FourVector> p(n);
332 
334  for (size_t i = 0; i < n; i++) {
335  outgoing_particles_[i].set_4momentum(p[i]);
336  }
337 }
338 
341  p.set_unpolarized_spin_vector();
342  }
343 }
344 
345 double Action::check_conservation(const uint32_t id_process) const {
348  double energy_violation = 0.;
349  if (before != after) {
350  std::stringstream particle_names;
351  for (const auto &p : incoming_particles_) {
352  particle_names << p.type().name();
353  }
354  particle_names << " vs. ";
355  for (const auto &p : outgoing_particles_) {
356  particle_names << p.type().name();
357  }
358  particle_names << "\n";
359  std::string err_msg = before.report_deviations(after);
360  /* Pythia does not conserve energy and momentum at high energy, so we just
361  * print the warning and continue. */
363  logg[LAction].warn() << "Conservation law violations due to Pythia\n"
364  << particle_names.str() << err_msg;
365  energy_violation = after.momentum()[0] - before.momentum()[0];
366  return energy_violation;
367  }
368  /* We allow decay of particles stable under the strong interaction to decay
369  * at the end, so just warn about such a "weak" process violating
370  * conservation laws */
372  incoming_particles_[0].type().is_stable()) {
373  logg[LAction].warn()
374  << "Conservation law violations of strong interaction in weak or "
375  "e.m. decay\n"
376  << particle_names.str() << err_msg;
377  return energy_violation;
378  }
379  /* If particles are added or removed, it is not surprising that conservation
380  * laws are potentially violated. Do not warn the user but print some
381  * information for debug */
383  logg[LAction].debug()
384  << "Conservation law violation, but we want it (Freeforall Action).\n"
385  << particle_names.str() << err_msg;
386  return energy_violation;
387  }
388  logg[LAction].error() << "Conservation law violations detected\n"
389  << particle_names.str() << err_msg;
390  if (id_process == ID_PROCESS_PHOTON) {
391  throw std::runtime_error("Conservation laws violated in photon process");
392  } else {
393  throw std::runtime_error("Conservation laws violated in process " +
394  std::to_string(id_process));
395  }
396  }
397  return energy_violation;
398 }
399 
400 std::ostream &operator<<(std::ostream &out, const ActionList &actions) {
401  out << "ActionList {\n";
402  for (const auto &a : actions) {
403  out << "- " << a << '\n';
404  }
405  return out << '}';
406 }
407 
408 namespace detail {
409 
411  const ParticleTypePtrList &types,
412  std::vector<FourVector> &sampled_momenta) {
413  const size_t n = types.size();
414  assert(n > 1);
415  sampled_momenta.resize(n);
416  std::vector<double> masses{}, masses_sum(n), Minv(n);
417  // Maximum estimate is rough and can be wrong. We multiply it by additional
418  // factor to be on the safer side, and increase it if needed.
419  double safety_factor = 1.1 + (n - 2) * 0.2;
420  int rejection_counter = 0;
421  constexpr int rejection_limit = 200;
422  double available_energy =
423  sqrts - std::accumulate(types.begin(), types.end(), 0.0,
424  [](double sum, const ParticleTypePtr &type) {
425  return sum + type->min_mass_spectral();
426  });
427  double acceptance = 1;
428  do {
429  double random_01;
430  // This loop increases the maximum if the safety_factor is too small
431  safety_factor *= std::sqrt(acceptance);
432  do {
433  // Mass sampling from spectral functions
434  double weight_sqr_max = safety_factor * safety_factor;
435  masses.clear();
436  masses_sum.clear();
437  for (const auto &type : types) {
438  if (type->is_stable()) {
439  masses.push_back(type->mass());
440  } else {
441  masses.push_back(
442  type->sample_breit_wigner_spectral_function(available_energy));
443  const double max_ratio = std::max(
445  type->ratio_spectral_full_to_breit_wigner(available_energy));
446  weight_sqr_max *= max_ratio * max_ratio;
447  }
448  }
449  // Arrange a convenient vector of m1, m1 + m2, m1 + m2 + m3, ...
450  std::partial_sum(masses.begin(), masses.end(),
451  std::back_inserter(masses_sum));
452  const double masses_sum_all = masses_sum[n - 1];
453  const double Ekin_share = (sqrts - masses_sum_all) / (n - 1);
454  for (size_t i = 1; i < n; i++) {
455  // This maximum estimate idea is due Scott Pratt: maximum should be
456  // roughly at equal kinetic energies
457  weight_sqr_max *=
458  pCM_sqr(i * Ekin_share + masses_sum[i],
459  (i - 1) * Ekin_share + masses_sum[i - 1], masses[i]);
460  }
461  // Generate invariant masses of 1, 12, 123, 1234, etc.
462  // Minv = {m1, M12, M123, ..., M123n-1, sqrts}
463  Minv[0] = 0.0;
464  Minv[n - 1] = sqrts - masses_sum_all;
465  for (size_t i = 1; i < n - 1; i++) {
466  Minv[i] = random::uniform(0.0, sqrts - masses_sum_all);
467  }
468  std::sort(Minv.begin(), Minv.end());
469  for (size_t i = 0; i < n; i++) {
470  Minv[i] += masses_sum[i];
471  }
472 
473  double weight_sqr = 1;
474  for (size_t i = 0; i < n; i++) {
475  const double ratio =
476  types[i]->is_stable()
477  ? 1
478  : types[i]->ratio_spectral_full_to_breit_wigner(masses[i]);
479  weight_sqr *= ratio * ratio;
480  }
481  for (size_t i = 1; i < n; i++) {
482  weight_sqr *= pCM_sqr(Minv[i], Minv[i - 1], masses[i]);
483  }
484  acceptance = weight_sqr / weight_sqr_max;
485  rejection_counter++;
486  random_01 = random::canonical();
487  } while (acceptance < random_01 * random_01 &&
488  rejection_counter < rejection_limit);
489  if (acceptance > 1) {
490  logg[LAction].debug()
491  << "sample_manybody_phasespace_impl: alarm, weight > 1, w^2 = "
492  << acceptance << ". Increasing safety factor.";
493  }
494  } while (acceptance > 1 && rejection_counter < rejection_limit);
495 
496  // Boost particles to the right frame
497  std::vector<ThreeVector> beta(n);
498  for (size_t i = n - 1; i > 0; i--) {
499  const double pcm = pCM(Minv[i], Minv[i - 1], masses[i]);
500  Angles phitheta;
501  phitheta.distribute_isotropically();
502  const ThreeVector isotropic_unitvector = phitheta.threevec();
503  sampled_momenta[i] =
504  FourVector(std::sqrt(masses[i] * masses[i] + pcm * pcm),
505  pcm * isotropic_unitvector);
506  if (i >= 2) {
507  beta[i - 2] = pcm * isotropic_unitvector /
508  std::sqrt(pcm * pcm + Minv[i - 1] * Minv[i - 1]);
509  }
510  if (i == 1) {
511  sampled_momenta[0] =
512  FourVector(std::sqrt(masses[0] * masses[0] + pcm * pcm),
513  -pcm * isotropic_unitvector);
514  }
515  }
516 
517  for (size_t i = 0; i < n - 2; i++) {
518  // After each boost except the last one the sum of 3-momenta should be 0
519  FourVector ptot = FourVector(0.0, 0.0, 0.0, 0.0);
520  for (size_t j = 0; j <= i + 1; j++) {
521  ptot += sampled_momenta[j];
522  }
523  logg[LAction].debug() << "Total momentum of 0.." << i + 1 << " = "
524  << ptot.threevec() << " and should be (0, 0, 0).";
525 
526  // Boost the first i+1 particles to the next CM frame
527  for (size_t j = 0; j <= i + 1; j++) {
528  sampled_momenta[j] = sampled_momenta[j].lorentz_boost(beta[i]);
529  }
530  }
531 
532  FourVector ptot_all = FourVector(0.0, 0.0, 0.0, 0.0);
533  for (size_t j = 0; j < n; j++) {
534  ptot_all += sampled_momenta[j];
535  }
536  logg[LAction].debug() << "Total 4-momentum = " << ptot_all << ", should be ("
537  << sqrts << ", 0, 0, 0)";
538  if (rejection_counter >= rejection_limit) {
539  logg[LAction].warn()
540  << "Failed to sample kinematically correct 4-momenta of "
541  << std::accumulate(types.begin(), types.end(), std::string{},
542  [](const std::string &a, const ParticleTypePtr &b) {
543  return a + b->name();
544  })
545  << " with energy " << sqrts
546  << " GeV.\n Using MCMC fallback, which conserves energy and momentum "
547  "but may deviate slightly from the correct spectral distribution.";
548  sample_manybody_phasespace_MCMC(types, sampled_momenta);
549  }
550 }
551 
552 void sample_manybody_phasespace_MCMC(const ParticleTypePtrList &types,
553  std::vector<FourVector> &sampled_momenta) {
554  constexpr int monte_carlo_iterations = 200;
555  // sampled_momenta already contains an initial guess
556  const int n = types.size();
557  int idx1, idx2;
558 
559  assert(sampled_momenta.size() == static_cast<size_t>(n));
560  // It is enough to check that the energy is a positive number
561  const bool all_zero =
562  std::all_of(sampled_momenta.begin(), sampled_momenta.end(),
563  [](const FourVector &p) { return p[0] < really_small; });
564  if (all_zero) {
565  throw std::runtime_error(
566  "All initial momenta for the MCMC algorithm manybody phase space "
567  "sampling are zero, which should not happen.");
568  }
569 
570  for (int i = 0; i < monte_carlo_iterations; i++) {
571  // Pick random pair of particles and resample their masses
572  do {
573  idx1 = random::uniform_int(0, n - 1);
574  do {
575  idx2 = random::uniform_int(0, n - 1);
576  } while (idx2 == idx1);
577  } while (types[idx1]->is_stable() && types[idx2]->is_stable());
578 
579  // Energy of the pair and CM frame velocity
580  const FourVector p_sum = sampled_momenta[idx1] + sampled_momenta[idx2];
581  const ThreeVector beta = p_sum.velocity();
582  const double sqrts_12 = p_sum.abs();
583  double m1, m2;
584  do {
585  m1 = types[idx1]->sample_full_spectral_function(sqrts_12);
586  m2 = types[idx2]->sample_full_spectral_function(sqrts_12);
587  } while (sqrts_12 < m1 + m2);
588  const double pcm = pCM(sqrts_12, m1, m2);
589  Angles phitheta;
590  phitheta.distribute_isotropically();
591  /*
592  * Metropolis-Hastings acceptance. As the Breit Wigner is biased towards
593  * lower masses compared to the full spectral function, we generally want to
594  * accept more often if the ratio increases, and less if it decreases.
595  */
596  double acc = 1.0;
597  if (!types[idx1]->is_stable()) {
598  acc *= types[idx1]->ratio_spectral_full_to_breit_wigner(m1) /
599  types[idx1]->ratio_spectral_full_to_breit_wigner(
600  sampled_momenta[idx1].abs());
601  }
602  if (!types[idx2]->is_stable()) {
603  acc *= types[idx2]->ratio_spectral_full_to_breit_wigner(m2) /
604  types[idx2]->ratio_spectral_full_to_breit_wigner(
605  sampled_momenta[idx2].abs());
606  }
607 
608  if (random::canonical() < acc) {
609  // Accept and impose momentum conservation with back-to-back particles
610  sampled_momenta[idx1] =
611  FourVector(std::sqrt(m1 * m1 + pcm * pcm), pcm * phitheta.threevec())
612  .lorentz_boost(-beta);
613  sampled_momenta[idx2] =
614  FourVector(std::sqrt(m2 * m2 + pcm * pcm), -pcm * phitheta.threevec())
615  .lorentz_boost(-beta);
616  }
617  }
618 }
619 
620 } // namespace detail
621 
622 } // namespace smash
Thrown for example when ScatterAction is called to perform with a wrong number of final-state particl...
Definition: action.h:330
virtual void sample_2body_phasespace()
Sample the full 2-body phase-space (masses, momenta, angles) in the center-of-mass frame for the fina...
Definition: action.cc:308
FourVector total_momentum_of_outgoing_particles() const
Calculate the total kinetic momentum of the outgoing particles.
Definition: action.cc:163
void assign_formation_time_to_outgoing_particles()
Assign the formation time to the outgoing particles.
Definition: action.cc:194
virtual ~Action()
Virtual Destructor.
int stochastic_position_idx_
This stores a randomly-chosen index to an incoming particle.
Definition: action.h:386
std::pair< FourVector, FourVector > get_potential_at_interaction_point() const
Get the skyrme and asymmetry potential at the interaction point.
Definition: action.cc:115
virtual void sample_angles(std::pair< double, double > masses, double kinetic_energy_cm)
Sample final-state momenta in general X->2 processes (here: using an isotropical angular distribution...
Definition: action.cc:285
ParticleList outgoing_particles_
Initially this stores only the PDG codes of final-state particles.
Definition: action.h:363
const ParticleType & type_of_pout(const ParticleData &p_out) const
Get the type of a given particle.
Definition: action.h:508
FourVector total_momentum() const
Sum of 4-momenta of incoming particles.
Definition: action.h:389
virtual void sample_manybody_phasespace()
Sample the full n-body phase-space (masses, momenta, angles) in the center-of-mass frame for the fina...
Definition: action.cc:319
const double time_of_execution_
Time at which the action is supposed to be performed (absolute time in the lab frame in fm).
Definition: action.h:369
virtual double perform(Particles *particles, uint32_t id_process)
Actually perform the action, e.g.
Definition: action.cc:131
void update_incoming(const Particles &particles)
Update the incoming particles that are stored in this action to the state they have in the global par...
Definition: action.cc:65
void assign_unpolarized_spin_vector_to_outgoing_particles()
Assign an unpolarized spin vector to all outgoing particles.
Definition: action.cc:339
virtual double check_conservation(const uint32_t id_process) const
Check various conservation laws.
Definition: action.cc:345
const ParticleList & incoming_particles() const
Get the list of particles that go into the action.
Definition: action.cc:61
double sqrt_s() const
Determine the total energy in the center-of-mass frame [GeV].
Definition: action.h:271
double box_length_
Box length: needed to determine coordinates of collision correctly in case of collision through the w...
Definition: action.h:379
ParticleList incoming_particles_
List with data of incoming particles.
Definition: action.h:355
FourVector get_interaction_point() const
Get the interaction point.
Definition: action.cc:71
virtual std::pair< double, double > sample_masses(double kinetic_energy_cm) const
Sample final-state masses in general X->2 processes (thus also fixing the absolute c....
Definition: action.cc:256
ProcessType process_type_
type of process
Definition: action.h:372
bool is_valid(const Particles &particles) const
Check whether the action still applies.
Definition: action.cc:32
bool is_pauli_blocked(const std::vector< Particles > &ensembles, const PauliBlocker &p_bl) const
Check if the action is Pauli-blocked.
Definition: action.cc:38
Angles provides a common interface for generating directions: i.e., two angles that should be interpr...
Definition: angles.h:59
ThreeVector threevec() const
Definition: angles.h:288
void distribute_isotropically()
Populate the object with a new direction.
Definition: angles.h:199
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
double abs() const
calculate the lorentz invariant absolute value
Definition: fourvector.h:464
FourVector lorentz_boost(const ThreeVector &v) const
Returns the FourVector boosted with velocity v.
Definition: fourvector.cc:17
ThreeVector threevec() const
Definition: fourvector.h:329
ThreeVector velocity() const
Get the velocity (3-vector divided by zero component).
Definition: fourvector.h:333
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:59
PdgCode pdgcode() const
Get the pdgcode of the particle.
Definition: particledata.h:88
void set_4momentum(const FourVector &momentum_vector)
Set the particle's 4-momentum directly.
Definition: particledata.h:177
A pointer-like interface to global references to ParticleType objects.
Definition: particletype.h:731
Particle type contains the static properties of a particle species.
Definition: particletype.h:100
double sample_resonance_mass(const double mass_stable, const double cms_energy, int L=0) const
Resonance mass sampling for 2-particle final state with one resonance (type given by 'this') and one ...
double min_mass_kinematic() const
The minimum mass of the resonance that is kinematically allowed.
const std::string & name() const
Definition: particletype.h:144
double max_ratio_spectral_full_to_breit_wigner() const
Getter used in the resonance mass sampling functions.
Definition: particletype.h:442
bool is_stable() const
Definition: particletype.h:251
double isospin3_rel() const
Definition: particletype.h:182
double mass() const
Definition: particletype.h:147
double ratio_spectral_full_to_breit_wigner(double m) const
Calculate the ratio between the full spectral function and simple one.
double sample_breit_wigner_spectral_function(double energy=mass_limit_) const
Sample mass from the simple spectral function (Breit-Wigner/Cauchy distribution).
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
size_t size() const
Definition: particles.h:87
void update(const ParticleList &old_state, ParticleList &new_state, bool do_replace)
Updates the Particles object, replacing the particles in old_state with the particles in new_state.
Definition: particles.h:200
const ParticleData & lookup(const ParticleData &old_state) const
Returns the particle that is currently stored in this object given an old copy of that particle.
Definition: particles.h:222
A class that stores parameters needed for Pauli blocking, tabulates necessary integrals and computes ...
Definition: pauliblocking.h:38
double phasespace_dens(const ThreeVector &r, const ThreeVector &p, const std::vector< Particles > &ensembles, const PdgCode pdg, const ParticleList &disregard) const
Calculate phase-space density of a particle species at the point (r,p).
static std::pair< double, int > force_scale(const ParticleType &data)
Evaluates the scaling factor of the forces acting on the particles.
Definition: potentials.cc:152
A container for storing conserved values.
FourVector momentum() const
std::string report_deviations(const std::vector< Particles > &ensembles) const
Checks if the current particle list has still the same values and reports about differences.
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:31
Collection of useful constants that are known at compile time.
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.
Definition: action.h:546
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > & logg
An array that stores all pre-configured Logger objects.
Definition: logging.h:245
constexpr Section potentials
Section for the potentials information.
Definition: input_keys.h:228
void sample_manybody_phasespace_impl(double sqrts, const ParticleTypePtrList &types, std::vector< FourVector > &sampled_momenta)
Implementation of the full n-body phase-space sampling (masses, momenta, angles) in the center-of-mas...
Definition: action.cc:410
void sample_manybody_phasespace_MCMC(const ParticleTypePtrList &types, std::vector< FourVector > &sampled_momenta)
Metropolis–Hastings sampling of the many body phase space, starting from an initial guess in sampled_...
Definition: action.cc:552
constexpr int p
Proton.
constexpr int n
Neutron.
T beta(T a, T b)
Draws a random number from a beta-distribution, where probability density of is .
Definition: random.h:373
T uniform_int(T min, T max)
Definition: random.h:106
T uniform(T min, T max)
Definition: random.h:91
T canonical()
Definition: random.h:122
Definition: action.h:24
static constexpr int LPauliBlocking
Definition: action.cc:30
T pCM(const T sqrts, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:79
T pCM_sqr(const T sqrts, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:91
constexpr std::uint32_t ID_PROCESS_PHOTON
Process ID for any photon process.
Definition: constants.h:129
static constexpr int LAction
Definition: action.h:25
@ FluidizationNoRemoval
See here for a short description.
@ Freeforall
See here for a short description.
@ Decay
See here for a short description.
@ Wall
See here for a short description.
@ Elastic
See here for a short description.
bool all_of(Container &&c, UnaryPredicate &&p)
Convenience wrapper for std::all_of that operates on a complete container.
Definition: algorithms.h:80
std::string to_string(ThermodynamicQuantity quantity)
Convert a ThermodynamicQuantity enum value to its corresponding string.
Definition: stringify.cc:26
Potentials * pot_pointer
Pointer to a Potential class.
RectangularLattice< FourVector > * UB_lat_pointer
Pointer to the skyrme potential on the lattice.
std::pair< double, double > sample_two_resonance_masses(const ParticleType &t1, const ParticleType &t2, const double cms_energy, int L=0)
Resonance mass sampling for 2-particle final state with two resonances.
bool is_string_process(ProcessType p)
Check if a given process type is a string excitation.
RectangularLattice< FourVector > * UI3_lat_pointer
Pointer to the symmmetry potential on the lattice.