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