Version: SMASH-3.1
collidermodus.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012-2023
3  * SMASH Team
4  *
5  * GNU General Public License (GPLv3 or later)
6  */
7 
8 #include "smash/collidermodus.h"
9 
10 #include <algorithm>
11 #include <cmath>
12 #include <cstdlib>
13 #include <cstring>
14 #include <memory>
15 #include <stdexcept>
16 #include <string>
17 #include <tuple>
18 #include <utility>
19 #include <vector>
20 
21 #include "smash/configuration.h"
22 #include "smash/customnucleus.h"
24 #include "smash/fourvector.h"
25 #include "smash/logging.h"
26 #include "smash/random.h"
27 
28 namespace smash {
29 static constexpr int LCollider = LogArea::Collider::id;
30 
32  const ExperimentParameters &params) {
33  Configuration modus_cfg =
34  modus_config.extract_sub_configuration({"Collider"});
35  // Get the reference frame for the collision calculation.
36  if (modus_cfg.has_value({"Calculation_Frame"})) {
37  frame_ = modus_cfg.take({"Calculation_Frame"});
38  }
39 
40  Configuration proj_cfg = modus_cfg.extract_sub_configuration({"Projectile"});
41  Configuration targ_cfg = modus_cfg.extract_sub_configuration({"Target"});
42  /* Needed to check if projectile and target in customnucleus are read from
43  * the same input file.*/
44  bool same_file = false;
45  // Set up the projectile nucleus
46  if (proj_cfg.has_value({"Deformed"})) {
47  projectile_ =
48  create_deformed_nucleus(proj_cfg, params.testparticles, "projectile");
49  } else if (proj_cfg.has_value({"Custom"})) {
50  same_file = same_inputfile(proj_cfg, targ_cfg);
51  projectile_ = std::make_unique<CustomNucleus>(
52  proj_cfg, params.testparticles, same_file);
53  } else {
54  projectile_ = std::make_unique<Nucleus>(proj_cfg, params.testparticles);
55  }
56  if (projectile_->size() < 1) {
57  throw ColliderEmpty("Input Error: Projectile nucleus is empty.");
58  }
60 
61  // Set up the target nucleus
62  if (targ_cfg.has_value({"Deformed"})) {
63  target_ = create_deformed_nucleus(targ_cfg, params.testparticles, "target");
64  } else if (targ_cfg.has_value({"Custom"})) {
65  target_ = std::make_unique<CustomNucleus>(targ_cfg, params.testparticles,
66  same_file);
67  } else {
68  target_ = std::make_unique<Nucleus>(targ_cfg, params.testparticles);
69  }
70  if (target_->size() < 1) {
71  throw ColliderEmpty("Input Error: Target nucleus is empty.");
72  }
73  target_->set_label(BelongsTo::Target);
74 
75  // Get the Fermi-Motion input (off, on, frozen)
76  if (modus_cfg.has_value({"Fermi_Motion"})) {
77  fermi_motion_ = modus_cfg.take({"Fermi_Motion"}, FermiMotion::Off);
78  }
79 
80  // Get the total nucleus-nucleus collision energy. Since there is
81  // no meaningful choice for a default energy, we require the user to
82  // give one (and only one) energy input from the available options.
83  int energy_input = 0;
84  const double mass_projec = projectile_->mass();
85  const double mass_target = target_->mass();
86  // average mass of a particle in that nucleus
87  const double mass_a =
88  projectile_->mass() / projectile_->number_of_particles();
89  const double mass_b = target_->mass() / target_->number_of_particles();
90  // Option 1: Center of mass energy.
91  if (modus_cfg.has_value({"Sqrtsnn"})) {
92  sqrt_s_NN_ = modus_cfg.take({"Sqrtsnn"});
93  // Check that input satisfies the lower bound (everything at rest).
94  if (sqrt_s_NN_ <= mass_a + mass_b) {
96  "Input Error: sqrt(s_NN) is not larger than masses:\n" +
97  std::to_string(sqrt_s_NN_) + " GeV <= " + std::to_string(mass_a) +
98  " GeV + " + std::to_string(mass_b) + " GeV.");
99  }
100  // Set the total nucleus-nucleus collision energy.
101  total_s_ = (sqrt_s_NN_ * sqrt_s_NN_ - mass_a * mass_a - mass_b * mass_b) *
102  mass_projec * mass_target / (mass_a * mass_b) +
103  mass_projec * mass_projec + mass_target * mass_target;
104  energy_input++;
105  }
106  /* Option 2: Total energy per nucleon of the projectile nucleus
107  * (target at rest). */
108  if (modus_cfg.has_value({"E_Tot"})) {
109  const double e_tot = modus_cfg.take({"E_Tot"});
110  if (e_tot < 0) {
112  "Input Error: "
113  "E_Tot must be nonnegative.");
114  }
115  // Set the total nucleus-nucleus collision energy.
116  total_s_ = s_from_Etot(e_tot * projectile_->number_of_particles(),
117  mass_projec, mass_target);
118  sqrt_s_NN_ = std::sqrt(s_from_Etot(e_tot, mass_a, mass_b));
119  energy_input++;
120  }
121  /* Option 3: Kinetic energy per nucleon of the projectile nucleus
122  * (target at rest). */
123  if (modus_cfg.has_value({"E_Kin"})) {
124  const double e_kin = modus_cfg.take({"E_Kin"});
125  if (e_kin < 0) {
127  "Input Error: "
128  "E_Kin must be nonnegative.");
129  }
130  // Set the total nucleus-nucleus collision energy.
131  total_s_ = s_from_Ekin(e_kin * projectile_->number_of_particles(),
132  mass_projec, mass_target);
133  sqrt_s_NN_ = std::sqrt(s_from_Ekin(e_kin, mass_a, mass_b));
134  energy_input++;
135  }
136  // Option 4: Momentum of the projectile nucleus (target at rest).
137  if (modus_cfg.has_value({"P_Lab"})) {
138  const double p_lab = modus_cfg.take({"P_Lab"});
139  if (p_lab < 0) {
141  "Input Error: "
142  "P_Lab must be nonnegative.");
143  }
144  // Set the total nucleus-nucleus collision energy.
145  total_s_ = s_from_plab(p_lab * projectile_->number_of_particles(),
146  mass_projec, mass_target);
147  sqrt_s_NN_ = std::sqrt(s_from_plab(p_lab, mass_a, mass_b));
148  energy_input++;
149  }
150  // Option 5: Total energy per nucleon of _each_ beam
151  if (proj_cfg.has_value({"E_Tot"}) && targ_cfg.has_value({"E_Tot"})) {
152  const double e_tot_p = proj_cfg.take({"E_Tot"});
153  const double e_tot_t = targ_cfg.take({"E_Tot"});
154  if (e_tot_p < 0 || e_tot_t < 0) {
156  "Input Error: "
157  "E_Tot must be nonnegative.");
158  }
159  total_s_ = s_from_Etot(e_tot_p * projectile_->number_of_particles(),
160  e_tot_t * target_->number_of_particles(),
161  mass_projec, mass_target);
162  sqrt_s_NN_ = std::sqrt(s_from_Ekin(e_tot_p, e_tot_t, mass_a, mass_b));
163  energy_input++;
164  }
165  // Option 6: Kinetic energy per nucleon of _each_ beam
166  if (proj_cfg.has_value({"E_Kin"}) && targ_cfg.has_value({"E_Kin"})) {
167  const double e_kin_p = proj_cfg.take({"E_Kin"});
168  const double e_kin_t = targ_cfg.take({"E_Kin"});
169  if (e_kin_p < 0 || e_kin_t < 0) {
171  "Input Error: "
172  "E_Kin must be nonnegative.");
173  }
174  total_s_ = s_from_Ekin(e_kin_p * projectile_->number_of_particles(),
175  e_kin_t * target_->number_of_particles(),
176  mass_projec, mass_target);
177  sqrt_s_NN_ = std::sqrt(s_from_Ekin(e_kin_p, e_kin_t, mass_a, mass_b));
178  energy_input++;
179  }
180  // Option 7: Momentum per nucleon of _each_ beam
181  if (proj_cfg.has_value({"P_Lab"}) && targ_cfg.has_value({"P_Lab"})) {
182  const double p_lab_p = proj_cfg.take({"P_Lab"});
183  const double p_lab_t = targ_cfg.take({"P_Lab"});
184  if (p_lab_p < 0 || p_lab_t < 0) {
186  "Input Error: "
187  "P_Lab must be nonnegative.");
188  }
189  total_s_ = s_from_plab(p_lab_p * projectile_->number_of_particles(),
190  p_lab_t * target_->number_of_particles(),
191  mass_projec, mass_target);
192  sqrt_s_NN_ = std::sqrt(s_from_plab(p_lab_p, p_lab_t, mass_a, mass_b));
193  energy_input++;
194  }
195  if (energy_input == 0) {
196  throw std::domain_error(
197  "Input Error: Non-existent collision energy. "
198  "Please provide one of Sqrtsnn/E_Kin/P_Lab.");
199  }
200  if (energy_input > 1) {
201  throw std::domain_error(
202  "Input Error: Redundant collision energy. "
203  "Please provide only one of Sqrtsnn/E_Kin/P_Lab.");
204  }
205 
206  /* Impact parameter setting: Either "Value", "Range", "Max" or "Sample".
207  * Unspecified means 0 impact parameter.*/
208  if (modus_cfg.has_value({"Impact", "Value"})) {
209  impact_ = modus_cfg.take({"Impact", "Value"});
210  imp_min_ = impact_;
211  imp_max_ = impact_;
212  } else {
213  // If impact is not supplied by value, inspect sampling parameters:
214  if (modus_cfg.has_value({"Impact", "Sample"})) {
215  sampling_ = modus_cfg.take({"Impact", "Sample"});
216  if (sampling_ == Sampling::Custom) {
217  if (!(modus_cfg.has_value({"Impact", "Values"}) ||
218  modus_cfg.has_value({"Impact", "Yields"}))) {
219  throw std::domain_error(
220  "Input Error: Need impact parameter spectrum for custom "
221  "sampling. "
222  "Please provide Values and Yields.");
223  }
224  const std::vector<double> impacts =
225  modus_cfg.take({"Impact", "Values"});
226  const std::vector<double> yields = modus_cfg.take({"Impact", "Yields"});
227  if (impacts.size() != yields.size()) {
228  throw std::domain_error(
229  "Input Error: Need as many impact parameter values as yields. "
230  "Please make sure that Values and Yields have the same length.");
231  }
232  impact_interpolation_ = std::make_unique<InterpolateDataLinear<double>>(
233  InterpolateDataLinear<double>(impacts, yields));
234 
235  const auto imp_minmax =
236  std::minmax_element(impacts.begin(), impacts.end());
237  imp_min_ = *imp_minmax.first;
238  imp_max_ = *imp_minmax.second;
239  yield_max_ = *std::max_element(yields.begin(), yields.end());
240  }
241  }
242  if (modus_cfg.has_value({"Impact", "Range"})) {
243  const std::array<double, 2> range = modus_cfg.take({"Impact", "Range"});
244  imp_min_ = range[0];
245  imp_max_ = range[1];
246  }
247  if (modus_cfg.has_value({"Impact", "Max"})) {
248  imp_min_ = 0.0;
249  imp_max_ = modus_cfg.take({"Impact", "Max"});
250  }
251  }
253  // whether the direction of separation should be ramdomly smapled
255  modus_cfg.take({"Impact", "Random_Reaction_Plane"}, false);
256  // Look for user-defined initial separation between nuclei.
257  if (modus_cfg.has_value({"Initial_Distance"})) {
258  initial_z_displacement_ = modus_cfg.take({"Initial_Distance"});
259  // the displacement is half the distance (both nuclei are shifted
260  // initial_z_displacement_ away from origin)
262  }
263 
265  logg[LCollider].info() << "Fermi motion is ON.";
266  } else if (fermi_motion_ == FermiMotion::Frozen) {
267  logg[LCollider].info() << "FROZEN Fermi motion is on.";
268  } else if (fermi_motion_ == FermiMotion::Off) {
269  logg[LCollider].info() << "Fermi motion is OFF.";
270  }
271 }
272 
273 std::ostream &operator<<(std::ostream &out, const ColliderModus &m) {
274  return out << "-- Collider Modus:\n"
275  << "sqrt(S) (nucleus-nucleus) = "
276  << format(std::sqrt(m.total_s_), "GeV\n")
277  << "sqrt(S) (nucleon-nucleon) = " << format(m.sqrt_s_NN_, "GeV\n")
278  << "Projectile:\n"
279  << *m.projectile_ << "\nTarget:\n"
280  << *m.target_;
281 }
282 
283 std::unique_ptr<DeformedNucleus> ColliderModus::create_deformed_nucleus(
284  Configuration &nucleus_cfg, int ntest, const std::string &nucleus_type) {
285  bool automatic_deformation = nucleus_cfg.take({"Deformed", "Automatic"});
286  bool was_any_beta_given = nucleus_cfg.has_value({"Deformed", "Beta_2"}) ||
287  nucleus_cfg.has_value({"Deformed", "Beta_3"}) ||
288  nucleus_cfg.has_value({"Deformed", "Beta_4"});
289  bool was_any_deformation_parameter_given =
290  was_any_beta_given || nucleus_cfg.has_value({"Deformed", "Gamma"});
291  bool was_gamma_given_without_beta_2 =
292  nucleus_cfg.has_value({"Deformed", "Gamma"}) &&
293  !nucleus_cfg.has_value({"Deformed", "Beta_2"});
294 
295  if (automatic_deformation && was_any_deformation_parameter_given) {
296  throw std::domain_error(
297  "Automatic deformation of " + nucleus_type +
298  " nucleus requested, but deformation parameter(s) were provided as"
299  " well. Please, check the 'Deformed' section in your input file.");
300  } else if (!automatic_deformation && !was_any_beta_given) {
301  throw std::domain_error(
302  "Manual deformation of " + nucleus_type +
303  " nucleus requested, but no deformation beta parameter was provided."
304  " Please, check the 'Deformed' section in your input file.");
305  } else if (!automatic_deformation && was_gamma_given_without_beta_2) {
306  throw std::domain_error(
307  "Manual deformation of " + nucleus_type +
308  " nucleus requested, but 'Gamma' parameter was provided without "
309  "providing a value of 'Beta_2' having hence no deformation effect. "
310  "Please, check the 'Deformed' section in your input file.");
311  } else {
312  return std::make_unique<DeformedNucleus>(nucleus_cfg, ntest,
313  automatic_deformation);
314  }
315 }
316 
318  const ExperimentParameters &) {
319  // Populate the nuclei with appropriately distributed nucleons.
320  // If deformed, this includes rotating the nucleus.
321  projectile_->arrange_nucleons();
322  target_->arrange_nucleons();
323 
324  // Use the total mandelstam variable to get the frame-dependent velocity for
325  // each nucleus. Position a is projectile, position b is target.
326  double v_a, v_b;
327  std::tie(v_a, v_b) =
328  get_velocities(total_s_, projectile_->mass(), target_->mass());
329 
330  // If velocities are larger or equal to 1, throw an exception.
331  if (v_a >= 1.0 || v_b >= 1.0) {
332  throw std::domain_error(
333  "Found velocity equal to or larger than 1 in "
334  "ColliderModus::initial_conditions.\nConsider using "
335  "the center of velocity reference frame.");
336  }
337 
338  // Calculate the beam velocity of the projectile and the target, which will be
339  // used to calculate the beam momenta in experiment.cc
341  velocity_projectile_ = v_a;
342  velocity_target_ = v_b;
343  }
344 
345  // Generate Fermi momenta if necessary
348  // Frozen: Fermi momenta will be ignored during the propagation to
349  // avoid that the nuclei will fly apart.
350  projectile_->generate_fermi_momenta();
351  target_->generate_fermi_momenta();
352  } else if (fermi_motion_ == FermiMotion::Off) {
353  } else {
354  throw std::domain_error("Invalid Fermi_Motion input.");
355  }
356 
357  // Boost the nuclei to the appropriate velocity.
358  projectile_->boost(v_a);
359  target_->boost(v_b);
360 
361  // Shift the nuclei into starting positions. Contracted spheres with
362  // nuclear radii should touch exactly at t=0. Modus starts at negative
363  // time corresponding to additional initial displacement.
364  const double d_a = std::max(0., projectile_->get_diffusiveness());
365  const double d_b = std::max(0., target_->get_diffusiveness());
366  const double r_a = projectile_->get_nuclear_radius();
367  const double r_b = target_->get_nuclear_radius();
368  const double dz = initial_z_displacement_;
369 
370  const double simulation_time = -dz / std::abs(v_a);
371  const double proj_z = -dz - std::sqrt(1.0 - v_a * v_a) * (r_a + d_a);
372  const double targ_z =
373  +dz * std::abs(v_b / v_a) + std::sqrt(1.0 - v_b * v_b) * (r_b + d_b);
374  // rotation angle in the transverse plane
375  const double phi =
376  random_reaction_plane_ ? random::uniform(0.0, 2.0 * M_PI) : 0.0;
377 
378  projectile_->shift(proj_z, +impact_ / 2.0, simulation_time);
379  target_->shift(targ_z, -impact_ / 2.0, simulation_time);
380 
381  // Put the particles in the nuclei into code particles.
382  projectile_->copy_particles(particles);
383  target_->copy_particles(particles);
384  rotate_reaction_plane(phi, particles);
385  return simulation_time;
386 }
387 
388 void ColliderModus::rotate_reaction_plane(double phi, Particles *particles) {
389  for (ParticleData &p : *particles) {
390  ThreeVector pos = p.position().threevec();
391  ThreeVector mom = p.momentum().threevec();
392  pos.rotate_around_z(phi);
393  mom.rotate_around_z(phi);
394  p.set_3position(pos);
395  p.set_3momentum(mom);
396  }
397 }
398 
400  switch (sampling_) {
401  case Sampling::Quadratic: {
402  // quadratic sampling: Note that for bmin > bmax, this still yields
403  // the correct distribution (however canonical() = 0 is then the
404  // upper end, not the lower).
405  impact_ = std::sqrt(imp_min_ * imp_min_ +
408  } break;
409  case Sampling::Custom: {
410  // rejection sampling based on given distribution
411  assert(impact_interpolation_ != nullptr);
412  double probability_random = 1;
413  double probability = 0;
414  double b;
415  while (probability_random > probability) {
417  probability = (*impact_interpolation_)(b) / yield_max_;
418  assert(probability < 1.);
419  probability_random = random::uniform(0., 1.);
420  }
421  impact_ = b;
422  } break;
423  case Sampling::Uniform: {
424  // linear sampling. Still, min > max works fine.
426  }
427  }
428 }
429 
430 std::pair<double, double> ColliderModus::get_velocities(double s, double m_a,
431  double m_b) {
432  double v_a = 0.0;
433  double v_b = 0.0;
434  // Frame dependent calculations of velocities. Assume v_a >= 0, v_b <= 0.
435  switch (frame_) {
437  v_a = center_of_velocity_v(s, m_a, m_b);
438  v_b = -v_a;
439  break;
441  // Compute center of mass momentum.
442  double pCM = pCM_from_s(s, m_a, m_b);
443  v_a = pCM / std::sqrt(m_a * m_a + pCM * pCM);
444  v_b = -pCM / std::sqrt(m_b * m_b + pCM * pCM);
445  } break;
447  v_a = fixed_target_projectile_v(s, m_a, m_b);
448  break;
449  default:
450  throw std::domain_error(
451  "Invalid reference frame in "
452  "ColliderModus::get_velocities.");
453  }
454  return std::make_pair(v_a, v_b);
455 }
456 
457 std::string ColliderModus::custom_file_path(const std::string &file_directory,
458  const std::string &file_name) {
459  // make sure that path is correct even if the / at the end is missing
460  if (file_directory.back() == '/') {
461  return file_directory + file_name;
462  } else {
463  return file_directory + '/' + file_name;
464  }
465 }
466 
468  Configuration &targ_config) {
469  /* Check if both nuclei are custom
470  * Only check target as function is called after if statement for projectile.
471  */
472  if (!targ_config.has_value({"Custom"})) {
473  return false;
474  }
475  std::string projectile_file_directory =
476  proj_config.read({"Custom", "File_Directory"});
477  std::string target_file_directory =
478  targ_config.read({"Custom", "File_Directory"});
479  std::string projectile_file_name = proj_config.read({"Custom", "File_Name"});
480  std::string target_file_name = targ_config.read({"Custom", "File_Name"});
481  // Check if files are the same for projectile and target
482  std::string proj_path =
483  custom_file_path(projectile_file_directory, projectile_file_name);
484  std::string targ_path =
485  custom_file_path(target_file_directory, target_file_name);
486  if (proj_path == targ_path) {
487  return true;
488  } else {
489  return false;
490  }
491 }
492 
493 } // namespace smash
ColliderModus: Provides a modus for colliding nuclei.
Definition: collidermodus.h:44
CalculationFrame frame_
Reference frame for the system, as specified from config.
double imp_min_
Minimum value of impact parameter.
double initial_z_displacement_
Initial z-displacement of nuclei.
double yield_max_
Maximum value of yield. Needed for custom impact parameter sampling.
bool random_reaction_plane_
Whether the reaction plane should be randomized.
void rotate_reaction_plane(double phi, Particles *particles)
Rotate the reaction plane about the angle phi.
std::pair< double, double > get_velocities(double mandelstam_s, double m_a, double m_b)
Get the frame dependent velocity for each nucleus, using the current reference frame.
void sample_impact()
Sample impact parameter.
std::unique_ptr< Nucleus > projectile_
Projectile.
std::unique_ptr< InterpolateDataLinear< double > > impact_interpolation_
Pointer to the impact parameter interpolation.
FermiMotion fermi_motion_
An option to include Fermi motion ("off", "on", "frozen")
double velocity_projectile_
Beam velocity of the projectile.
Sampling sampling_
Method used for sampling of impact parameter.
std::string custom_file_path(const std::string &file_directory, const std::string &file_name)
Creates full path string consisting of file_directory and file_name Needed to initialize a customnucl...
double total_s_
Center-of-mass energy squared of the nucleus-nucleus collision.
std::unique_ptr< Nucleus > target_
Target.
ColliderModus(Configuration modus_config, const ExperimentParameters &parameters)
Constructor.
double impact_
Impact parameter.
double sqrt_s_NN_
Center-of-mass energy of a nucleon-nucleon collision.
double initial_conditions(Particles *particles, const ExperimentParameters &parameters)
Generates initial state of the particles in the system.
bool same_inputfile(Configuration &proj_config, Configuration &targ_config)
Checks if target and projectile are read from the same external file if they are both initialized as ...
static std::unique_ptr< DeformedNucleus > create_deformed_nucleus(Configuration &nucleus_cfg, const int ntest, const std::string &nucleus_type)
Configure Deformed Nucleus.
double velocity_target_
Beam velocity of the target.
double imp_max_
Maximum value of impact parameter.
Interface to the SMASH configuration files.
bool has_value(std::initializer_list< const char * > keys) const
Return whether there is a non-empty value behind the requested keys.
Configuration extract_sub_configuration(std::initializer_list< const char * > keys, Configuration::GetEmpty empty_if_not_existing=Configuration::GetEmpty::No)
Create a new configuration from a then-removed section of the present object.
Value take(std::initializer_list< const char * > keys)
The default interface for SMASH to read configuration values.
Value read(std::initializer_list< const char * > keys) const
Additional interface for SMASH to read configuration values without removing them.
Represent a piecewise linear interpolation.
Definition: interpolation.h:62
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:58
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:31
void rotate_around_z(double theta)
Rotate the vector around the z axis by the given angle theta.
Definition: threevector.h:315
@ On
Use fermi motion in combination with potentials.
@ Frozen
Use fermi motion without potentials.
@ Off
Don't use fermi motion.
@ Quadratic
Sample from areal / quadratic distribution.
@ Custom
Sample from custom, user-defined distribution.
@ Uniform
Sample from uniform distribution.
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.
Definition: action.h:547
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
FormattingHelper< T > format(const T &value, const char *unit, int width=-1, int precision=-1)
Acts as a stream modifier for std::ostream to output an object with an optional suffix string and wit...
Definition: logging.h:217
constexpr int p
Proton.
T uniform(T min, T max)
Definition: random.h:88
T canonical()
Definition: random.h:113
Definition: action.h:24
T pCM(const T sqrts, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:79
double fixed_target_projectile_v(double s, double ma, double mb)
Definition: kinematics.h:39
double s_from_Ekin(double e_kin, double m_P, double m_T)
Convert E_kin to Mandelstam-s for a fixed-target setup, with a projectile of mass m_P and a kinetic e...
Definition: kinematics.h:239
double center_of_velocity_v(double s, double ma, double mb)
Definition: kinematics.h:26
T pCM_from_s(const T s, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:66
static constexpr int LCollider
double s_from_Etot(double e_tot, double m_P, double m_T)
Convert E_tot to Mandelstam-s for a fixed-target setup, with a projectile of mass m_P and a total ene...
Definition: kinematics.h:211
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:265
Thrown when either projectile_ or target_ nuclei are empty.
Helper structure for Experiment.
int testparticles
Number of test-particles.
Thrown when the requested energy is smaller than the masses of two particles.
Definition: modusdefault.h:174