Version: SMASH-1.5
smash.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2012-2018
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/inputfunctions.h"
22 #include "smash/random.h"
24 #include "smash/stringfunctions.h"
25 /* build dependent variables */
26 #include "smash/config.h"
27 
28 namespace smash {
29 
30 namespace {
42 void usage(const int rc, const std::string &progname) {
117  std::printf("\nUsage: %s [option]\n\n", progname.c_str());
118  std::printf(
119  " -h, --help usage information\n"
120  "\n"
121  " -i, --inputfile <file> path to input configuration file\n"
122  " (default: ./config.yaml)\n"
123  " -d, --decaymodes <file> override default decay modes from file\n"
124  " -p, --particles <file> override default particles from file\n"
125  "\n"
126  " -c, --config <YAML> specify config value overrides\n"
127  " (multiple -c arguments are supported)\n"
128  " -m, --modus <modus> shortcut for -c 'General: { Modus: <modus> "
129  "}'\n"
130  " -e, --endtime <time> shortcut for -c 'General: { End_Time: <time> "
131  "}'"
132  "\n"
133  "\n"
134  " -o, --output <dir> output directory (default: ./data/<runid>)\n"
135  " -l, --list-2-to-n list all possible 2->n reactions (with n>1)\n"
136  " -r, --resonance <pdg> dump width(m) and m*spectral function(m^2)"
137  " for resonance pdg\n"
138  " -s, --cross-sections <pdg1>,<pdg2>[,mass1,mass2] \n"
139  " dump all partial cross-sections of "
140  "pdg1 + pdg2 reactions versus sqrt(s).\n"
141  " -S, --cross-sections-fs <pdg1>,<pdg2>[,mass1,mass2] \n"
142  " dump all partial final-state cross-sections "
143  "of pdg1 + pdg2 reactions versus sqrt(s). Non-deterministic if strings "
144  "are enabled.\n"
145  " Masses are optional, by default pole masses"
146  " are used.\n"
147  " Note the required comma and no spaces.\n"
148  " -f, --force force overwriting files in the output "
149  "directory"
150  "\n"
151  " -v, --version\n\n");
152  std::exit(rc);
153 }
154 
157  std::cout
158  << "###################################################################"
159  << "############"
160  << "\n"
161  << "\n"
162  << " :::s.\n"
163  << " .... ''ss:: "
164  "ah:\n"
165  << " a::''. ..:sss "
166  ".HH.\n"
167  << " mss'.. ...s'm. sSA##As mAhh##hsh##s. .hA##ASa sS###As "
168  "hMAh##h.\n"
169  << " :a':'. .'':as sM#' .HHm''HMS''AMs mMA' .AMs aMA. "
170  "'HHa..HM:\n"
171  << " .a:s'. ..''ss 'h#H#S. mMm 'M#' .HH. 'MH. :MA 'h#H#S. "
172  "hMm :M#\n"
173  << " .::ss'. .... 'SMm .HH. SMa hMm sM#..mHMs 'AMa "
174  "'MH. SMa\n"
175  << " .s::' #SMASHh aMS .MH: HM: #MMMmMM. #SMASHh "
176  "aMS .MM.\n"
177  << "\n"
178  << "###################################################################"
179  << "############"
180  << "\n"
181  << " This is SMASH version: " << VERSION_MAJOR << "\n"
182  << " Simulating Many Accelerated Strongly-interacting Hadrons"
183  << "\n"
184  << "\n"
185  << " Distributed under the GNU General Public License 3.0"
186  << " (GPLv3 or later)."
187  << "\n"
188  << " See LICENSE for details."
189  << "\n"
190  << " For the full list of contributors see AUTHORS."
191  << "\n"
192  << "\n"
193  << " When using SMASH, please cite"
194  << "\n"
195  << " J. Weil et al., Phys.Rev. C94 (2016) no.5, 054905"
196  << "\n"
197  << " and in addition, if Pythia is used please cite"
198  << "\n"
199  << " T. Sjöstrand, S. Mrenna and P. Skands, JHEP05 (2006) 026,"
200  << "\n"
201  << " Comput. Phys. Comm. 178 (2008) 852."
202  << "\n"
203  << "\n"
204  << " Webpage: https://smash-transport.github.io"
205  << "\n"
206  << "\n"
207  << " Report issues at https://github.com/smash-transport/smash"
208  << "\n"
209  << " or contact us by email at elfner@th.physik.uni-frankfurt.de"
210  << "\n"
211  << "\n"
212  << "###################################################################"
213  << "############"
214  << "\n"
215  << "\n";
216 }
217 
223 struct OutputDirectoryExists : public std::runtime_error {
224  using std::runtime_error::runtime_error;
225 };
232 struct OutputDirectoryOutOfIds : public std::runtime_error {
233  using std::runtime_error::runtime_error;
234 };
235 
238  const bf::path p = bf::absolute("data");
239  if (!bf::exists(p)) {
240  return p / "0";
241  }
242  bf::path p2;
243  for (int id = 0; id < std::numeric_limits<int>::max(); ++id) {
244  p2 = p / std::to_string(id);
245  if (!bf::exists(p2)) {
246  break;
247  }
248  }
249  if (p == p2) {
250  throw OutputDirectoryOutOfIds("no unique data subdir ID left");
251  }
252  return p2;
253 }
254 
261 void ensure_path_is_valid(const bf::path &path) {
262  if (bf::exists(path)) {
263  if (!bf::is_directory(path)) {
264  throw OutputDirectoryExists("The given path (" + path.native() +
265  ") exists, but it is not a directory.");
266  }
267  } else {
268  if (!bf::create_directories(path)) {
269  throw OutputDirectoryExists(
270  "Race condition detected: The directory " + path.native() +
271  " did not exist a few cycles ago, but was created in the meantime by "
272  "a different process.");
273  }
274  }
275 }
276 
284  std::vector<bool> nucleon_has_interacted = {};
286  return ScatterActionsFinder(configuration, params, nucleon_has_interacted, 0,
287  0);
288 }
289 
299  const std::string smash_version = "1.5";
300  const std::set<std::string> compatible_config_versions = {"1.5"};
301 
302  const std::string config_version = configuration.read({"Version"});
303 
304  if (compatible_config_versions.find(config_version) ==
305  compatible_config_versions.end()) {
306  std::stringstream err;
307  err << "The version of the configuration file (" << config_version
308  << ") is not compatible with the SMASH version (" << smash_version
309  << ").\nThe following config versions are supported:\n";
310  for (auto it : compatible_config_versions) {
311  err << it << " ";
312  }
313  err << "\nPlease consider updating your config or using a compatible SMASH"
314  " version.";
315  throw std::runtime_error(err.str());
316  }
317 }
318 
319 } // unnamed namespace
320 
321 } // namespace smash
322 
333 int main(int argc, char *argv[]) {
334  using namespace smash; // NOLINT(build/namespaces)
335 
336  const auto &log = logger<LogArea::Main>();
337 
338  constexpr option longopts[] = {
339  {"config", required_argument, 0, 'c'},
340  {"decaymodes", required_argument, 0, 'd'},
341  {"endtime", required_argument, 0, 'e'},
342  {"force", no_argument, 0, 'f'},
343  {"help", no_argument, 0, 'h'},
344  {"inputfile", required_argument, 0, 'i'},
345  {"modus", required_argument, 0, 'm'},
346  {"particles", required_argument, 0, 'p'},
347  {"output", required_argument, 0, 'o'},
348  {"list-2-to-n", no_argument, 0, 'l'},
349  {"resonance", required_argument, 0, 'r'},
350  {"cross-sections", required_argument, 0, 's'},
351  {"cross-sections-fs", required_argument, 0, 'S'},
352  {"version", no_argument, 0, 'v'},
353  {nullptr, 0, 0, 0}};
354 
355  // strip any path to progname
356  const std::string progname = bf::path(argv[0]).filename().native();
357 
358  try {
359  bool force_overwrite = false;
360  bf::path output_path = default_output_path(), input_path("./config.yaml");
361  std::vector<std::string> extra_config;
362  char *particles = nullptr, *decaymodes = nullptr, *modus = nullptr,
363  *end_time = nullptr, *pdg_string = nullptr, *cs_string = nullptr;
364  bool list2n_activated = false;
365  bool resonance_dump_activated = false;
366  bool cross_section_dump_activated = false;
367  bool final_state_cross_sections = false;
368 
369  // parse command-line arguments
370  int opt;
371  bool suppress_disclaimer = false;
372  while ((opt = getopt_long(argc, argv, "c:d:e:fhi:m:p:o:lr:s:S:v", longopts,
373  nullptr)) != -1) {
374  switch (opt) {
375  case 'c':
376  extra_config.emplace_back(optarg);
377  break;
378  case 'd':
379  decaymodes = optarg;
380  break;
381  case 'f':
382  force_overwrite = true;
383  break;
384  case 'i':
385  input_path = optarg;
386  break;
387  case 'h':
388  usage(EXIT_SUCCESS, progname);
389  break;
390  case 'm':
391  modus = optarg;
392  break;
393  case 'p':
394  particles = optarg;
395  break;
396  case 'e':
397  end_time = optarg;
398  break;
399  case 'o':
400  output_path = optarg;
401  break;
402  case 'l':
403  list2n_activated = true;
404  suppress_disclaimer = true;
405  break;
406  case 'r':
407  resonance_dump_activated = true;
408  pdg_string = optarg;
409  suppress_disclaimer = true;
410  break;
411  case 'S':
412  final_state_cross_sections = true;
413  // fallthrough
414  case 's':
415  cross_section_dump_activated = true;
416  cs_string = optarg;
417  suppress_disclaimer = true;
418  break;
419  case 'v':
420  std::printf(
421  "%s\n"
422  "Branch : %s\nSystem : %s\nCompiler : %s %s\n"
423  "Build : %s\nDate : %s\n",
424  VERSION_MAJOR, GIT_BRANCH, CMAKE_SYSTEM, CMAKE_CXX_COMPILER_ID,
425  CMAKE_CXX_COMPILER_VERSION, CMAKE_BUILD_TYPE, BUILD_DATE);
426  std::exit(EXIT_SUCCESS);
427  default:
428  usage(EXIT_FAILURE, progname);
429  }
430  }
431 
432  // Abort if there are unhandled arguments left.
433  if (optind < argc) {
434  std::cout << argv[0] << ": invalid argument -- '" << argv[optind]
435  << "'\n";
436  usage(EXIT_FAILURE, progname);
437  }
438 
439  if (!suppress_disclaimer) {
441  }
442 
444 
445  // Read in config file
446  Configuration configuration(input_path.parent_path(),
447  input_path.filename());
448  for (const auto &config : extra_config) {
449  configuration.merge_yaml(config);
450  }
451 
452  // check if version matches before doing anything else
454 
455  if (particles) {
456  if (!bf::exists(particles)) {
457  std::stringstream err;
458  err << "The particles file was expected at '" << particles
459  << "', but the file does not exist.";
460  throw std::runtime_error(err.str());
461  }
462  std::string particle_string = read_all(bf::ifstream{particles});
463  if (has_crlf_line_ending(particle_string)) {
464  std::stringstream err;
465  err << "The particles file has CR LF line endings. Please use LF"
466  " line endings.";
467  throw std::runtime_error(err.str());
468  }
469  configuration["particles"] = particle_string;
470  }
471  if (decaymodes) {
472  if (!bf::exists(decaymodes)) {
473  std::stringstream err;
474  err << "The decay modes file was expected at '" << decaymodes
475  << "', but the file does not exist.";
476  throw std::runtime_error(err.str());
477  }
478  std::string decay_string = read_all(bf::ifstream{decaymodes});
479  if (has_crlf_line_ending(decay_string)) {
480  std::stringstream err;
481  err << "The decay mode file has CR LF line endings. Please use LF"
482  " line endings.";
483  throw std::runtime_error(err.str());
484  }
485  configuration["decaymodes"] = decay_string;
486  }
487  if (list2n_activated) {
488  /* Print only 2->n, n > 1. Do not dump decays, which can be found in
489  * decaymodes.txt anyway */
490  configuration.merge_yaml("{Collision_Term: {Two_to_One: False}}");
494  auto scat_finder = actions_finder_for_dump(configuration);
495  scat_finder.dump_reactions();
496  std::exit(EXIT_SUCCESS);
497  }
498  if (resonance_dump_activated) {
502  PdgCode pdg(pdg_string);
503  const ParticleType &res = ParticleType::find(pdg);
505  std::exit(EXIT_SUCCESS);
506  }
507  if (cross_section_dump_activated) {
511  std::string arg_string(cs_string);
512  std::vector<std::string> args = split(arg_string, ',');
513  const unsigned int n_arg = args.size();
514  if (n_arg < 2 || n_arg > 4) {
515  throw std::invalid_argument("-s usage: pdg1,pdg2[,m1][,m2]");
516  }
517  PdgCode pdg_a(args[0]), pdg_b(args[1]);
518  const ParticleType &a = ParticleType::find(pdg_a);
519  const ParticleType &b = ParticleType::find(pdg_b);
520  for (unsigned int i = 0; i < 4 - n_arg; i++) {
521  args.push_back("");
522  }
523  double ma = (args[2] == "") ? a.mass() : std::stod(args[2]);
524  double mb = (args[3] == "") ? b.mass() : std::stod(args[3]);
525  if (a.is_stable() && args[2] != "") {
526  ma = a.mass();
527  std::cout << "Warning: pole mass is used for stable particle "
528  << a.name() << " instead of " << args[2] << std::endl;
529  }
530  if (b.is_stable() && args[3] != "") {
531  mb = b.mass();
532  std::cout << "Warning: pole mass is used for stable particle "
533  << b.name() << " instead of " << args[3] << std::endl;
534  }
535  auto scat_finder = actions_finder_for_dump(configuration);
536  scat_finder.dump_cross_sections(a, b, ma, mb, final_state_cross_sections);
537  std::exit(EXIT_SUCCESS);
538  }
539  if (modus) {
540  configuration["General"]["Modus"] = std::string(modus);
541  }
542  if (end_time) {
543  configuration["General"]["End_Time"] = std::abs(std::atof(end_time));
544  }
545 
546  // Set up logging
548  configuration.take({"Logging", "default"}, einhard::ALL));
549  create_all_loggers(configuration["Logging"]);
550 
551  int64_t seed = configuration.read({"General", "Randomseed"});
552  if (seed < 0) {
553  // Seed with a truly random 63-bit value, if possible
554  std::random_device rd;
555  static_assert(std::is_same<decltype(rd()), uint32_t>::value,
556  "random_device is assumed to generate uint32_t");
557  uint64_t unsigned_seed =
558  (static_cast<uint64_t>(rd()) << 32) | static_cast<uint64_t>(rd());
559  // Discard the highest bit to make sure it fits into a positive int64_t
560  seed = static_cast<int64_t>(unsigned_seed >> 1);
561  configuration["General"]["Randomseed"] = seed;
562  }
563 
564  // Check output path
565  ensure_path_is_valid(output_path);
566  const bf::path lock_path = output_path / "smash.lock";
567  FileLock lock(lock_path);
568  if (!lock.acquire()) {
569  throw std::runtime_error(
570  "Another instance of SMASH is already writing to the specified "
571  "output directory. If you are sure this is not the case, remove \"" +
572  lock_path.native() + "\".");
573  }
574  log.debug("output path: ", output_path);
575  if (!force_overwrite && bf::exists(output_path / "config.yaml")) {
576  throw std::runtime_error(
577  "Output directory would get overwritten. Select a different output "
578  "directory, clean up, or tell SMASH to ignore existing files.");
579  }
580 
581  /* Keep a copy of the configuration that was used in the output directory
582  * also save information about SMASH build as a comment */
583  bf::ofstream(output_path / "config.yaml")
584  << "# " << VERSION_MAJOR << '\n'
585  << "# Branch : " << GIT_BRANCH << '\n'
586  << "# System : " << CMAKE_SYSTEM << '\n'
587  << "# Compiler : " << CMAKE_CXX_COMPILER_ID << ' '
588  << CMAKE_CXX_COMPILER_VERSION << '\n'
589  << "# Build : " << CMAKE_BUILD_TYPE << '\n'
590  << "# Date : " << BUILD_DATE << '\n'
591  << configuration.to_string() << '\n';
592 
593  log.trace(source_location, " create ParticleType and DecayModes");
597 
598  // Create an experiment
599  log.trace(source_location, " create Experiment");
600  auto experiment = ExperimentBase::create(configuration, output_path);
601  //
602  // version value is not used in experiment. Get rid of it to prevent
603  // warning.
604  configuration.take({"Version"});
605  const std::string report = configuration.unused_values_report();
606 
607  if (report != "{}") {
608  throw std::runtime_error(
609  "The following configuration values were not used:\n" + report);
610  }
611 
612  // Run the experiment
613  log.trace(source_location, " run the Experiment");
614  experiment->run();
615  } catch (std::exception &e) {
616  log.fatal() << "SMASH failed with the following error:\n" << e.what();
617  return EXIT_FAILURE;
618  }
619 
620  log.trace() << source_location << " about to return from main";
621  return 0;
622 }
Value read(std::initializer_list< const char *> keys) const
Additional interface for SMASH to read configuration values without removing them.
int main(int argc, char *argv[])
Main program Smashes Many Accelerated Strongly-Interacting Hadrons :-)
Definition: smash.cc:333
std::string unused_values_report() const
Returns a string listing the key/value pairs that have not been taken yet.
Configuration configuration(std::string overrides={})
Return a configuration object filled with data from src/config.yaml.
Definition: setup.h:161
double mass() const
Definition: particletype.h:134
Exception class that is thrown, if the requested output directory already exists and -f was not speci...
Definition: smash.cc:223
bool acquire()
Try to acquire the file lock.
Definition: filelock.cc:20
static std::unique_ptr< ExperimentBase > create(Configuration config, const bf::path &output_path)
Factory method that creates and initializes a new Experiment<Modus>.
ScatterActionsFinder actions_finder_for_dump(Configuration configuration)
Prepares ActionsFinder for cross-section and reaction dumps.
Definition: smash.cc:283
static void check_consistency()
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:298
static void load_decaymodes(const std::string &input)
Loads the DecayModes map as described in the input string.
Definition: decaymodes.cc:163
void dump_width_and_spectral_function() const
Prints out width and spectral function versus mass to the standard output.
void ensure_path_is_valid(const bf::path &path)
Ensures the output path is valid.
Definition: smash.cc:261
std::vector< std::string > split(const std::string &s, char delim)
Split string by delimiter.
Interface to the SMASH configuration files.
ExperimentParameters create_experiment_parameters(Configuration config)
Gathers all general Experiment parameters.
Definition: experiment.cc:269
void setup_default_float_traps()
Setup the floating-point traps used throughout SMASH.
static const ParticleType & find(PdgCode pdgcode)
Returns the ParticleType object for the given pdgcode.
void merge_yaml(const std::string &yaml)
Merge the configuration in yaml into the existing tree.
void create_all_loggers(Configuration config)
Called from main() right after the Configuration object is fully set up to create all logger objects ...
Definition: logging.cc:116
bool is_stable() const
Definition: particletype.h:226
#define source_location
Hackery that is required to output the location in the source code where the log statement occurs...
Definition: logging.h:246
Guard to create a file lock.
Definition: filelock.h:30
Particle type contains the static properties of a particle species.
Definition: particletype.h:87
A simple scatter finder: Just loops through all particles and checks each pair for a collision...
void print_disclaimer()
Print the disclaimer.
Definition: smash.cc:156
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
Definition: pdgcode.h:108
static void create_type_list(const std::string &particles)
Initialize the global ParticleType list (list_all) from the given input data.
bool has_crlf_line_ending(const std::string in)
Check if a line in the string ends with \r\n.
constexpr int p
Proton.
void set_default_loglevel(einhard::LogLevel level)
Set the default log level (what will be returned from subsequent default_loglevel calls)...
Definition: logging.cc:24
Exception class that is thrown, if no new output path can be generated (there is a directory name for...
Definition: smash.cc:232
Value take(std::initializer_list< const char *> keys)
The default interface for SMASH to read configuration values.
Log all message.
Definition: einhard.hpp:106
std::unique_ptr< ExperimentBase > experiment(const Configuration &c=configuration())
Create an experiment.
Definition: setup.h:175
Helper structure for Experiment.
std::string to_string() const
Returns a YAML string of the current tree.
Definition: action.h:24
const std::string & name() const
Definition: particletype.h:131
std::string read_all(std::istream &&input)
Utility function to read a complete input stream (e.g.