Version: SMASH-2.2
smash.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2012-2022
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 #include <getopt.h>
10 
11 #include <set>
12 #include <sstream>
13 #include <vector>
14 
15 #include <boost/filesystem/fstream.hpp>
16 
17 #include "smash/cxx14compat.h"
18 #include "smash/decaymodes.h"
19 #include "smash/experiment.h"
20 #include "smash/filelock.h"
21 #include "smash/random.h"
24 #include "smash/sha256.h"
25 #include "smash/stringfunctions.h"
26 /* build dependent variables */
27 #include "smash/config.h"
28 
29 #include "smash/library.h"
30 
31 namespace smash {
32 
33 namespace {
45 void usage(const int rc, const std::string &progname) {
133  std::printf("\nUsage: %s [option]\n\n", progname.c_str());
134  std::printf(
135  " -h, --help usage information\n"
136  "\n"
137  " -i, --inputfile <file> path to input configuration file\n"
138  " (default: ./config.yaml)\n"
139  " -d, --decaymodes <file> override default decay modes from file\n"
140  " -p, --particles <file> override default particles from file\n"
141  "\n"
142  " -c, --config <YAML> specify config value overrides\n"
143  " (multiple -c arguments are supported)\n"
144  " -m, --modus <modus> shortcut for -c 'General: { Modus: <modus> "
145  "}'\n"
146  " -e, --endtime <time> shortcut for -c 'General: { End_Time: <time> "
147  "}'"
148  "\n"
149  "\n"
150  " -o, --output <dir> output directory (default: ./data/<runid>)\n"
151  " -l, --list-2-to-n list all possible 2->n reactions (with n>1)\n"
152  " -r, --resonance <pdg> dump width(m) and m*spectral function(m^2)"
153  " for resonance pdg\n"
154  " -s, --cross-sections <pdg1>,<pdg2>[,mass1,mass2[,plab1,...]] \n"
155  " dump all partial cross-sections of "
156  "pdg1 + pdg2 reactions versus sqrt(s).\n"
157  " -S, --cross-sections-fs <pdg1>,<pdg2>[,mass1,mass2[,plab1,...]] \n"
158  " dump an approximation of the final-state"
159  " cross-sections\n"
160  " of pdg1 + pdg2 reactions versus sqrt(s).\n"
161  " Contributions from strings are not considered"
162  " for the final state.\n"
163  " Masses are optional, by default pole masses"
164  " are used.\n"
165  " Note the required comma and no spaces.\n"
166  " -f, --force force overwriting files in the output "
167  "directory"
168  "\n"
169  " -x, --dump_iSS Dump particle table in iSS format\n"
170  " This format is used in MUSIC and CLVisc\n"
171  " relativistic hydro codes\n"
172  " -q, --quiet Supress disclaimer print-out\n"
173  " -n, --no-cache Don't cache integrals on disk\n"
174  " -v, --version\n\n");
175  std::exit(rc);
176 }
177 
180  std::cout
181  << "###################################################################"
182  << "############"
183  << "\n"
184  << "\n"
185  << " :::s.\n"
186  << " .... ''ss:: "
187  "ah:\n"
188  << " a::''. ..:sss "
189  ".HH.\n"
190  << " mss'.. ...s'm. sSA##As mAhh##hsh##s. .hA##ASa sS###As "
191  "hMAh##h.\n"
192  << " :a':'. .'':as sM#' .HHm''HMS''AMs mMA' .AMs aMA. "
193  "'HHa..HM:\n"
194  << " .a:s'. ..''ss 'h#H#S. mMm 'M#' .HH. 'MH. :MA 'h#H#S. "
195  "hMm :M#\n"
196  << " .::ss'. .... 'SMm .HH. SMa hMm sM#..mHMs 'AMa "
197  "'MH. SMa\n"
198  << " .s::' #SMASHh aMS .MH: HM: #MMMmMM. #SMASHh "
199  "aMS .MM.\n"
200  << "\n"
201  << "###################################################################"
202  << "############"
203  << "\n"
204  << " This is SMASH version: " << SMASH_VERSION << "\n"
205  << " Simulating Many Accelerated Strongly-interacting Hadrons"
206  << "\n"
207  << "\n"
208  << " Distributed under the GNU General Public License 3.0"
209  << " (GPLv3 or later)."
210  << "\n"
211  << " See LICENSE for details."
212  << "\n"
213  << " For the full list of contributors see AUTHORS."
214  << "\n"
215  << "\n"
216  << " When using SMASH, please cite"
217  << "\n"
218  << " J. Weil et al., Phys.Rev. C94 (2016) no.5, 054905"
219  << "\n"
220  << " together with the software DOI for the specific code version "
221  << "employed:"
222  << "\n"
223  << " https://doi.org/10.5281/zenodo.3484711."
224  << "\n"
225  << " In addition, if Pythia is used please cite"
226  << "\n"
227  << " T. Sjöstrand, S. Mrenna and P. Skands, JHEP05 (2006) 026,"
228  << "\n"
229  << " Comput. Phys. Comm. 178 (2008) 852."
230  << "\n"
231  << "\n"
232  << " Webpage: https://smash-transport.github.io"
233  << "\n"
234  << "\n"
235  << " Report issues at https://github.com/smash-transport/smash"
236  << "\n"
237  << " or contact us by email at elfner@itp.uni-frankfurt.de"
238  << "\n"
239  << "\n"
240  << "###################################################################"
241  << "############"
242  << "\n"
243  << "\n";
244 }
245 
251 struct OutputDirectoryExists : public std::runtime_error {
252  using std::runtime_error::runtime_error;
253 };
260 struct OutputDirectoryOutOfIds : public std::runtime_error {
261  using std::runtime_error::runtime_error;
262 };
263 
266  const bf::path p = bf::absolute("data");
267  if (!bf::exists(p)) {
268  return p / "0";
269  }
270  bf::path p2;
271  for (int id = 0; id < std::numeric_limits<int>::max(); ++id) {
272  p2 = p / std::to_string(id);
273  if (!bf::exists(p2)) {
274  break;
275  }
276  }
277  if (p == p2) {
278  throw OutputDirectoryOutOfIds("no unique data subdir ID left");
279  }
280  return p2;
281 }
282 
289 void ensure_path_is_valid(const bf::path &path) {
290  if (bf::exists(path)) {
291  if (!bf::is_directory(path)) {
292  throw OutputDirectoryExists("The given path (" + path.native() +
293  ") exists, but it is not a directory.");
294  }
295  } else {
296  if (!bf::create_directories(path)) {
297  throw OutputDirectoryExists(
298  "Race condition detected: The directory " + path.native() +
299  " did not exist a few cycles ago, but was created in the meantime by "
300  "a different process.");
301  }
302  }
303 }
304 
313  return ScatterActionsFinder(configuration, params);
314 }
315 
325  const std::string smash_version = "1.8";
326  const std::set<std::string> compatible_config_versions = {"1.8"};
327 
328  const std::string config_version = configuration.read({"Version"});
329 
330  if (compatible_config_versions.find(config_version) ==
331  compatible_config_versions.end()) {
332  std::stringstream err;
333  err << "The version of the configuration file (" << config_version
334  << ") is not compatible with the SMASH version (" << smash_version
335  << ").\nThe following config versions are supported:\n";
336  for (auto it : compatible_config_versions) {
337  err << it << " ";
338  }
339  err << "\nPlease consider updating your config or using a compatible SMASH"
340  " version.";
341  throw std::runtime_error(err.str());
342  }
343 }
344 
349  const std::string report = configuration.unused_values_report();
350 
351  if (report != "{}") {
352  throw std::runtime_error(
353  "The following configuration values were not used:\n" + report);
354  }
355 }
356 
364  for (const std::string s :
365  {"Version", "particles", "decaymodes", "Modi", "General", "Output",
366  "Lattice", "Potentials", "Forced_Thermalization"}) {
367  if (configuration.has_value({s.c_str()})) {
368  configuration.take({s.c_str()});
369  }
370  }
371 }
372 
373 } // unnamed namespace
374 
375 } // namespace smash
376 
387 int main(int argc, char *argv[]) {
388  using namespace smash; // NOLINT(build/namespaces)
389 
390  constexpr option longopts[] = {
391  {"config", required_argument, 0, 'c'},
392  {"decaymodes", required_argument, 0, 'd'},
393  {"endtime", required_argument, 0, 'e'},
394  {"force", no_argument, 0, 'f'},
395  {"help", no_argument, 0, 'h'},
396  {"inputfile", required_argument, 0, 'i'},
397  {"modus", required_argument, 0, 'm'},
398  {"particles", required_argument, 0, 'p'},
399  {"output", required_argument, 0, 'o'},
400  {"list-2-to-n", no_argument, 0, 'l'},
401  {"resonance", required_argument, 0, 'r'},
402  {"cross-sections", required_argument, 0, 's'},
403  {"cross-sections-fs", required_argument, 0, 'S'},
404  {"dump-iSS", no_argument, 0, 'x'},
405  {"version", no_argument, 0, 'v'},
406  {"no-cache", no_argument, 0, 'n'},
407  {"quiet", no_argument, 0, 'q'},
408  {nullptr, 0, 0, 0}};
409 
410  // strip any path to progname
411  const std::string progname = bf::path(argv[0]).filename().native();
412 
413  try {
414  bool force_overwrite = false;
415  bf::path output_path = default_output_path();
416  std::string input_path("./config.yaml"), particles, decaymodes;
417  std::vector<std::string> extra_config;
418  char *modus = nullptr, *end_time = nullptr, *pdg_string = nullptr,
419  *cs_string = nullptr;
420  bool list2n_activated = false;
421  bool resonance_dump_activated = false;
422  bool cross_section_dump_activated = false;
423  bool final_state_cross_sections = false;
424  bool particles_dump_iSS_format = false;
425  bool cache_integrals = true;
426  bool suppress_disclaimer = false;
427 
428  // parse command-line arguments
429  int opt;
430  while ((opt = getopt_long(argc, argv, "c:d:e:fhi:m:p:o:lr:s:S:xvnq",
431  longopts, nullptr)) != -1) {
432  switch (opt) {
433  case 'c':
434  extra_config.emplace_back(optarg);
435  break;
436  case 'd':
437  decaymodes = optarg;
438  break;
439  case 'f':
440  force_overwrite = true;
441  break;
442  case 'i':
443  input_path = optarg;
444  break;
445  case 'h':
446  usage(EXIT_SUCCESS, progname);
447  break;
448  case 'm':
449  modus = optarg;
450  break;
451  case 'p':
452  particles = optarg;
453  break;
454  case 'e':
455  end_time = optarg;
456  break;
457  case 'o':
458  output_path = optarg;
459  break;
460  case 'l':
461  list2n_activated = true;
462  suppress_disclaimer = true;
463  break;
464  case 'r':
465  resonance_dump_activated = true;
466  pdg_string = optarg;
467  suppress_disclaimer = true;
468  break;
469  case 'S':
470  final_state_cross_sections = true;
471  // fallthrough
472  case 's':
473  cross_section_dump_activated = true;
474  cs_string = optarg;
475  suppress_disclaimer = true;
476  break;
477  case 'x':
478  particles_dump_iSS_format = true;
479  suppress_disclaimer = true;
480  break;
481  case 'v':
482  std::printf(
483  "%s\n"
484 #ifdef GIT_BRANCH
485  "Branch : %s\n"
486 #endif
487  "System : %s\nCompiler : %s %s\n"
488  "Build : %s\nDate : %s\n",
489  SMASH_VERSION,
490 #ifdef GIT_BRANCH
491  GIT_BRANCH,
492 #endif
493  CMAKE_SYSTEM, CMAKE_CXX_COMPILER_ID, CMAKE_CXX_COMPILER_VERSION,
494  CMAKE_BUILD_TYPE, BUILD_DATE);
495  std::exit(EXIT_SUCCESS);
496  case 'n':
497  cache_integrals = false;
498  break;
499  case 'q':
500  suppress_disclaimer = true;
501  break;
502  default:
503  usage(EXIT_FAILURE, progname);
504  }
505  }
506 
507  // Abort if there are unhandled arguments left.
508  if (optind < argc) {
509  std::cout << argv[0] << ": invalid argument -- '" << argv[optind]
510  << "'\n";
511  usage(EXIT_FAILURE, progname);
512  }
513 
514  if (!suppress_disclaimer) {
516  }
517 
518  auto configuration = setup_config_and_logging(input_path, particles,
519  decaymodes, extra_config);
520 
523 
524  // Check output path
525  ensure_path_is_valid(output_path);
526  std::string tabulations_path;
527  if (cache_integrals) {
528  tabulations_path = output_path.has_parent_path()
529  ? output_path.parent_path().string()
530  : ".";
531  tabulations_path += "/tabulations";
532  } else {
533  tabulations_path = "";
534  }
535  const std::string version(SMASH_VERSION);
536 
537  if (list2n_activated) {
538  /* Print only 2->n, n > 1. Do not dump decays, which can be found in
539  * decaymodes.txt anyway */
540  configuration.merge_yaml("{Collision_Term: {Two_to_One: False}}");
542  tabulations_path);
543  auto scat_finder = actions_finder_for_dump(configuration);
544 
547 
548  scat_finder.dump_reactions();
549  std::exit(EXIT_SUCCESS);
550  }
551  if (particles_dump_iSS_format) {
553  tabulations_path);
554  ParticleTypePtrList list;
555  list.clear();
556  for (const auto &ptype : ParticleType::list_all()) {
557  list.push_back(&ptype);
558  }
559  std::sort(list.begin(), list.end(),
561  return a->mass() < b->mass();
562  });
563  for (const ParticleTypePtr &ptype : list) {
564  if (ptype->pdgcode().is_lepton() || ptype->baryon_number() < 0) {
565  continue;
566  }
567  const auto &decay_modes = ptype->decay_modes();
568  const auto &modelist = decay_modes.decay_mode_list();
569  int ndecays = ptype->is_stable() ? 1 : modelist.size();
570  std::printf("%13i %s %10.5f %10.5f %5i %5i %5i %5i %5i %5i %5i %5i\n",
571  ptype->pdgcode().get_decimal(),
572  smash::utf8::fill_left(ptype->name(), 12, ' ').c_str(),
573  ptype->mass(), ptype->width_at_pole(),
574  ptype->pdgcode().spin_degeneracy(), ptype->baryon_number(),
575  ptype->strangeness(), ptype->pdgcode().charmness(),
576  ptype->pdgcode().bottomness(), ptype->isospin() + 1,
577  ptype->charge(), ndecays);
578  if (!ptype->is_stable()) {
579  for (const auto &decay : modelist) {
580  auto ptypes = decay->particle_types();
581  std::printf("%13i %13i %20.5f %13i %13i %13i %13i %13i\n",
582  ptype->pdgcode().get_decimal(), 2, decay->weight(),
583  ptypes[0]->pdgcode().get_decimal(),
584  ptypes[1]->pdgcode().get_decimal(), 0, 0, 0);
585  }
586  } else {
587  std::printf("%13i %13i %20.5f %13i %13i %13i %13i %13i\n",
588  ptype->pdgcode().get_decimal(), 1, 1.0,
589  ptype->pdgcode().get_decimal(), 0, 0, 0, 0);
590  }
591  }
592  std::exit(EXIT_SUCCESS);
593  }
594  if (resonance_dump_activated) {
595  // Ignore config values that don't make sense.
597  tabulations_path);
598  const auto _dummy = ExperimentBase::create(configuration, output_path);
601 
602  PdgCode pdg(pdg_string);
603  const ParticleType &res = ParticleType::find(pdg);
605  std::exit(EXIT_SUCCESS);
606  }
607  if (cross_section_dump_activated) {
609  tabulations_path);
610  std::string arg_string(cs_string);
611  std::vector<std::string> args = split(arg_string, ',');
612  const unsigned int n_arg = args.size();
613  if (n_arg != 2 && n_arg != 4 && n_arg < 5) {
614  throw std::invalid_argument("-s usage: pdg1,pdg2[,m1,m2[,sqrts1,...]]");
615  }
616  PdgCode pdg_a(args[0]), pdg_b(args[1]);
617  const ParticleType &a = ParticleType::find(pdg_a);
618  const ParticleType &b = ParticleType::find(pdg_b);
619  if (n_arg < 4) {
620  for (unsigned int i = 0; i < 4 - n_arg; i++) {
621  args.push_back("");
622  }
623  }
624  double ma = (args[2] == "") ? a.mass() : std::stod(args[2]);
625  double mb = (args[3] == "") ? b.mass() : std::stod(args[3]);
626  if (a.is_stable() && args[2] != "" && std::stod(args[2]) != a.mass()) {
627  ma = a.mass();
628  std::cerr << "Warning: pole mass is used for stable particle "
629  << a.name() << " instead of " << args[2] << std::endl;
630  }
631  if (b.is_stable() && args[3] != "" && std::stod(args[3]) != b.mass()) {
632  mb = b.mass();
633  std::cerr << "Warning: pole mass is used for stable particle "
634  << b.name() << " instead of " << args[3] << std::endl;
635  }
636  const size_t plab_size = n_arg <= 4 ? 0 : n_arg - 4;
637  std::vector<double> plab;
638  plab.reserve(plab_size);
639  for (size_t i = 4; i < n_arg; i++) {
640  plab.push_back(std::stod(args.at(i)));
641  }
642  auto scat_finder = actions_finder_for_dump(configuration);
643 
646 
647  scat_finder.dump_cross_sections(a, b, ma, mb, final_state_cross_sections,
648  plab);
649  std::exit(EXIT_SUCCESS);
650  }
651  if (modus) {
652  configuration["General"]["Modus"] = std::string(modus);
653  }
654  if (end_time) {
655  configuration["General"]["End_Time"] = std::abs(std::atof(end_time));
656  }
657 
658  int64_t seed = configuration.read({"General", "Randomseed"});
659  if (seed < 0) {
660  configuration["General"]["Randomseed"] = random::generate_63bit_seed();
661  }
662 
663  // Avoid overwriting SMASH output
664  const bf::path lock_path = output_path / "smash.lock";
665  FileLock lock(lock_path);
666  if (!lock.acquire()) {
667  throw std::runtime_error(
668  "Another instance of SMASH is already writing to the specified "
669  "output directory. If you are sure this is not the case, remove \"" +
670  lock_path.native() + "\".");
671  }
672  logg[LMain].debug("output path: ", output_path);
673  if (!force_overwrite && bf::exists(output_path / "config.yaml")) {
674  throw std::runtime_error(
675  "Output directory would get overwritten. Select a different output "
676  "directory, clean up, or tell SMASH to ignore existing files.");
677  }
678 
679  /* Keep a copy of the configuration that was used in the output directory
680  * also save information about SMASH build as a comment */
681  bf::ofstream(output_path / "config.yaml")
682  << "# " << SMASH_VERSION << '\n'
683 #ifdef GIT_BRANCH
684  << "# Branch : " << GIT_BRANCH << '\n'
685 #endif
686  << "# System : " << CMAKE_SYSTEM << '\n'
687  << "# Compiler : " << CMAKE_CXX_COMPILER_ID << ' '
688  << CMAKE_CXX_COMPILER_VERSION << '\n'
689  << "# Build : " << CMAKE_BUILD_TYPE << '\n'
690  << "# Date : " << BUILD_DATE << '\n'
691  << configuration.to_string() << '\n';
692 
694  tabulations_path);
695 
696  // Create an experiment
697  logg[LMain].trace(SMASH_SOURCE_LOCATION, " create Experiment");
698  auto experiment = ExperimentBase::create(configuration, output_path);
699 
700  // Version value is not used in experiment. Get rid of it to prevent
701  // warning.
702  configuration.take({"Version"});
704 
705  // Run the experiment
706  logg[LMain].trace(SMASH_SOURCE_LOCATION, " run the Experiment");
707  experiment->run();
708  } catch (std::exception &e) {
709  logg[LMain].fatal() << "SMASH failed with the following error:\n"
710  << e.what();
711  return EXIT_FAILURE;
712  }
713 
714  logg[LMain].trace() << SMASH_SOURCE_LOCATION << " about to return from main";
715  return 0;
716 }
Interface to the SMASH configuration files.
bool has_value(std::initializer_list< const char * > keys) const
Returns whether there is a non-empty value behind the requested keys.
void merge_yaml(const std::string &yaml)
Merge the configuration in yaml into the existing tree.
std::string to_string() const
Returns a YAML string of the current tree.
std::string unused_values_report() const
Returns a string listing the key/value pairs that have not been taken yet.
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.
Guard to create a file lock.
Definition: filelock.h:30
bool acquire()
Try to acquire the file lock.
Definition: filelock.cc:20
A pointer-like interface to global references to ParticleType objects.
Definition: particletype.h:671
Particle type contains the static properties of a particle species.
Definition: particletype.h:97
void dump_width_and_spectral_function() const
Prints out width and spectral function versus mass to the standard output.
const std::string & name() const
Definition: particletype.h:141
bool is_stable() const
Definition: particletype.h:242
double mass() const
Definition: particletype.h:144
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
Definition: pdgcode.h:108
A simple scatter finder: Just loops through all particles and checks each pair for a collision.
#define SMASH_SOURCE_LOCATION
Hackery that is required to output the location in the source code where the log statement occurs.
Definition: logging.h:243
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
Configuration configuration(std::string overrides={})
Return a configuration object filled with data from src/config.yaml.
Definition: setup.h:173
std::unique_ptr< ExperimentBase > experiment(const Configuration &c=configuration())
Create an experiment.
Definition: setup.h:187
void ignore_simulation_config_values(Configuration &configuration)
Remove all config values that are only needed for simulations.
Definition: smash.cc:363
ScatterActionsFinder actions_finder_for_dump(Configuration configuration)
Prepares ActionsFinder for cross-section and reaction dumps.
Definition: smash.cc:311
void check_for_unused_config_values(const Configuration &configuration)
Checks if there are unused config values.
Definition: smash.cc:348
void ensure_path_is_valid(const bf::path &path)
Ensures the output path is valid.
Definition: smash.cc:289
void print_disclaimer()
Print the disclaimer.
Definition: smash.cc:179
void check_config_version_is_compatible(Configuration configuration)
Checks if the SMASH version is compatible with the version of the configuration file.
Definition: smash.cc:324
constexpr int p
Proton.
int64_t generate_63bit_seed()
Generates a seed with a truly random 63-bit value, if possible.
Definition: random.cc:18
std::string fill_left(const std::string &s, size_t width, char fill=' ')
Fill string with characters to the left until the given width is reached.
Definition: action.h:24
std::vector< std::string > split(const std::string &s, char delim)
Split string by delimiter.
void initialize_particles_decays_and_tabulations(Configuration &configuration, const std::string &version, const std::string &tabulations_dir={})
Initialize the particles and decays from the given configuration, plus tabulate the resonance integra...
Definition: library.cc:76
Configuration setup_config_and_logging(const std::string &config_file, const std::string &particles_file={}, const std::string &decaymodes_file={}, const std::vector< std::string > &extra_config={})
Set up configuration and logging from input files and extra config.
Definition: library.cc:23
void setup_default_float_traps()
Setup the floating-point traps used throughout SMASH.
static constexpr int LMain
Definition: experiment.h:87
ExperimentParameters create_experiment_parameters(Configuration config)
Gathers all general Experiment parameters.
Definition: experiment.cc:494
int main(int argc, char *argv[])
Main program Smashes Many Accelerated Strongly-Interacting Hadrons :-)
Definition: smash.cc:387
Helper structure for Experiment.
Exception class that is thrown, if the requested output directory already exists and -f was not speci...
Definition: smash.cc:251
Exception class that is thrown, if no new output path can be generated (there is a directory name for...
Definition: smash.cc:260