Version: SMASH-2.0
experiment.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2012-2020
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/experiment.h"
11 
12 #include <cstdint>
13 
14 #include "smash/boxmodus.h"
15 #include "smash/collidermodus.h"
16 #include "smash/cxx14compat.h"
17 #include "smash/listmodus.h"
18 #include "smash/spheremodus.h"
19 
20 namespace smash {
21 
22 /* ExperimentBase carries everything that is needed for the evolution */
23 ExperimentPtr ExperimentBase::create(Configuration config,
24  const bf::path &output_path) {
25  logg[LExperiment].trace() << source_location;
47  const std::string modus_chooser = config.read({"General", "Modus"});
48  logg[LExperiment].debug() << "Modus for this calculation: " << modus_chooser;
49 
50  if (modus_chooser == "Box") {
51  return make_unique<Experiment<BoxModus>>(config, output_path);
52  } else if (modus_chooser == "List") {
53  return make_unique<Experiment<ListModus>>(config, output_path);
54  } else if (modus_chooser == "Collider") {
55  return make_unique<Experiment<ColliderModus>>(config, output_path);
56  } else if (modus_chooser == "Sphere") {
57  return make_unique<Experiment<SphereModus>>(config, output_path);
58  } else {
59  throw InvalidModusRequest("Invalid Modus (" + modus_chooser +
60  ") requested from ExperimentBase::create.");
61  }
62 }
63 
336  logg[LExperiment].trace() << source_location;
337 
338  const int ntest = config.take({"General", "Testparticles"}, 1);
339  if (ntest <= 0) {
340  throw std::invalid_argument("Testparticle number should be positive!");
341  }
342 
343  const std::string modus_chooser = config.take({"General", "Modus"});
344  // remove config maps of unused Modi
345  config["Modi"].remove_all_but(modus_chooser);
346 
347  double box_length = -1.0;
348  if (config.has_value({"Modi", "Box", "Length"})) {
349  box_length = config.read({"Modi", "Box", "Length"});
350  }
351 
352  /* If this Delta_Time option is absent (this can be for timestepless mode)
353  * just assign 1.0 fm/c, reasonable value will be set at event initialization
354  */
355  const double dt = config.take({"General", "Delta_Time"}, 1.);
356  const double t_end = config.read({"General", "End_Time"});
357 
358  // Enforce a small time step, if the box modus is used
359  if (box_length > 0.0 && dt > box_length / 10.0) {
360  throw std::invalid_argument(
361  "Please decrease the timestep size. "
362  "A value of (dt < l_box / 10) is recommended in the boxmodus.");
363  }
364 
365  // define output clock
366  std::unique_ptr<Clock> output_clock = nullptr;
367  if (config.has_value({"Output", "Output_Times"})) {
368  if (config.has_value({"Output", "Output_Interval"})) {
369  throw std::invalid_argument(
370  "Please specify either Output_Interval or Output_Times");
371  }
372  std::vector<double> output_times = config.take({"Output", "Output_Times"});
373  // Add an output time larger than the end time so that the next time is
374  // always defined during the time evolution
375  output_times.push_back(t_end + 1.);
376  output_clock = make_unique<CustomClock>(output_times);
377  } else {
378  const double output_dt = config.take({"Output", "Output_Interval"}, t_end);
379  output_clock = make_unique<UniformClock>(0.0, output_dt);
380  }
381 
382  // Add proper error messages if photons are not configured properly.
383  // 1) Missing Photon config section.
384  if (config["Output"].has_value({"Photons"}) &&
385  (!config.has_value({"Collision_Term", "Photons"}))) {
386  throw std::invalid_argument(
387  "Photon output is enabled although photon production is disabled. "
388  "Photon production can be configured in the \"Photon\" subsection "
389  "of the \"Collision_Term\".");
390  }
391 
392  // 2) Missing Photon output section.
393  bool missing_output_2to2 = false;
394  bool missing_output_brems = false;
395  if (!(config["Output"].has_value({"Photons"}))) {
396  if (config.has_value({"Collision_Term", "Photons", "2to2_Scatterings"})) {
397  missing_output_2to2 =
398  config.read({"Collision_Term", "Photons", "2to2_Scatterings"});
399  }
400  if (config.has_value({"Collision_Term", "Photons", "Bremsstrahlung"})) {
401  missing_output_brems =
402  config.read({"Collision_Term", "Photons", "Bremsstrahlung"});
403  }
404 
405  if (missing_output_2to2 || missing_output_brems) {
406  throw std::invalid_argument(
407  "Photon output is disabled although photon production is enabled. "
408  "Please enable the photon output.");
409  }
410  }
411 
412  // Add proper error messages if dileptons are not configured properly.
413  // 1) Missing Dilepton config section.
414  if (config["Output"].has_value({"Dileptons"}) &&
415  (!config.has_value({"Collision_Term", "Dileptons"}))) {
416  throw std::invalid_argument(
417  "Dilepton output is enabled although dilepton production is disabled. "
418  "Dilepton production can be configured in the \"Dileptons\" subsection "
419  "of the \"Collision_Term\".");
420  }
421 
422  // 2) Missing Dilepton output section.
423  bool missing_output_decays = false;
424  if (!(config["Output"].has_value({"Dileptons"}))) {
425  if (config.has_value({"Collision_Term", "Dileptons", "Decays"})) {
426  missing_output_decays =
427  config.read({"Collision_Term", "Dileptons", "Decays"});
428  }
429 
430  if (missing_output_decays) {
431  throw std::invalid_argument(
432  "Dilepton output is disabled although dilepton production is "
433  "enabled. "
434  "Please enable the dilepton output.");
435  }
436  }
437 
438  auto config_coll = config["Collision_Term"];
439  /* Elastic collisions between the nucleons with the square root s
440  * below low_snn_cut are excluded. */
441  const double low_snn_cut =
442  config_coll.take({"Elastic_NN_Cutoff_Sqrts"}, 1.98);
443  const auto proton = ParticleType::try_find(pdg::p);
444  const auto pion = ParticleType::try_find(pdg::pi_z);
445  if (proton && pion &&
446  low_snn_cut > proton->mass() + proton->mass() + pion->mass()) {
447  logg[LExperiment].warn("The cut-off should be below the threshold energy",
448  " of the process: NN to NNpi");
449  }
450  const bool potential_affect_threshold =
451  config.take({"Lattice", "Potentials_Affect_Thresholds"}, false);
452  const double scale_xs = config_coll.take({"Cross_Section_Scaling"}, 1.0);
461  const double maximum_cross_section_default =
462  ParticleType::exists("d'") ? 2000.0 : 200.0;
463  double maximum_cross_section = config_coll.take(
464  {"Maximum_Cross_Section"}, maximum_cross_section_default);
465  maximum_cross_section *= scale_xs;
466  return {
467  make_unique<UniformClock>(0.0, dt),
468  std::move(output_clock),
469  ntest,
470  config.take({"General", "Gaussian_Sigma"}, 1.),
471  config.take({"General", "Gauss_Cutoff_In_Sigma"}, 4.),
472  config_coll.take({"Collision_Criterion"}, CollisionCriterion::Covariant),
473  config_coll.take({"Two_to_One"}, true),
474  config_coll.take({"Included_2to2"}, ReactionsBitSet().set()),
475  config_coll.take({"Multi_Particle_Reactions"},
476  MultiParticleReactionsBitSet().reset()),
477  config_coll.take({"Strings"}, modus_chooser != "Box"),
478  config_coll.take({"Use_AQM"}, true),
479  config_coll.take({"Resonance_Lifetime_Modifier"}, 1.),
480  config_coll.take({"Strings_with_Probability"}, true),
481  config_coll.take({"NNbar_Treatment"}, NNbarTreatment::Strings),
482  low_snn_cut,
483  potential_affect_threshold,
484  box_length,
485  maximum_cross_section,
486  scale_xs,
487  config_coll.take({"Additional_Elastic_Cross_Section"}, 0.0)};
488 }
489 
490 std::string format_measurements(const Particles &particles,
491  uint64_t scatterings_this_interval,
492  const QuantumNumbers &conserved_initial,
493  SystemTimePoint time_start, double time,
494  double E_mean_field,
495  double E_mean_field_initial) {
496  const SystemTimeSpan elapsed_seconds = SystemClock::now() - time_start;
497 
498  const QuantumNumbers current_values(particles);
499  const QuantumNumbers difference = current_values - conserved_initial;
500 
501  // Make sure there are no FPEs in case of IC output, were there will
502  // eventually be no more particles in the system
503  const double current_energy =
504  (particles.size() > 0) ? current_values.momentum().x0() : 0.0;
505  const double energy_per_part =
506  (particles.size() > 0)
507  ? (current_energy + E_mean_field) / particles.size()
508  : 0.0;
509 
510  std::ostringstream ss;
511  // clang-format off
512  ss << field<7, 3> << time
513  // total kinetic energy in the system
514  << field<11, 3> << current_energy
515  // total mean field energy in the system
516  << field<11, 3> << E_mean_field
517  // total energy in the system
518  << field<12, 3> << current_energy + E_mean_field
519  // total energy per particle in the system
520  << field<12, 6> << energy_per_part;
521  // change in total energy per particle (unless IC output is enabled)
522  if (particles.size() == 0) {
523  ss << field<13, 6> << "N/A";
524  } else {
525  ss << field<13, 6> << (difference.momentum().x0()
526  + E_mean_field - E_mean_field_initial)
527  / particles.size();
528  }
529  ss << field<14, 3> << scatterings_this_interval
530  << field<10, 3> << particles.size()
531  << field<9, 3> << elapsed_seconds;
532  // clang-format on
533  return ss.str();
534 }
535 
537  const Potentials &potentials,
539  const ExperimentParameters &parameters) {
540  // basic parameters and variables
541  const double V_cell = (jmu_B_lat.cell_sizes())[0] *
542  (jmu_B_lat.cell_sizes())[1] *
543  (jmu_B_lat.cell_sizes())[2];
544 
545  double E_mean_field = 0.0;
546  double density_mean = 0.0;
547  double density_variance = 0.0;
548 
549  /*
550  * We anticipate having other options, like the vector DFT potentials, in the
551  * future, hence we include checking which potentials are used.
552  */
553  if (potentials.use_skyrme()) {
554  /*
555  * Calculating the symmetry energy contribution to the total mean field
556  * energy in the system is not implemented at this time.
557  */
558  if (potentials.use_symmetry() &&
559  parameters.outputclock->current_time() == 0.0) {
560  logg[LExperiment].warn()
561  << "Note:"
562  << "\nSymmetry energy is not included in the mean field calculation."
563  << "\n\n";
564  }
565 
566  /*
567  * Skyrme potential parameters:
568  * C1GeV are the Skyrme coefficients converted to GeV,
569  * b1 are the powers of the baryon number density entering the expression
570  * for the energy density of the system. Note that these exponents are
571  * larger by 1 than those for the energy of a particle (which are used in
572  * Potentials class). The formula for a total mean field energy due to a
573  * Skyrme potential is E_MF = \sum_i (C_i/b_i) ( n_B^b_i )/( n_0^(b_i - 1) )
574  * where nB is the local rest frame baryon number density and n_0 is the
575  * saturation density. Then the single particle potential follows from
576  * V = d E_MF / d n_B .
577  */
578  double C1GeV = (potentials.skyrme_a()) / 1000.0;
579  double C2GeV = (potentials.skyrme_b()) / 1000.0;
580  double b1 = 2.0;
581  double b2 = (potentials.skyrme_tau()) + 1.0;
582 
583  /*
584  * Note: calculating the mean field only works if lattice is used.
585  * We iterate over the nodes of the baryon density lattice to sum their
586  * contributions to the total mean field.
587  */
588  int number_of_nodes = 0;
589  double lattice_mean_field_total = 0.0;
590 
591  for (auto &node : jmu_B_lat) {
592  number_of_nodes++;
593  // the rest frame density
594  double nB = node.density();
595  // the computational frame density
596  const double j0 = node.jmu_net().x0();
597 
598  const double abs_nB = std::abs(nB);
599  if ((abs_nB < really_small) || (std::abs(j0) < really_small)) {
600  continue;
601  }
602  density_mean += j0;
603  density_variance += j0 * j0;
604 
605  /*
606  * The mean-field energy for the Skyrme potential. Note: this expression
607  * is only exact in the rest frame, and is expected to significantly
608  * deviate from the correct value for systems that are considerably
609  * relativistic. Note: symmetry energy is not taken into the account.
610  *
611  * TODO: Add symmetry energy.
612  */
613  double mean_field_contribution_1 = (C1GeV / b1) * std::pow(abs_nB, b1) /
614  std::pow(nuclear_density, b1 - 1);
615  double mean_field_contribution_2 = (C2GeV / b2) * std::pow(abs_nB, b2) /
616  std::pow(nuclear_density, b2 - 1);
617 
618  lattice_mean_field_total +=
619  V_cell * (mean_field_contribution_1 + mean_field_contribution_2);
620  }
621 
622  // logging statistical properties of the density calculation
623  density_mean = density_mean / number_of_nodes;
624  density_variance = density_variance / number_of_nodes;
625  double density_scaled_variance =
626  std::sqrt(density_variance - density_mean * density_mean) /
627  density_mean;
628  logg[LExperiment].debug() << "\t\t\t\t\t";
629  logg[LExperiment].debug()
630  << "\n\t\t\t\t\t density mean = " << density_mean;
631  logg[LExperiment].debug()
632  << "\n\t\t\t\t\t density scaled variance = " << density_scaled_variance;
633  logg[LExperiment].debug()
634  << "\n\t\t\t\t\t total mean_field = "
635  << lattice_mean_field_total * parameters.testparticles << "\n";
636 
637  /*
638  * E_mean_field is multiplied by the number of testparticles because the
639  * total kinetic energy tracked is that of all particles, including
640  * testparticles, and so this is more consistent with the general paradigm.
641  */
642  E_mean_field = lattice_mean_field_total * parameters.testparticles;
643  }
644 
645  return E_mean_field;
646 }
647 
648 EventInfo fill_event_info(const Particles &particles, double E_mean_field,
649  double modus_impact_parameter,
650  const ExperimentParameters &parameters,
651  bool projectile_target_interact) {
652  const QuantumNumbers current_values(particles);
653  const double E_kinetic_total = current_values.momentum().x0();
654  const double E_total = E_kinetic_total + E_mean_field;
655 
656  EventInfo event_info{modus_impact_parameter,
657  parameters.box_length,
658  parameters.outputclock->current_time(),
659  E_kinetic_total,
660  E_mean_field,
661  E_total,
662  parameters.testparticles,
663  !projectile_target_interact};
664  return event_info;
665 }
666 
667 } // namespace smash
smash
Definition: action.h:24
smash::SystemTimePoint
std::chrono::time_point< std::chrono::system_clock > SystemTimePoint
Type (alias) that is used to store the current time.
Definition: chrono.h:22
smash::ParticleType::exists
static bool exists(PdgCode pdgcode)
Definition: particletype.cc:107
smash::QuantumNumbers::momentum
FourVector momentum() const
Definition: quantumnumbers.h:131
smash::Potentials::skyrme_tau
double skyrme_tau() const
Definition: potentials.h:200
smash::Particles::size
size_t size() const
Definition: particles.h:87
smash::LExperiment
static constexpr int LExperiment
Definition: outputparameters.h:19
smash::Configuration::read
Value read(std::initializer_list< const char * > keys) const
Additional interface for SMASH to read configuration values without removing them.
Definition: configuration.cc:158
cxx14compat.h
NNbarTreatment::Strings
Use string fragmentation.
smash::format_measurements
std::string format_measurements(const Particles &particles, uint64_t scatterings_this_interval, const QuantumNumbers &conserved_initial, SystemTimePoint time_start, double time, double E_mean_field, double E_mean_field_initial)
Generate the tabulated string which will be printed to the screen when SMASH is running.
Definition: experiment.cc:490
smash::nuclear_density
constexpr double nuclear_density
Ground state density of symmetric nuclear matter [fm^-3].
Definition: constants.h:45
smash::Configuration::has_value
bool has_value(std::initializer_list< const char * > keys) const
Returns whether there is a non-empty value behind the requested keys.
Definition: configuration.cc:181
smash::Potentials::use_symmetry
virtual bool use_symmetry() const
Definition: potentials.h:193
smash::Potentials::skyrme_b
double skyrme_b() const
Definition: potentials.h:198
smash::Potentials::use_skyrme
virtual bool use_skyrme() const
Definition: potentials.h:191
smash::SystemTimeSpan
SystemClock::duration SystemTimeSpan
The time duration type (alias) used for measuring run times.
Definition: chrono.h:28
ReactionsBitSet
std::bitset< 10 > ReactionsBitSet
Container for the 2 to 2 reactions in the code.
Definition: forwarddeclarations.h:231
smash::EventInfo
Structure to contain custom data for output.
Definition: outputinterface.h:35
smash::pdg::pi_z
constexpr int pi_z
π⁰.
Definition: pdgcode_constants.h:64
smash::logg
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
smash::ExperimentParameters::box_length
double box_length
Length of the box in fm in case of box modus, otherwise -1.
Definition: experimentparameters.h:96
smash::really_small
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:37
smash::Configuration
Interface to the SMASH configuration files.
Definition: configuration.h:464
MultiParticleReactionsBitSet
std::bitset< 2 > MultiParticleReactionsBitSet
Container for the 2 to 2 reactions in the code.
Definition: forwarddeclarations.h:240
smash::fill_event_info
EventInfo fill_event_info(const Particles &particles, double E_mean_field, double modus_impact_parameter, const ExperimentParameters &parameters, bool projectile_target_interact)
Generate the EventInfo object which is passed to outputs_.
Definition: experiment.cc:648
source_location
#define source_location
Hackery that is required to output the location in the source code where the log statement occurs.
Definition: logging.h:243
boxmodus.h
experiment.h
spheremodus.h
CollisionCriterion::Covariant
Covariant Criterion.
smash::FourVector::x0
double x0() const
Definition: fourvector.h:303
smash::create_experiment_parameters
ExperimentParameters create_experiment_parameters(Configuration config)
Gathers all general Experiment parameters.
Definition: experiment.cc:335
smash::RectangularLattice
A container class to hold all the arrays on the lattice and access them.
Definition: lattice.h:47
smash::Potentials
A class that stores parameters of potentials, calculates potentials and their gradients.
Definition: potentials.h:32
collidermodus.h
smash::ParticleType::try_find
static const ParticleTypePtr try_find(PdgCode pdgcode)
Returns the ParticleTypePtr for the given pdgcode.
Definition: particletype.cc:89
smash::Potentials::skyrme_a
double skyrme_a() const
Definition: potentials.h:196
smash::Particles
Definition: particles.h:33
smash::RectangularLattice::cell_sizes
const std::array< double, 3 > & cell_sizes() const
Definition: lattice.h:158
smash::ExperimentParameters::outputclock
std::unique_ptr< Clock > outputclock
Output clock to keep track of the next output time.
Definition: experimentparameters.h:29
smash::calculate_mean_field_energy
double calculate_mean_field_energy(const Potentials &potentials, RectangularLattice< smash::DensityOnLattice > &jmu_B_lat, const ExperimentParameters &parameters)
Calculate the total mean field energy of the system; this will be printed to the screen when SMASH is...
Definition: experiment.cc:536
smash::Configuration::take
Value take(std::initializer_list< const char * > keys)
The default interface for SMASH to read configuration values.
Definition: configuration.cc:140
listmodus.h
smash::ExperimentParameters
Helper structure for Experiment.
Definition: experimentparameters.h:24
smash::pdg::p
constexpr int p
Proton.
Definition: pdgcode_constants.h:28
smash::Configuration::remove_all_but
void remove_all_but(const std::string &key)
Removes all entries in the map except for key.
Definition: configuration.cc:163
smash::ExperimentParameters::testparticles
int testparticles
Number of test particle.
Definition: experimentparameters.h:32
smash::ExperimentBase::create
static std::unique_ptr< ExperimentBase > create(Configuration config, const bf::path &output_path)
Factory method that creates and initializes a new Experiment<Modus>.
smash::QuantumNumbers
Definition: quantumnumbers.h:53