Version: SMASH-3.4
binaryoutput.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2020,2022-2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/binaryoutput.h"
11 
12 #include <cstdint>
13 #include <filesystem>
14 #include <string>
15 
16 #include "smash/action.h"
17 #include "smash/clock.h"
18 #include "smash/config.h"
19 
20 namespace smash {
21 
22 static auto get_list_of_binary_quantities(const std::string &content,
23  const std::string &format,
24  const OutputParameters &parameters);
25 
26 static auto get_binary_filename(const std::string &content,
27  const std::vector<std::string> &quantities) {
28  std::string filename = content;
29  if (content == "Particles" || content == "Collisions") {
30  std::transform(filename.begin(), filename.end(), filename.begin(),
31  [](unsigned char c) { return std::tolower(c); });
32  } else if (content == "Photons" || content == "Dileptons") {
33  // Nothing to be done here
34  } else if (content == "Initial_Conditions") {
35  filename = "SMASH_IC";
36  } else {
37  throw std::invalid_argument(
38  "Unknown content to get the binary output filename.");
39  }
40  if (quantities == OutputDefaultQuantities::oscar2013) {
41  filename += "_oscar2013";
42  } else if (quantities == OutputDefaultQuantities::oscar2013extended) {
43  filename += "_oscar2013_extended";
44  } else {
45  filename += "_custom";
46  }
47  return filename + ".bin";
48 }
49 
50 /*!\Userguide
51  * \page doxypage_output_binary
52  * SMASH supports a binary output version similar to the OSCAR 2013 standard.
53  * It is faster to read and write and theoretically needs less disk space.
54  * However, currently in ASCII OSCAR 2013 only 5 digits after the comma are
55  * written for any real number, while the binary saves the whole double
56  * (16 digits). By accident, this makes the sizes of the binary output files
57  * approximately the same as the OSCAR ASCII files.
58  * **The binary format follows the general block structure of the OSCAR
59  * format:** \ref doxypage_output_oscar. However, for the binary format,
60  * the data type specification is stricter. The types used for the output are 4
61  * bytes signed integers, 8 bytes doubles and 1 byte chars.
62  *
63  * As for OSCAR ASCII output there are two kinds of binary output:
64  * particles and collisions.
65  * The specifics for both particles and collisions output are the following:\n
66  * **Header**
67  * \code
68  * 4*char uint16_t uint16_t uint32_t len*char
69  * magic_number, format_version, format_variant, len, smash_version
70  * \endcode
71  * \li magic_number - 4 bytes that in ASCII read as "SMSH".
72  * \li Format version is an integer number, currently it is 10.
73  * \li Format variant is an integer number:
74  * \li 0 for quantities corresponding to OSCAR 2013 format;
75  * \li 1 for quantities corresponding to OSCAR 2013 extended format;
76  * \li 2 for custom list of quantities.
77  * \li len is the length of smash version string
78  * \li smash_version is len chars that give information about the SMASH version.
79  *
80  * **Output block header**\n
81  * At start of event, end of event or any other particle output:
82  * \code
83  * char int32_t int32_t uint32_t
84  * 'p' event_number ensemble_number n_part_lines
85  * \endcode
86  * \li \key event_number: Number of the event, starting with 0.
87  * \li \key ensemble_number: Number of the ensemble, starting with 0.
88  * \li \c n_part_lines is the number of particle lines in the block that follows
89  *
90  * At interaction:
91  * \code
92  * char uint32_t uint32_t double double double uint32_t
93  * 'i' nin nout density xsection partial_xsection process_type
94  * \endcode
95  * \li \c nin, \c nout are numbers of incoming and outgoing particles
96  *
97  * Block header is followed by \c nin + \c nout particle lines.
98  *
99  * **Particle line**
100  * \code
101  * 9*double int32_t int32_t int32_t
102  * t x y z mass p0 px py pz pdg ID charge
103  * \endcode
104  *
105  * **Extended Particle line**
106  * <div class="fragment">
107  * <div class="line">
108  * 9*double int32_t int32_t int32_t int32_t double
109  * double int32_t int32_t
110  * double int32_t int32_t int32_t int32_t
111  * </div>
112  * <div class="line">
113  * t x y z mass p0 px py pz pdg ID charge ncoll form_time xsecfac
114  * proc_id_origin proc_type_origin time_last_coll pdg_mother1 pdg_mother2
115  * baryon_number strangeness
116  * </div></div>
117  * \li \key proc_id_origin, \key proc_type_orgin record the id and type of
118  * the last reaction that the particle has experienced.
119  * \li \key time_last_coll records the time of the particle's last interaction
120  * (except wall crossing), from which we can calculate the position of this
121  * particle at kinetic freeze-out.
122  * \li \key pdg_mother1, \key pdg_mother2 record the pdg numbers of the
123  * incoming particles of the reaction where this particle is produced. If the
124  * particle is produced in a resonance decay, then pdg_mother2 is set equal
125  * to 0. If it is produced in a thermal bubble, then both the pdg_mother1 and
126  * pdg_mother2 are set equal to zero. Both the pdg numbers are not affected
127  * by elastic scatterings.
128  * \li \key baryon_number: Baryon number of the particle. 1 for baryons, -1 for
129  * anti-baryons and 0 for mesons.
130  *
131  * **Custom Particle line**
132  *
133  * Similar to the \ref doxypage_output_ascii "ASCII format", the binary format
134  * also supports custom quantities for particle lines. An example of particle
135  * quantities is shown below:
136  * \verbatim
137  Output:
138  Particles:
139  Format: ["Binary"]
140  Quantities: ["p0", "pz", "pdg", "charge"]
141  \endverbatim
142  * Here, the particle data will be serialized in the same order as they appear
143  * in the Quantities list. The \ref doxypage_output_ascii "ASCII format table"
144  * contains the types of the quantities written in the file and be able to
145  * correctly read the output e.g. in an analysis software.
146  *
147  * \attention If a custom binary format is used, there is no way to know which
148  * quantities were stored from the output file. It is the user's responsibility
149  * to keep track of this information in their projects.
150  *
151  * **Event end line**
152  *
153  * \code
154  * char int32_t int32_t double char
155  * 'f' event_number ensemble_number impact_parameter empty
156  * \endcode
157  * Where
158  * \li \key event_number: Number of the event, starting with 0.
159  * \li \key ensemble_number: Number of the ensemble, starting with 0.
160  * \li \key impact_parameter: Impact parameter [fm] of the collision in case of
161  * a collider setup, 0.0 otherwise.
162  * \li \key empty: 0 if there was an interaction between the projectile
163  * and the target, 1 otherwise. For non-collider setups, this is always 0.
164  *
165  * <h2> %Particles output </h2>
166  *
167  * The name of particles output file depends on its content:
168  * \li \c particles_custom.bin &rarr; this is the default;
169  * \li \c particles_oscar2013.bin &rarr;
170  * if the list of quantities corresponds to the OSCAR2013 format;
171  * \li \c particles_oscar2013_extended.bin &rarr;
172  * if the list of quantities corresponds to the extended OSCAR2013 format.
173  *
174  * The output file contains the current particle list at specific moments of
175  * time. Every moment of time is written as a \c 'p' block. For options of this
176  * output see the corresponding \ref input_output_content_specific_
177  * "content-specific output options".
178  *
179  * <h2> Collisions output </h2>
180  *
181  * The name of collisions output file depends on its content:
182  * \li \c collisions_custom.bin &rarr; this is the default;
183  * \li \c collisions_oscar2013.bin &rarr;
184  * if the list of quantities corresponds to the OSCAR2013 format;
185  * \li \c collisions_oscar2013_extended.bin &rarr;
186  * if the list of quantities corresponds to the extended OSCAR2013 format.
187  *
188  * It contains interactions (collisions, decays, box wall crossings) and
189  * optionally the initial and final configuration. The interactions are written
190  * in computational frame time-ordered fashion, in \c 'i' blocks, which contains
191  * the information of the incoming and the outgoing particles of each reaction
192  * written in the 'incoming' and 'outgoing' blocks respectively.
193  * Initial and final states are written as \c 'p' blocks. The process IDs
194  * indicating the types of the reaction, such as resonance decay,
195  * elastic scattering, soft string process, hard string process, etc.,
196  * are written in the 'process_type' blocks. For options of this output see the
197  * \ref input_output_content_specific_ "content-specific output options".
198  *
199  * See also \ref doxypage_output_collisions_box_modus.
200  */
201 
202 BinaryOutputBase::BinaryOutputBase(const std::filesystem::path &path,
203  const std::string &mode,
204  const std::string &name,
205  const std::vector<std::string> &quantities)
206  : OutputInterface(name), file_{path, mode}, formatter_(quantities) {
207  if (quantities.empty()) {
208  throw std::invalid_argument(
209  "Empty quantities list passed to 'BinaryOutputBase' constructor.");
210  }
211  std::fwrite("SMSH", 4, 1, file_.get()); // magic number
212  write(format_version_); // file format version number
213  std::uint16_t format_variant{};
214  if (quantities == OutputDefaultQuantities::oscar2013) {
215  format_variant = 0;
216  } else if (quantities == OutputDefaultQuantities::oscar2013extended) {
217  format_variant = 1;
218  } else {
219  format_variant = format_custom_;
220  }
221  write(format_variant);
222  write(SMASH_VERSION);
223 }
224 
225 // write functions:
226 void BinaryOutputBase::write(const char c) {
227  std::fwrite(&c, sizeof(char), 1, file_.get());
228 }
230  std::fwrite(chunk.data(), sizeof(char), chunk.size(), file_.get());
231 }
232 
233 void BinaryOutputBase::write(const std::string &s) {
234  const auto size = smash::numeric_cast<uint32_t>(s.size());
235  std::fwrite(&size, sizeof(std::uint32_t), 1, file_.get());
236  std::fwrite(s.c_str(), s.size(), 1, file_.get());
237 }
238 
239 void BinaryOutputBase::write(const double x) {
240  std::fwrite(&x, sizeof(x), 1, file_.get());
241 }
242 
244  std::fwrite(v.begin(), sizeof(*v.begin()), 4, file_.get());
245 }
246 
247 void BinaryOutputBase::write(const Particles &particles) {
248  write_in_chunk<ToBinary>(
249  particles, formatter_,
250  [this](const ToBinary::type &buf) { this->write(buf); });
251 }
252 
253 void BinaryOutputBase::write(const ParticleList &particles) {
254  write_in_chunk<ToBinary>(
255  particles, formatter_,
256  [this](const ToBinary::type &buf) { this->write(buf); });
257 }
258 
260  write(formatter_.single_particle_data(p));
261 }
262 
264  const std::filesystem::path &path, std::string name,
265  const OutputParameters &out_par, const std::vector<std::string> &quantities)
266  : BinaryOutputBase(path / get_binary_filename(name, quantities), "wb", name,
267  quantities),
268  print_start_end_(out_par.coll_printstartend) {}
269 
271  const EventLabel &event_label,
272  const EventInfo &) {
273  const char pchar = 'p';
274  if (print_start_end_) {
275  std::fwrite(&pchar, sizeof(char), 1, file_.get());
276  write(event_label.event_number);
277  write(event_label.ensemble_number);
278  write(particles.size());
279  write(particles);
280  }
281 }
282 
284  const EventLabel &event_label,
285  const EventInfo &event) {
286  const char pchar = 'p';
287  if (print_start_end_) {
288  std::fwrite(&pchar, sizeof(char), 1, file_.get());
289  write(event_label.event_number);
290  write(event_label.ensemble_number);
291  write(particles.size());
292  write(particles);
293  }
294 
295  // Event end line
296  const char fchar = 'f';
297  std::fwrite(&fchar, sizeof(char), 1, file_.get());
298  write(event_label.event_number);
299  write(event_label.ensemble_number);
300  write(event.impact_parameter);
301  const char empty = event.empty_event;
302  write(empty);
303 
304  // Flush to disk
305  std::fflush(file_.get());
306 }
307 
309  const double density) {
310  const char ichar = 'i';
311  std::fwrite(&ichar, sizeof(char), 1, file_.get());
312  write(action.incoming_particles().size());
313  write(action.outgoing_particles().size());
314  std::fwrite(&density, sizeof(double), 1, file_.get());
315  const double weight = action.get_total_weight();
316  std::fwrite(&weight, sizeof(double), 1, file_.get());
317  const double partial_weight = action.get_partial_weight();
318  std::fwrite(&partial_weight, sizeof(double), 1, file_.get());
319  const auto type = static_cast<uint32_t>(action.get_type());
320  std::fwrite(&type, sizeof(uint32_t), 1, file_.get());
321  write(action.incoming_particles());
322  write(action.outgoing_particles());
323 }
324 
326  const std::filesystem::path &path, std::string name,
327  const OutputParameters &out_par, const std::vector<std::string> &quantities)
328  : BinaryOutputBase(path / get_binary_filename(name, quantities), "wb", name,
329  quantities),
330  only_final_(out_par.part_only_final) {}
331 
333  const EventLabel &event_label,
334  const EventInfo &) {
335  const char pchar = 'p';
337  std::fwrite(&pchar, sizeof(char), 1, file_.get());
338  write(event_label.event_number);
339  write(event_label.ensemble_number);
340  write(particles.size());
341  write(particles);
342  }
343 }
344 
346  const EventLabel &event_label,
347  const EventInfo &event) {
348  const char pchar = 'p';
350  std::fwrite(&pchar, sizeof(char), 1, file_.get());
351  write(event_label.event_number);
352  write(event_label.ensemble_number);
353  write(particles.size());
354  write(particles);
355  }
356 
357  // Event end line
358  const char fchar = 'f';
359  std::fwrite(&fchar, sizeof(char), 1, file_.get());
360  write(event_label.event_number);
361  write(event_label.ensemble_number);
362  write(event.impact_parameter);
363  const char empty = event.empty_event;
364  write(empty);
365 
366  // Flush to disk
367  std::fflush(file_.get());
368 }
369 
371  const std::unique_ptr<Clock> &,
372  const DensityParameters &,
373  const EventLabel &event_label,
374  const EventInfo &) {
375  const char pchar = 'p';
377  std::fwrite(&pchar, sizeof(char), 1, file_.get());
378  write(event_label.event_number);
379  write(event_label.ensemble_number);
380  write(particles.size());
381  write(particles);
382  }
383 }
384 
386  const std::filesystem::path &path, std::string name,
387  const std::vector<std::string> &quantities)
388  : BinaryOutputBase(path / get_binary_filename(name, quantities), "wb", name,
389  quantities) {}
390 
392  const EventLabel &,
393  const EventInfo &) {}
394 
396  [[maybe_unused]] const Particles &particles, const EventLabel &event_label,
397  const EventInfo &event) {
398  // Event end line
399  const char fchar = 'f';
400  std::fwrite(&fchar, sizeof(char), 1, file_.get());
401  write(event_label.event_number);
402  write(event_label.ensemble_number);
403  write(event.impact_parameter);
404  const char empty = event.empty_event;
405  write(empty);
406 
407  // Flush to disk
408  std::fflush(file_.get());
409 }
410 
412  const double) {
413  if (action.get_type() == ProcessType::Fluidization ||
415  const char pchar = 'p';
416  std::fwrite(&pchar, sizeof(char), 1, file_.get());
417  write(action.incoming_particles().size());
418  write(action.incoming_particles());
419  }
420 }
421 
422 static auto get_list_of_binary_quantities(const std::string &content,
423  const std::string &format,
424  const OutputParameters &parameters) {
425  const bool is_extended = std::invoke([&content, &parameters]() {
426  if (content == "Particles")
427  return parameters.part_extended;
428  else if (content == "Collisions")
429  return parameters.coll_extended;
430  else if (content == "Dileptons")
431  return parameters.dil_extended;
432  else if (content == "Photons")
433  return parameters.photons_extended;
434  else if (content == "Initial_Conditions")
435  return parameters.ic_extended;
436  else
437  return false;
438  });
439  const auto default_quantities =
442  if (format == "Oscar2013_bin") {
443  return default_quantities;
444  } else if (format == "Binary") {
445  if (content == "Particles" || content == "Collisions" ||
446  content == "Dileptons" || content == "Photons" ||
447  content == "Initial_Conditions") {
448  auto list_of_quantities = parameters.quantities.at(content);
449  if (list_of_quantities.empty()) {
450  return default_quantities;
451  } else {
452  return list_of_quantities;
453  }
454  } else {
455  /* Note that this function should not be called with "Binary" format for
456  * output contents which do not support custom binary quantities. Hence we
457  * throw here to prevent such a case.*/
458  throw std::invalid_argument(
459  "Unknown content to get the list of quantities for binary output.");
460  }
461  } else {
462  throw std::invalid_argument(
463  "Unknown format to get the list of quantities for binary output.");
464  }
465 }
466 
467 std::unique_ptr<OutputInterface> create_binary_output(
468  const std::string &format, const std::string &content,
469  const std::filesystem::path &path, const OutputParameters &out_par) {
470  const auto quantities =
471  get_list_of_binary_quantities(content, format, out_par);
472  if (content == "Particles") {
473  return std::make_unique<BinaryOutputParticles>(path, content, out_par,
474  quantities);
475  } else if (content == "Collisions" || content == "Dileptons" ||
476  content == "Photons") {
477  return std::make_unique<BinaryOutputCollisions>(path, content, out_par,
478  quantities);
479  } else if (content == "Initial_Conditions") {
480  return std::make_unique<BinaryOutputInitialConditions>(path, content,
481  quantities);
482  } else {
483  throw std::invalid_argument("Binary output not available for '" + content +
484  "' content.");
485  }
486 }
487 
488 } // namespace smash
Action is the base class for a generic process that takes a number of incoming particles and transfor...
Definition: action.h:35
virtual ProcessType get_type() const
Get the process type.
Definition: action.h:131
virtual double get_total_weight() const =0
Return the total weight value, which is mainly used for the weight output entry.
const ParticleList & incoming_particles() const
Get the list of particles that go into the action.
Definition: action.cc:61
virtual double get_partial_weight() const =0
Return the specific weight for the chosen outgoing channel, which is mainly used for the partial weig...
const ParticleList & outgoing_particles() const
Get the list of particles that resulted from the action.
Definition: action.h:247
Base class for SMASH binary output.
Definition: binaryoutput.h:30
void write(const ToBinary::type &chunk)
Write several bytes to the binary output.
void write_particledata(const ParticleData &p)
Write particle data to binary output.
const uint16_t format_version_
Binary file format version number.
Definition: binaryoutput.h:131
const uint16_t format_custom_
Format variant number associated to the custom quantities case.
Definition: binaryoutput.h:133
OutputFormatter< ToBinary > formatter_
The output formatter.
Definition: binaryoutput.h:135
RenamingFilePtr file_
Binary particles output file path.
Definition: binaryoutput.h:127
BinaryOutputBase(const std::filesystem::path &path, const std::string &mode, const std::string &name, const std::vector< std::string > &quantities)
Create binary output base.
void at_interaction(const Action &action, const double density) override
Writes an interaction block, including information about the incoming and outgoing particles,...
void at_eventstart(const Particles &particles, const EventLabel &event_label, const EventInfo &event) override
Writes the initial particle information list of an event to the binary output.
void at_eventend(const Particles &particles, const EventLabel &event_label, const EventInfo &event) override
Writes the final particle information list of an event to the binary output.
BinaryOutputCollisions(const std::filesystem::path &path, std::string name, const OutputParameters &out_par, const std::vector< std::string > &quantities)
Create binary particle output.
bool print_start_end_
Write initial and final particles additonally to collisions?
Definition: binaryoutput.h:194
void at_eventend(const Particles &particles, const EventLabel &event_label, const EventInfo &event) override
Writes the final particle information of an event to the binary output.
BinaryOutputInitialConditions(const std::filesystem::path &path, std::string name, const std::vector< std::string > &quantities)
Create binary initial conditions particle output.
void at_interaction(const Action &action, const double) override
Writes particles that are removed when crossing the hypersurface to the output.
void at_eventstart(const Particles &, const EventLabel &, const EventInfo &) override
Writes the initial particle information of an event to the binary output.
void at_eventstart(const Particles &particles, const EventLabel &event_label, const EventInfo &event) override
Writes the initial particle information of an event to the binary output.
void at_intermediate_time(const Particles &particles, const std::unique_ptr< Clock > &clock, const DensityParameters &dens_param, const EventLabel &event_label, const EventInfo &event) override
Writes particles at each time interval; fixed by option OUTPUT_INTERVAL.
OutputOnlyFinal only_final_
Whether final- or initial-state particles should be written.
Definition: binaryoutput.h:260
BinaryOutputParticles(const std::filesystem::path &path, std::string name, const OutputParameters &out_par, const std::vector< std::string > &quantities)
Create binary particle output.
void at_eventend(const Particles &particles, const EventLabel &event_label, const EventInfo &event) override
Writes the final particle information of an event to the binary output.
A class to pre-calculate and store parameters relevant for density calculation.
Definition: density.h:92
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
iterator begin()
Definition: fourvector.h:291
Abstraction of generic output.
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:59
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
size_t size() const
Definition: particles.h:87
FILE * get()
Get the underlying FILE* pointer.
Definition: file.cc:27
std::vector< char > type
Return type of this converter.
@ IfNotEmpty
Print only final-state particles, and those only if the event is not empty.
@ No
Print initial, intermediate and final-state particles.
FormattingHelper< T > format(const T &value, const char *unit, int width=-1, int precision=-1)
Acts as a stream modifier for std::ostream to output an object with an optional suffix string and wit...
Definition: logging.h:217
std::unique_ptr< OutputInterface > create_binary_output(const std::string &format, const std::string &content, const std::filesystem::path &path, const OutputParameters &out_par)
Create a binary output object.
constexpr int p
Proton.
Definition: action.h:24
@ FluidizationNoRemoval
See here for a short description.
@ Fluidization
See here for a short description.
static auto get_list_of_binary_quantities(const std::string &content, const std::string &format, const OutputParameters &parameters)
static auto get_binary_filename(const std::string &content, const std::vector< std::string > &quantities)
Definition: binaryoutput.cc:26
Structure to contain custom data for output.
bool empty_event
True if no collisions happened.
double impact_parameter
Impact parameter for collider modus, otherwise dummy.
Structure to contain information about the event and ensemble numbers.
int32_t ensemble_number
The number of the ensemble.
int32_t event_number
The number of the event.
static const std::vector< std::string > oscar2013
Quantities output in OSCAR2013 format.
static const std::vector< std::string > oscar2013extended
Quantities output in Extended OSCAR2013 format.
Helper structure for Experiment to hold output options and parameters.
bool dil_extended
Extended format for dilepton output.
bool coll_extended
Extended format for collisions output.
bool part_extended
Extended format for particles output.
bool photons_extended
Extended format for photon output.
std::map< std::string, std::vector< std::string > > quantities
Map of quantities to be printed in the output.
bool ic_extended
Extended initial conditions output.