Version: SMASH-1.6
collidermodus.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2019
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/angles.h"
22 #include "smash/configuration.h"
23 #include "smash/customnucleus.h"
24 #include "smash/cxx14compat.h"
26 #include "smash/fourvector.h"
27 #include "smash/interpolation.h"
28 #include "smash/kinematics.h"
29 #include "smash/logging.h"
30 #include "smash/numerics.h"
31 #include "smash/particles.h"
32 #include "smash/pdgcode.h"
33 #include "smash/random.h"
34 
35 namespace smash {
36 
260  const ExperimentParameters &params) {
261  Configuration modus_cfg = modus_config["Collider"];
262 
263  // Get the reference frame for the collision calculation.
264  if (modus_cfg.has_value({"Calculation_Frame"})) {
265  frame_ = modus_cfg.take({"Calculation_Frame"});
266  }
267 
269  if (modus_cfg.has_value({"Collisions_Within_Nucleus"})) {
270  cll_in_nucleus_ = modus_cfg.take({"Collisions_Within_Nucleus"});
271  }
272 
273  // Set up the projectile nucleus
274  Configuration proj_cfg = modus_cfg["Projectile"];
275  if (proj_cfg.has_value({"Deformed"})) {
276  projectile_ =
277  create_deformed_nucleus(proj_cfg, params.testparticles, "projectile");
278  } else if (proj_cfg.has_value({"Custom"})) {
279  projectile_ = make_unique<CustomNucleus>(proj_cfg, params.testparticles);
280  } else {
281  projectile_ = make_unique<Nucleus>(proj_cfg, params.testparticles);
282  }
283  if (projectile_->size() < 1) {
284  throw ColliderEmpty("Input Error: Projectile nucleus is empty.");
285  }
286 
287  // Set up the target nucleus
288  Configuration targ_cfg = modus_cfg["Target"];
289  if (targ_cfg.has_value({"Deformed"})) {
290  target_ = create_deformed_nucleus(targ_cfg, params.testparticles, "target");
291  } else if (targ_cfg.has_value({"Custom"})) {
292  target_ = make_unique<CustomNucleus>(targ_cfg, params.testparticles);
293  } else {
294  target_ = make_unique<Nucleus>(targ_cfg, params.testparticles);
295  }
296  if (target_->size() < 1) {
297  throw ColliderEmpty("Input Error: Target nucleus is empty.");
298  }
299 
300  // Get the Fermi-Motion input (off, on, frozen)
301  if (modus_cfg.has_value({"Fermi_Motion"})) {
302  // We only read the value, because it is still required by the experiment
303  // class to make sure we don't use frozen Fermi momenta with potentials.
304  fermi_motion_ = modus_cfg.read({"Fermi_Motion"});
305  }
306 
307  // Get the total nucleus-nucleus collision energy. Since there is
308  // no meaningful choice for a default energy, we require the user to
309  // give one (and only one) energy input from the available options.
310  int energy_input = 0;
311  const double mass_projec = projectile_->mass();
312  const double mass_target = target_->mass();
313  // average mass of a particle in that nucleus
314  const double mass_a =
315  projectile_->mass() / projectile_->number_of_particles();
316  const double mass_b = target_->mass() / target_->number_of_particles();
317  // Option 1: Center of mass energy.
318  if (modus_cfg.has_value({"Sqrtsnn"})) {
319  sqrt_s_NN_ = modus_cfg.take({"Sqrtsnn"});
320  // Check that input satisfies the lower bound (everything at rest).
321  if (sqrt_s_NN_ <= mass_a + mass_b) {
323  "Input Error: sqrt(s_NN) is not larger than masses:\n" +
324  std::to_string(sqrt_s_NN_) + " GeV <= " + std::to_string(mass_a) +
325  " GeV + " + std::to_string(mass_b) + " GeV.");
326  }
327  // Set the total nucleus-nucleus collision energy.
328  total_s_ = (sqrt_s_NN_ * sqrt_s_NN_ - mass_a * mass_a - mass_b * mass_b) *
329  mass_projec * mass_target / (mass_a * mass_b) +
330  mass_projec * mass_projec + mass_target * mass_target;
331  energy_input++;
332  }
333  /* Option 2: Kinetic energy per nucleon of the projectile nucleus
334  * (target at rest). */
335  if (modus_cfg.has_value({"E_Kin"})) {
336  const double e_kin = modus_cfg.take({"E_Kin"});
337  if (e_kin < 0) {
339  "Input Error: "
340  "E_Kin must be nonnegative.");
341  }
342  // Set the total nucleus-nucleus collision energy.
343  total_s_ = s_from_Ekin(e_kin * projectile_->number_of_particles(),
344  mass_projec, mass_target);
345  sqrt_s_NN_ = std::sqrt(s_from_Ekin(e_kin, mass_a, mass_b));
346  energy_input++;
347  }
348  // Option 3: Momentum of the projectile nucleus (target at rest).
349  if (modus_cfg.has_value({"P_Lab"})) {
350  const double p_lab = modus_cfg.take({"P_Lab"});
351  if (p_lab < 0) {
353  "Input Error: "
354  "P_Lab must be nonnegative.");
355  }
356  // Set the total nucleus-nucleus collision energy.
357  total_s_ = s_from_plab(p_lab * projectile_->number_of_particles(),
358  mass_projec, mass_target);
359  sqrt_s_NN_ = std::sqrt(s_from_plab(p_lab, mass_a, mass_b));
360  energy_input++;
361  }
362  if (energy_input == 0) {
363  throw std::domain_error(
364  "Input Error: Non-existent collision energy. "
365  "Please provide one of Sqrtsnn/E_Kin/P_Lab.");
366  }
367  if (energy_input > 1) {
368  throw std::domain_error(
369  "Input Error: Redundant collision energy. "
370  "Please provide only one of Sqrtsnn/E_Kin/P_Lab.");
371  }
372 
373  /* Impact parameter setting: Either "Value", "Range", "Max" or "Sample".
374  * Unspecified means 0 impact parameter.*/
375  if (modus_cfg.has_value({"Impact", "Value"})) {
376  impact_ = modus_cfg.take({"Impact", "Value"});
377  imp_min_ = impact_;
378  imp_max_ = impact_;
379  } else {
380  // If impact is not supplied by value, inspect sampling parameters:
381  if (modus_cfg.has_value({"Impact", "Sample"})) {
382  sampling_ = modus_cfg.take({"Impact", "Sample"});
383  if (sampling_ == Sampling::Custom) {
384  if (!(modus_cfg.has_value({"Impact", "Values"}) ||
385  modus_cfg.has_value({"Impact", "Yields"}))) {
386  throw std::domain_error(
387  "Input Error: Need impact parameter spectrum for custom "
388  "sampling. "
389  "Please provide Values and Yields.");
390  }
391  const std::vector<double> impacts =
392  modus_cfg.take({"Impact", "Values"});
393  const std::vector<double> yields = modus_cfg.take({"Impact", "Yields"});
394  if (impacts.size() != yields.size()) {
395  throw std::domain_error(
396  "Input Error: Need as many impact parameter values as yields. "
397  "Please make sure that Values and Yields have the same length.");
398  }
399  impact_interpolation_ = make_unique<InterpolateDataLinear<double>>(
400  InterpolateDataLinear<double>(impacts, yields));
401 
402  const auto imp_minmax =
403  std::minmax_element(impacts.begin(), impacts.end());
404  imp_min_ = *imp_minmax.first;
405  imp_max_ = *imp_minmax.second;
406  yield_max_ = *std::max_element(yields.begin(), yields.end());
407  }
408  }
409  if (modus_cfg.has_value({"Impact", "Range"})) {
410  const std::array<double, 2> range = modus_cfg.take({"Impact", "Range"});
411  imp_min_ = range[0];
412  imp_max_ = range[1];
413  }
414  if (modus_cfg.has_value({"Impact", "Max"})) {
415  imp_min_ = 0.0;
416  imp_max_ = modus_cfg.take({"Impact", "Max"});
417  }
418  }
420 
421  // Look for user-defined initial separation between nuclei.
422  if (modus_cfg.has_value({"Initial_Distance"})) {
423  initial_z_displacement_ = modus_cfg.take({"Initial_Distance"});
424  // the displacement is half the distance (both nuclei are shifted
425  // initial_z_displacement_ away from origin)
427  }
428 }
429 
430 std::ostream &operator<<(std::ostream &out, const ColliderModus &m) {
431  return out << "-- Collider Modus:\n"
432  << "sqrt(S) (nucleus-nucleus) = "
433  << format(std::sqrt(m.total_s_), "GeV\n")
434  << "sqrt(S) (nucleon-nucleon) = " << format(m.sqrt_s_NN_, "GeV\n")
435  << "Projectile:\n"
436  << *m.projectile_ << "\nTarget:\n"
437  << *m.target_;
438 }
439 
440 std::unique_ptr<DeformedNucleus> ColliderModus::create_deformed_nucleus(
441  Configuration &nucleus_cfg, int ntest, const std::string &nucleus_type) {
442  bool auto_deform = nucleus_cfg.take({"Deformed", "Automatic"});
443  bool is_beta2 = nucleus_cfg.has_value({"Deformed", "Beta_2"}) ? true : false;
444  bool is_beta4 = nucleus_cfg.has_value({"Deformed", "Beta_4"}) ? true : false;
445  std::unique_ptr<DeformedNucleus> nucleus;
446 
447  if ((auto_deform && (!is_beta2 && !is_beta4)) ||
448  (!auto_deform && (is_beta2 && is_beta4))) {
449  nucleus = make_unique<DeformedNucleus>(nucleus_cfg, ntest);
450  return nucleus;
451  } else {
452  throw std::domain_error("Deformation of " + nucleus_type +
453  " nucleus not configured "
454  "properly, please check whether all necessary "
455  "parameters are set.");
456  }
457 }
458 
460  const ExperimentParameters &) {
461  const auto &log = logger<LogArea::Collider>();
462  sample_impact();
463 
464  log.info() << "Impact parameter = " << format(impact_, "fm");
465  // Populate the nuclei with appropriately distributed nucleons.
466  // If deformed, this includes rotating the nucleus.
467  projectile_->arrange_nucleons();
468  target_->arrange_nucleons();
469 
470  // Use the total mandelstam variable to get the frame-dependent velocity for
471  // each nucleus. Position a is projectile, position b is target.
472  double v_a, v_b;
473  std::tie(v_a, v_b) =
474  get_velocities(total_s_, projectile_->mass(), target_->mass());
475 
476  // If velocities are larger or equal to 1, throw an exception.
477  if (v_a >= 1.0 || v_b >= 1.0) {
478  throw std::domain_error(
479  "Found velocity equal to or larger than 1 in "
480  "ColliderModus::initial_conditions.\nConsider using "
481  "the center of velocity reference frame.");
482  }
483 
484  // Calculate the beam velocity of the projectile and the target, which will be
485  // used to calculate the beam momenta in experiment.cc
487  velocity_projectile_ = v_a;
488  velocity_target_ = v_b;
489  }
490 
491  // Generate Fermi momenta if necessary
494  // Frozen: Fermi momenta will be ignored during the propagation to
495  // avoid that the nuclei will fly apart.
496  projectile_->generate_fermi_momenta();
497  target_->generate_fermi_momenta();
499  log.info() << "Fermi motion is ON.";
500  } else {
501  log.info() << "FROZEN Fermi motion is on.";
502  }
503  } else if (fermi_motion_ == FermiMotion::Off) {
504  // No Fermi-momenta are generated in this case
505  log.info() << "Fermi motion is OFF.";
506  } else {
507  throw std::domain_error("Invalid Fermi_Motion input.");
508  }
509 
510  // Boost the nuclei to the appropriate velocity.
511  projectile_->boost(v_a);
512  target_->boost(v_b);
513 
514  // Shift the nuclei into starting positions. Contracted spheres with
515  // nuclear radii should touch exactly at t=0. Modus starts at negative
516  // time corresponding to additional initial displacement.
517  const double d_a = std::max(0., projectile_->get_diffusiveness());
518  const double d_b = std::max(0., target_->get_diffusiveness());
519  const double r_a = projectile_->get_nuclear_radius();
520  const double r_b = target_->get_nuclear_radius();
521  const double dz = initial_z_displacement_;
522 
523  const double simulation_time = -dz / std::abs(v_a);
524  const double proj_z = -dz - std::sqrt(1.0 - v_a * v_a) * (r_a + d_a);
525  const double targ_z =
526  +dz * std::abs(v_b / v_a) + std::sqrt(1.0 - v_b * v_b) * (r_b + d_b);
527  projectile_->shift(proj_z, +impact_ / 2.0, simulation_time);
528  target_->shift(targ_z, -impact_ / 2.0, simulation_time);
529 
530  // Put the particles in the nuclei into code particles.
531  projectile_->copy_particles(particles);
532  target_->copy_particles(particles);
533  return simulation_time;
534 }
535 
537  switch (sampling_) {
538  case Sampling::Quadratic: {
539  // quadratic sampling: Note that for bmin > bmax, this still yields
540  // the correct distribution (however canonical() = 0 is then the
541  // upper end, not the lower).
542  impact_ = std::sqrt(imp_min_ * imp_min_ +
545  } break;
546  case Sampling::Custom: {
547  // rejection sampling based on given distribution
548  assert(impact_interpolation_ != nullptr);
549  double probability_random = 1;
550  double probability = 0;
551  double b;
552  while (probability_random > probability) {
554  probability = (*impact_interpolation_)(b) / yield_max_;
555  assert(probability < 1.);
556  probability_random = random::uniform(0., 1.);
557  }
558  impact_ = b;
559  } break;
560  case Sampling::Uniform: {
561  // linear sampling. Still, min > max works fine.
563  }
564  }
565 }
566 
567 std::pair<double, double> ColliderModus::get_velocities(double s, double m_a,
568  double m_b) {
569  double v_a = 0.0;
570  double v_b = 0.0;
571  // Frame dependent calculations of velocities. Assume v_a >= 0, v_b <= 0.
572  switch (frame_) {
574  v_a = center_of_velocity_v(s, m_a, m_b);
575  v_b = -v_a;
576  break;
578  // Compute center of mass momentum.
579  double pCM = pCM_from_s(s, m_a, m_b);
580  v_a = pCM / std::sqrt(m_a * m_a + pCM * pCM);
581  v_b = -pCM / std::sqrt(m_b * m_b + pCM * pCM);
582  } break;
584  v_a = fixed_target_projectile_v(s, m_a, m_b);
585  break;
586  default:
587  throw std::domain_error(
588  "Invalid reference frame in "
589  "ColliderModus::get_velocities.");
590  }
591  return std::make_pair(v_a, v_b);
592 }
593 
594 } // namespace smash
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:310
double yield_max_
Maximum value of yield. Needed for custom impact parameter sampling.
Thrown when the requested energy is smaller than the masses of two particles.
Definition: modusdefault.h:172
double fixed_target_projectile_v(double s, double ma, double mb)
Definition: kinematics.h:39
double velocity_target_
Beam velocity of the target.
static std::unique_ptr< DeformedNucleus > create_deformed_nucleus(Configuration &nucleus_cfg, const int ntest, const std::string &nucleus_type)
Configure Deformed Nucleus.
double center_of_velocity_v(double s, double ma, double mb)
Definition: kinematics.h:26
double impact_
Impact parameter.
T pCM_from_s(const T s, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:66
Value read(std::initializer_list< const char * > keys) const
Additional interface for SMASH to read configuration values without removing them.
Interface to the SMASH configuration files.
std::unique_ptr< InterpolateDataLinear< double > > impact_interpolation_
Pointer to the impact parameter interpolation.
double imp_min_
Minimum value of impact parameter.
bool has_value(std::initializer_list< const char * > keys) const
Returns whether there is a non-empty value behind the requested keys.
Generic numerical functions.
T canonical()
Definition: random.h:113
ColliderModus(Configuration modus_config, const ExperimentParameters &parameters)
Constructor.
std::unique_ptr< Nucleus > projectile_
Projectile.
Sample from uniform distribution.
Sampling sampling_
Method used for sampling of impact parameter.
Thrown when either projectile_ or target_ nuclei are empty.
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
Sample from custom, user-defined distribution.
Value take(std::initializer_list< const char * > keys)
The default interface for SMASH to read configuration values.
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.
FermiMotion fermi_motion_
An option to include Fermi motion ("off", "on", "frozen")
double initial_z_displacement_
Initial z-displacement of nuclei.
Sample from areal / quadratic distribution.
Don&#39;t use fermi motion.
ColliderModus: Provides a modus for colliding nuclei.
Definition: collidermodus.h:42
double sqrt_s_NN_
Center-of-mass energy of a nucleon-nucleon collision.
T uniform(T min, T max)
Definition: random.h:88
Use fermi motion without potentials.
bool cll_in_nucleus_
An option to accept first collisions within the same nucleus.
double initial_conditions(Particles *particles, const ExperimentParameters &parameters)
Generates initial state of the particles in the system.
std::unique_ptr< Nucleus > target_
Target.
double velocity_projectile_
Beam velocity of the projectile.
void sample_impact()
Sample impact parameter.
int testparticles
Number of test particle.
friend std::ostream & operator<<(std::ostream &, const ColliderModus &)
Writes the initial state for the ColliderModus to the output stream.
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
T pCM(const T sqrts, const T mass_a, const T mass_b) noexcept
Definition: kinematics.h:79
double imp_max_
Maximum value of impact parameter.
Helper structure for Experiment.
Use fermi motion in combination with potentials.
CalculationFrame frame_
Reference frame for the system, as specified from config.
Definition: action.h:24
double total_s_
Center-of-mass energy squared of the nucleus-nucleus collision.
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:211