Version: SMASH-3.1
listmodus.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2015-2023
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/listmodus.h"
11 
12 #include <cmath>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <filesystem>
16 #include <fstream>
17 #include <list>
18 #include <map>
19 #include <sstream>
20 #include <utility>
21 #include <vector>
22 
23 #include "smash/algorithms.h"
24 #include "smash/boxmodus.h"
25 #include "smash/configuration.h"
26 #include "smash/constants.h"
28 #include "smash/fourvector.h"
29 #include "smash/inputfunctions.h"
30 #include "smash/logging.h"
31 #include "smash/particledata.h"
32 #include "smash/threevector.h"
34 
35 namespace smash {
36 static constexpr int LList = LogArea::List::id;
37 
39  const ExperimentParameters &param)
40  : file_id_{std::nullopt}, event_id_{0} {
41  /*
42  * Extract the only expected section of the configuration to make this
43  * constructor work also for children classes. These do the same but have a
44  * different section name (like for instance 'ListBox' instead of 'List')
45  */
46  const auto config_sections = modus_config.list_upmost_nodes();
47  assert(config_sections.size() == 1);
48  auto plain_config =
49  modus_config.extract_sub_configuration({config_sections[0].c_str()});
50  // Impose strict requirement on possible keys present in configuration file
51  bool file_prefix_used = plain_config.has_value({"File_Prefix"});
52  bool filename_used = plain_config.has_value({"Filename"});
53  if (file_prefix_used == filename_used) {
54  throw std::invalid_argument(
55  "Either 'Filename' or 'File_Prefix' key must be used in 'List' section "
56  "in configuration file. Please, adjust your configuration file.");
57  }
58  std::string key_to_take = "Filename";
59  if (file_prefix_used) {
60  key_to_take = "File_Prefix";
61  file_id_ = plain_config.take({"Shift_Id"}, 0);
62  }
64  plain_config.take({key_to_take.c_str()})
67  plain_config.take({"File_Directory"})
68  .convert_for(particle_list_file_directory_);
69  if (param.n_ensembles > 1) {
70  throw std::runtime_error("ListModus only makes sense with one ensemble");
71  }
72 }
73 
74 /* console output on startup of List specific parameters */
75 std::ostream &operator<<(std::ostream &out, const ListModus &m) {
76  out << "-- List Modus\nInput directory for external particle lists:\n"
77  << m.particle_list_file_directory_ << "\n";
78  return out;
79 }
80 
82  /* (1) If particles are already at the same time - don't touch them
83  AND start at the start_time_ from the config. */
84  double earliest_formation_time = DBL_MAX;
85  double formation_time_difference = 0.0;
86  double reference_formation_time = 0.0; // avoid compiler warning
87  bool first_particle = true;
88  for (const auto &particle : particles) {
89  const double t = particle.position().x0();
90  if (t < earliest_formation_time) {
91  earliest_formation_time = t;
92  }
93  if (first_particle) {
94  reference_formation_time = t;
95  first_particle = false;
96  } else {
97  formation_time_difference += std::abs(t - reference_formation_time);
98  }
99  }
100  /* (2) If particles are NOT at the same time -> anti-stream them to
101  the earliest time (Note: not to the start_time_ set by config) */
102  bool anti_streaming_needed = (formation_time_difference > really_small);
103  start_time_ = earliest_formation_time;
104  if (anti_streaming_needed) {
105  for (auto &particle : particles) {
106  /* for hydro output where formation time is different */
107  const double t = particle.position().x0();
108  const double delta_t = t - start_time_;
109  const ThreeVector r =
110  particle.position().threevec() - delta_t * particle.velocity();
111  particle.set_4position(FourVector(start_time_, r));
112  particle.set_formation_time(t);
113  particle.set_cross_section_scaling_factor(0.0);
114  }
115  }
116 }
117 
119  double t, double x, double y, double z,
120  double mass, double E, double px, double py,
121  double pz) {
122  try {
123  ParticleData new_particle =
125  pdgcode, mass, {t, x, y, z}, {E, px, py, pz}, LList,
127  particles.insert(new_particle);
129  logg[LList].warn() << "SMASH does not recognize pdg code " << pdgcode
130  << " loaded from file. This particle will be ignored.\n";
131  }
132 }
133 
134 /* initial_conditions - sets particle data for @particles */
136  const ExperimentParameters &) {
137  std::string particle_list = next_event_();
138  for (const Line &line : line_parser(particle_list)) {
139  std::istringstream lineinput(line.text);
140  double t, x, y, z, mass, E, px, py, pz;
141  int id, charge;
142  std::string pdg_string;
143  lineinput >> t >> x >> y >> z >> mass >> E >> px >> py >> pz >>
144  pdg_string >> id >> charge;
145  if (lineinput.fail()) {
146  throw LoadFailure(
147  build_error_string("While loading external particle lists data:\n"
148  "Failed to convert the input string to the "
149  "expected data types.",
150  line));
151  }
152  PdgCode pdgcode(pdg_string);
153  logg[LList].debug("Particle ", pdgcode, " (x,y,z)= (", x, ", ", y, ", ", z,
154  ")");
155 
156  // Charge consistency check
157  if (pdgcode.charge() != charge) {
158  logg[LList].error() << "Charge of pdg = " << pdgcode << " != " << charge;
159  throw std::invalid_argument("Inconsistent input (charge).");
160  }
161  try_create_particle(*particles, pdgcode, t, x, y, z, mass, E, px, py, pz);
162  }
163  if (particles->size() > 0) {
164  backpropagate_to_same_time(*particles);
165  } else {
166  start_time_ = 0.0;
167  }
168  event_id_++;
169 
170  return start_time_;
171 }
172 
173 std::filesystem::path ListModus::file_path_(std::optional<int> file_id) {
174  std::string fname = particle_list_filename_or_prefix_ +
175  ((file_id) ? std::to_string(*file_id) : "");
176 
177  const std::filesystem::path default_path =
178  std::filesystem::absolute(particle_list_file_directory_);
179 
180  const std::filesystem::path fpath = default_path / fname;
181 
182  logg[LList].debug() << "File: " << std::filesystem::absolute(fpath) << '\n';
183 
184  if (!std::filesystem::exists(fpath)) {
185  logg[LList].fatal()
186  << fpath.filename().native() << " does not exist! \n\n"
187  << "Usage of smash with external particle lists:\n"
188  << " 1. Put the external particle lists in one or more files\n"
189  << " according to the user guide instructions.\n"
190  << " 2. Particles info: t x y z mass p0 px py pz pdg ID charge\n"
191  << " in units of: fm fm fm fm GeV GeV GeV GeV GeV none none e\n";
192  throw std::runtime_error("External particle list does not exist!");
193  }
194 
195  return fpath;
196 }
197 
198 std::string ListModus::next_event_() {
199  const std::filesystem::path fpath = file_path_(file_id_);
200  std::ifstream ifs{fpath};
201  ifs.seekg(last_read_position_);
202 
203  if (!file_has_events_(fpath, last_read_position_)) {
204  if (file_id_) {
205  // Get next file and call this function recursively
206  (*file_id_)++;
208  ifs.close();
209  return next_event_();
210  } else {
211  throw std::runtime_error(
212  "Attempt to read in next event in Listmodus object but no further "
213  "data found in single provided file. Please, check your setup.");
214  }
215  }
216 
217  // read one event. events marked by line # event end i in case of Oscar
218  // output. Assume one event per file for all other output formats
219  std::string event_string;
220  const std::string needle = "end";
221  std::string line;
222  while (getline(ifs, line)) {
223  if (line.find(needle) == std::string::npos) {
224  event_string += line + "\n";
225  } else {
226  break;
227  }
228  }
229 
230  if (!ifs.eof() && (ifs.fail() || ifs.bad())) {
231  logg[LList].fatal() << "Error while reading " << fpath.filename().native();
232  throw std::runtime_error("Error while reading external particle list");
233  }
234  // save position for next event read
235  last_read_position_ = ifs.tellg();
236  ifs.close();
237 
238  return event_string;
239 }
240 
241 bool ListModus::file_has_events_(std::filesystem::path filepath,
242  std::streampos last_position) {
243  std::ifstream ifs{filepath};
244  std::string line;
245 
246  // last event read read at end of file. we know this because errors are
247  // handled in next_event
248  if (last_position == -1) {
249  return false;
250  }
251  ifs.seekg(last_position);
252  // skip over comment lines, assume that a max. of four consecutive comment
253  // lines can occur
254  int skipped_lines = 0;
255  const int max_comment_lines = 4;
256  while (std::getline(ifs, line) && line[0] != '#' &&
257  skipped_lines++ < max_comment_lines) {
258  }
259 
260  if (ifs.eof()) {
261  return false;
262  }
263 
264  if (!ifs.good()) {
265  logg[LList].fatal() << "Error while reading "
266  << filepath.filename().native();
267  throw std::runtime_error("Error while reading external particle list");
268  }
269 
270  ifs.close();
271  return true;
272 }
273 
275  const ExperimentParameters &param)
276  : ListModus(), length_(modus_config.take({"ListBox", "Length"})) {
277  /*
278  * ATTENTION: In a child class initialization list nothing can be done before
279  * calling the base constructor. However, here we cannot hand over the
280  * configuration to the base class as there are child-specific keys to be
281  * taken before. This cannot be done after having moved the configuration and
282  * changing the constructor signature would be a big change as all modus
283  * classes should have the same constructor signature to allow Experiment to
284  * template on it. Therefore, we abuse C++ here by default-initializing the
285  * parent class, then taking the child-specific key(s) and then assigning a
286  * parent instance to the child using the parent assignment operator. In
287  * general this would be risky as it would open up the possibility to leave
288  * part of the children uninitialized, but here we should have under control
289  * what exactly happens at initialization time.
290  */
291  this->ListModus::operator=(ListModus(std::move(modus_config), param));
292 }
293 
295  const OutputsList &output_list) {
296  int wraps = 0;
297  for (ParticleData &data : *particles) {
298  FourVector position = data.position();
299  bool wall_hit = enforce_periodic_boundaries(position.begin() + 1,
300  position.end(), length_);
301  if (wall_hit) {
302  const ParticleData incoming_particle(data);
303  data.set_4position(position);
304  ++wraps;
305  ActionPtr action =
306  std::make_unique<WallcrossingAction>(incoming_particle, data);
307  for (const auto &output : output_list) {
308  if (!output->is_dilepton_output() && !output->is_photon_output()) {
309  output->at_interaction(*action, 0.);
310  }
311  }
312  }
313  }
314 
315  logg[LList].debug("Moved ", wraps, " particles back into the box.");
316  return wraps;
317 }
318 
319 } // namespace smash
Generic algorithms on containers and ranges.
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.
Configuration extract_sub_configuration(std::initializer_list< const char * > keys, Configuration::GetEmpty empty_if_not_existing=Configuration::GetEmpty::No)
Create a new configuration from a then-removed section of the present object.
std::vector< std::string > list_upmost_nodes()
Lists all YAML::Nodes from the configuration setup.
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
iterator end()
Definition: fourvector.h:294
iterator begin()
Definition: fourvector.h:291
const double length_
Length of the cube's edge in fm.
Definition: listmodus.h:303
int impose_boundary_conditions(Particles *particles, const OutputsList &output_list={})
Enforces that all particles are inside the box at the beginning of an event.
Definition: listmodus.cc:294
ListBoxModus(Configuration modus_config, const ExperimentParameters &parameters)
Constructor (This is the same as for the ListModus)
Definition: listmodus.cc:274
ListModus: Provides a modus for running SMASH on an external particle list, for example as an afterbu...
Definition: listmodus.h:56
void backpropagate_to_same_time(Particles &particles)
Judge whether formation times are the same for all the particles; Don't do anti-freestreaming if all ...
Definition: listmodus.cc:81
bool file_has_events_(std::filesystem::path filepath, std::streampos last_position)
Check if the file given by filepath has events left after streampos last_position.
Definition: listmodus.cc:241
std::optional< int > file_id_
The id of the current file.
Definition: listmodus.h:202
std::string particle_list_filename_or_prefix_
Prefix of the file(s) containing the particle list.
Definition: listmodus.h:199
std::filesystem::path file_path_(std::optional< int > file_id)
Return the absolute path of the data file.
Definition: listmodus.cc:173
std::string particle_list_file_directory_
File directory of the particle list.
Definition: listmodus.h:193
double start_time_
Starting time for the List; changed to the earliest formation time.
Definition: listmodus.h:152
ListModus()=default
Construct an empty list.
void try_create_particle(Particles &particles, PdgCode pdgcode, double t, double x, double y, double z, double mass, double E, double px, double py, double pz)
Tries to add a new particle to particles and performs consistency checks: (i) The PDG code is legal a...
Definition: listmodus.cc:118
std::streampos last_read_position_
Last read position in current file.
Definition: listmodus.h:208
bool warn_about_mass_discrepancy_
Auxiliary flag to warn about mass-discrepancies only once per instance.
Definition: listmodus.h:211
bool warn_about_off_shell_particles_
Auxiliary flag to warn about off-shell particles only once per instance.
Definition: listmodus.h:213
std::string next_event_()
Read the next event.
Definition: listmodus.cc:198
double initial_conditions(Particles *particles, const ExperimentParameters &parameters)
Generates initial state of the particles in the system according to a list.
Definition: listmodus.cc:135
int event_id_
The unique id of the current event.
Definition: listmodus.h:205
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:58
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
size_t size() const
Definition: particles.h:87
const ParticleData & insert(const ParticleData &p)
Inserts the particle into the list of particles.
Definition: particles.cc:50
PdgCode stores a Particle Data Group Particle Numbering Scheme particle type number.
Definition: pdgcode.h:111
int charge() const
The charge of the particle.
Definition: pdgcode.h:567
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:31
Collection of useful constants that are known at compile time.
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.
Definition: action.h:547
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
Definition: action.h:24
static bool enforce_periodic_boundaries(Iterator begin, const Iterator &end, typename std::iterator_traits< Iterator >::value_type length)
Enforces periodic boundaries on the given collection of values.
Definition: algorithms.h:53
build_vector_< Line > line_parser(const std::string &input)
Helper function for parsing particles.txt and decaymodes.txt.
static constexpr int LList
Definition: listmodus.cc:36
ParticleData create_valid_smash_particle_matching_provided_quantities(PdgCode pdgcode, double mass, const FourVector &four_position, const FourVector &four_momentum, int log_area, bool &mass_warning, bool &on_shell_warning)
This function creates a SMASH particle validating the provided information.
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:37
std::string build_error_string(std::string message, const Line &line)
Builds a meaningful error message.
Helper structure for Experiment.
int n_ensembles
Number of parallel ensembles.
Line consists of a line number and the contents of that line.
Used when external particle list cannot be found.
Definition: listmodus.h:143