Version: SMASH-3.1
rootoutput.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2023
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/rootoutput.h"
11 
12 #include "TFile.h"
13 #include "TTree.h"
14 #include "smash/action.h"
15 #include "smash/clock.h"
17 #include "smash/particles.h"
18 
19 namespace smash {
20 static constexpr int LHyperSurfaceCrossing = LogArea::HyperSurfaceCrossing::id;
21 
22 const int RootOutput::max_buffer_size_ = 500000;
23 
232 RootOutput::RootOutput(const std::filesystem::path &path,
233  const std::string &name, const OutputParameters &out_par)
234  : OutputInterface(name),
235  filename_(path / (name + ".root")),
236  write_collisions_(name == "Collisions" || name == "Dileptons" ||
237  name == "Photons"),
238  write_particles_(name == "Particles"),
239  write_initial_conditions_(name == "SMASH_IC"),
240  particles_only_final_(out_par.part_only_final),
241  autosave_frequency_(1000),
242  part_extended_(out_par.part_extended),
243  coll_extended_(out_par.coll_extended),
244  ic_extended_(out_par.ic_extended) {
246  filename_unfinished_ += ".unfinished";
248  std::make_unique<TFile>(filename_unfinished_.native().c_str(), "NEW");
249  init_trees();
250 }
251 
254  particles_tree_ = new TTree("particles", "particles");
255 
256  particles_tree_->Branch("ev", &ev_, "ev/I");
257  particles_tree_->Branch("tcounter", &tcounter_, "tcounter/I");
258  particles_tree_->Branch("npart", &npart_, "npart/I");
259  particles_tree_->Branch("test_p", &test_p_, "test_p/I");
260  particles_tree_->Branch("modus_l", &modus_l_, "modus_l/D");
261  particles_tree_->Branch("current_t", &current_t_, "current_t/D");
262  particles_tree_->Branch("impact_b", &impact_b_, "impact_b/D");
263  particles_tree_->Branch("empty_event", &empty_event_, "empty_event/O");
264 
265  particles_tree_->Branch("pdgcode", &pdgcode_[0], "pdgcode[npart]/I");
266  particles_tree_->Branch("charge", &charge_[0], "charge[npart]/I");
267 
268  particles_tree_->Branch("p0", &p0_[0], "p0[npart]/D");
269  particles_tree_->Branch("px", &px_[0], "px[npart]/D");
270  particles_tree_->Branch("py", &py_[0], "py[npart]/D");
271  particles_tree_->Branch("pz", &pz_[0], "pz[npart]/D");
272 
273  particles_tree_->Branch("t", &t_[0], "t[npart]/D");
274  particles_tree_->Branch("x", &x_[0], "x[npart]/D");
275  particles_tree_->Branch("y", &y_[0], "y[npart]/D");
276  particles_tree_->Branch("z", &z_[0], "z[npart]/D");
277 
278  particles_tree_->Branch("E_kinetic_tot", &E_kinetic_tot_,
279  "E_kinetic_tot/D");
280  particles_tree_->Branch("E_fields_tot", &E_fields_tot_, "E_fields_tot/D");
281  particles_tree_->Branch("E_tot", &E_tot_, "E_tot/D");
282 
283  if (part_extended_ || ic_extended_) {
284  particles_tree_->Branch("ncoll", &coll_per_part_[0], "ncoll[npart]/I");
285  particles_tree_->Branch("form_time", &formation_time_[0],
286  "form_time[npart]/D");
287  particles_tree_->Branch("xsecfac", &xsec_factor_[0], "xsecfac[npart]/D");
288  particles_tree_->Branch("proc_id_origin", &proc_id_origin_[0],
289  "proc_id_origin[npart]/I");
290  particles_tree_->Branch("proc_type_origin", &proc_type_origin_[0],
291  "proc_type_origin[npart]/I");
292  particles_tree_->Branch("time_last_coll", &time_last_coll_[0],
293  "time_last_coll[npart]/D");
294  particles_tree_->Branch("pdg_mother1", &pdg_mother1_[0],
295  "pdg_mother1[npart]/I");
296  particles_tree_->Branch("pdg_mother2", &pdg_mother2_[0],
297  "pdg_mother2[npart]/I");
298  particles_tree_->Branch("baryon_number", &baryon_number_[0],
299  "baryon_number[npart]/I");
300  particles_tree_->Branch("strangeness", &strangeness_[0],
301  "strangeness[npart]/I");
302  }
303  }
304 
305  if (write_collisions_) {
306  collisions_tree_ = new TTree("collisions", "collisions");
307 
308  collisions_tree_->Branch("nin", &nin_, "nin/I");
309  collisions_tree_->Branch("nout", &nout_, "nout/I");
310  collisions_tree_->Branch("npart", &npart_, "npart/I");
311  collisions_tree_->Branch("ev", &ev_, "ev/I");
312  collisions_tree_->Branch("weight", &wgt_, "weight/D");
313  collisions_tree_->Branch("partial_weight", &par_wgt_, "partial_weight/D");
314 
315  collisions_tree_->Branch("pdgcode", &pdgcode_[0], "pdgcode[npart]/I");
316  collisions_tree_->Branch("charge", &charge_[0], "charge[npart]/I");
317 
318  collisions_tree_->Branch("p0", &p0_[0], "p0[npart]/D");
319  collisions_tree_->Branch("px", &px_[0], "px[npart]/D");
320  collisions_tree_->Branch("py", &py_[0], "py[npart]/D");
321  collisions_tree_->Branch("pz", &pz_[0], "pz[npart]/D");
322 
323  collisions_tree_->Branch("t", &t_[0], "t[npart]/D");
324  collisions_tree_->Branch("x", &x_[0], "x[npart]/D");
325  collisions_tree_->Branch("y", &y_[0], "y[npart]/D");
326  collisions_tree_->Branch("z", &z_[0], "z[npart]/D");
327 
328  if (coll_extended_) {
329  collisions_tree_->Branch("ncoll", &coll_per_part_[0], "ncoll[npart]/I");
330  collisions_tree_->Branch("form_time", &formation_time_[0],
331  "form_time[npart]/D");
332  collisions_tree_->Branch("xsecfac", &xsec_factor_[0], "xsecfac[npart]/D");
333  collisions_tree_->Branch("proc_id_origin", &proc_id_origin_[0],
334  "proc_id_origin[npart]/I");
335  collisions_tree_->Branch("proc_type_origin", &proc_type_origin_[0],
336  "proc_type_origin[npart]/I");
337  collisions_tree_->Branch("time_last_coll", &time_last_coll_[0],
338  "time_last_coll[npart]/D");
339  collisions_tree_->Branch("pdg_mother1", &pdg_mother1_[0],
340  "pdg_mother1[npart]/I");
341  collisions_tree_->Branch("pdg_mother2", &pdg_mother2_[0],
342  "pdg_mother2[npart]/I");
343  collisions_tree_->Branch("baryon_number", &baryon_number_[0],
344  "baryon_number[npart]/I");
345  collisions_tree_->Branch("strangeness", &strangeness_[0],
346  "strangeness[npart]/I");
347  }
348  }
349 }
350 
356  // kOverwrite option prevents from writing extra TKey objects into root file
357  root_out_file_->Write("", TObject::kOverwrite);
358  root_out_file_->Close();
359  std::filesystem::rename(filename_unfinished_, filename_);
360 }
361 
362 void RootOutput::at_eventstart(const Particles &particles,
363  const int event_number, const EventInfo &event) {
364  // save event number
365  current_event_ = event_number;
366 
367  modus_l_ = event.modus_length;
368  test_p_ = event.test_particles;
369  current_t_ = event.current_time;
370  E_kinetic_tot_ = event.total_kinetic_energy;
371  E_fields_tot_ = event.total_mean_field_energy;
372  E_tot_ = event.total_energy;
373 
375  output_counter_ = 0;
376  // This is to have only one output of positive impact parameter per event
377  impact_b_ = -1.0;
378  empty_event_ = false;
379  particles_to_tree(particles);
380  output_counter_++;
381  }
382 }
383 
385  const std::unique_ptr<Clock> &,
386  const DensityParameters &,
387  const EventInfo &event) {
388  modus_l_ = event.modus_length;
389  test_p_ = event.test_particles;
390  current_t_ = event.current_time;
391  E_kinetic_tot_ = event.total_kinetic_energy;
392  E_fields_tot_ = event.total_mean_field_energy;
393  E_tot_ = event.total_energy;
394 
396  particles_to_tree(particles);
397  output_counter_++;
398  }
399 }
400 
401 void RootOutput::at_eventend(const Particles &particles,
402  const int /*event_number*/,
403  const EventInfo &event) {
404  modus_l_ = event.modus_length;
405  test_p_ = event.test_particles;
406  current_t_ = event.current_time;
407  E_kinetic_tot_ = event.total_kinetic_energy;
408  E_fields_tot_ = event.total_mean_field_energy;
409  E_tot_ = event.total_energy;
410 
411  impact_b_ = event.impact_parameter;
412  empty_event_ = event.empty_event;
413  if (write_particles_ &&
414  !(event.empty_event &&
416  particles_to_tree(particles);
417  }
418  /* Forced regular dump from operational memory to disk. Very demanding!
419  * If program crashes written data will NOT be lost. */
422  particles_tree_->AutoSave("SaveSelf");
423  }
424  if (write_collisions_) {
425  collisions_tree_->AutoSave("SaveSelf");
426  }
427  }
428 
430  // If the runtime is too short some particles might not yet have
431  // reached the hypersurface. Warning is printed.
432  if (particles.size() != 0 && !event.impose_kinematic_cut_for_SMASH_IC) {
434  "End time might be too small for initial conditions output. "
435  "Hypersurface has not yet been crossed by ",
436  particles.size(), " particle(s).");
437  }
438  }
439 }
440 
442  const double /*density*/) {
443  if (write_collisions_) {
445  action.get_total_weight(), action.get_partial_weight());
446  }
447 
451  }
452 }
453 
454 template <typename T>
455 void RootOutput::particles_to_tree(T &particles) {
456  int i = 0;
457 
460  bool exceeded_buffer_message = true;
461 
462  for (const auto &p : particles) {
463  // Buffer full - flush to tree, else fill with particles
464  if (i >= max_buffer_size_) {
465  if (exceeded_buffer_message) {
466  logg[LOutput].warn()
467  << "\nThe number of particles N = " << particles.size()
468  << " exceeds the maximum buffer size B = " << max_buffer_size_
469  << ".\nceil(N/B) = "
470  << std::ceil(particles.size() /
471  static_cast<double>(max_buffer_size_))
472  << " separate ROOT Tree entries will be created at this output."
473  << "\nMaximum buffer size (max_buffer_size_) can be changed in "
474  << "rootoutput.h\n\n";
475  exceeded_buffer_message = false;
476  }
478  i = 0;
479  particles_tree_->Fill();
480  } else {
481  pdgcode_[i] = p.pdgcode().get_decimal();
482  charge_[i] = p.type().charge();
483 
484  p0_[i] = p.momentum().x0();
485  px_[i] = p.momentum().x1();
486  py_[i] = p.momentum().x2();
487  pz_[i] = p.momentum().x3();
488 
489  t_[i] = p.position().x0();
490  x_[i] = p.position().x1();
491  y_[i] = p.position().x2();
492  z_[i] = p.position().x3();
493 
494  if (part_extended_ || ic_extended_) {
495  const auto h = p.get_history();
496  formation_time_[i] = p.formation_time();
497  xsec_factor_[i] = p.xsec_scaling_factor();
498  time_last_coll_[i] = h.time_last_collision;
499  coll_per_part_[i] = h.collisions_per_particle;
500  proc_id_origin_[i] = h.id_process;
501  proc_type_origin_[i] = static_cast<int>(h.process_type);
502  pdg_mother1_[i] = h.p1.get_decimal();
503  pdg_mother2_[i] = h.p2.get_decimal();
504  baryon_number_[i] = p.type().baryon_number();
505  strangeness_[i] = p.type().strangeness();
506  }
507 
508  i++;
509  }
510  }
511  // Flush rest to tree
512  if (i > 0) {
513  npart_ = i;
514  particles_tree_->Fill();
515  }
516 }
517 
518 void RootOutput::collisions_to_tree(const ParticleList &incoming,
519  const ParticleList &outgoing,
520  const double weight,
521  const double partial_weight) {
523  nin_ = incoming.size();
524  nout_ = outgoing.size();
525  npart_ = nin_ + nout_;
526  wgt_ = weight;
527  par_wgt_ = partial_weight;
528 
529  int i = 0;
530 
531  /* It is assumed that nin + nout < max_buffer_size_
532  * This is true for any possible reaction for current buffer size: 10000
533  * But if one wants initial/final particles written to collisions
534  * then implementation should be updated. */
535 
536  for (const ParticleList &plist : {incoming, outgoing}) {
537  for (const auto &p : plist) {
538  pdgcode_[i] = p.pdgcode().get_decimal();
539  charge_[i] = p.type().charge();
540 
541  p0_[i] = p.momentum().x0();
542  px_[i] = p.momentum().x1();
543  py_[i] = p.momentum().x2();
544  pz_[i] = p.momentum().x3();
545 
546  t_[i] = p.position().x0();
547  x_[i] = p.position().x1();
548  y_[i] = p.position().x2();
549  z_[i] = p.position().x3();
550 
551  if (coll_extended_) {
552  const auto h = p.get_history();
553  formation_time_[i] = p.formation_time();
554  xsec_factor_[i] = p.xsec_scaling_factor();
555  time_last_coll_[i] = h.time_last_collision;
556  coll_per_part_[i] = h.collisions_per_particle;
557  proc_id_origin_[i] = h.id_process;
558  proc_type_origin_[i] = static_cast<int>(h.process_type);
559  pdg_mother1_[i] = h.p1.get_decimal();
560  pdg_mother2_[i] = h.p2.get_decimal();
561  baryon_number_[i] = p.type().baryon_number();
562  }
563 
564  i++;
565  }
566  }
567 
568  collisions_tree_->Fill();
569 }
570 } // 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:58
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
A class to pre-calculate and store parameters relevant for density calculation.
Definition: density.h:108
Abstraction of generic output.
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
size_t size() const
Definition: particles.h:87
void at_interaction(const Action &action, const double density) override
Writes collisions to a tree defined by treename.
Definition: rootoutput.cc:441
std::vector< double > t_
Property that is written to ROOT output.
Definition: rootoutput.h:189
std::vector< double > y_
Property that is written to ROOT output.
Definition: rootoutput.h:191
double current_t_
Property that is written to ROOT output.
Definition: rootoutput.h:208
OutputOnlyFinal particles_only_final_
Print only final particles in the event, no intermediate output.
Definition: rootoutput.h:223
int current_event_
Number of current event.
Definition: rootoutput.h:170
TTree * collisions_tree_
TTree for collision output.
Definition: rootoutput.h:150
~RootOutput()
Destructor.
Definition: rootoutput.cc:355
double E_kinetic_tot_
Property that is written to ROOT output.
Definition: rootoutput.h:209
int test_p_
Property that is written to ROOT output.
Definition: rootoutput.h:207
void collisions_to_tree(const ParticleList &incoming, const ParticleList &outgoing, const double weight, const double partial_weight)
Writes collisions to a tree defined by treename.
Definition: rootoutput.cc:518
int autosave_frequency_
Root file cannot be read if it was not properly closed and finalized.
Definition: rootoutput.h:234
std::vector< int > proc_type_origin_
Property that is written to ROOT output.
Definition: rootoutput.h:202
double modus_l_
Property that is written to ROOT output.
Definition: rootoutput.h:208
std::vector< double > time_last_coll_
Property that is written to ROOT output.
Definition: rootoutput.h:196
std::vector< int > coll_per_part_
Property that is written to ROOT output.
Definition: rootoutput.h:200
int tcounter_
Property that is written to ROOT output.
Definition: rootoutput.h:207
std::vector< int > baryon_number_
Property that is written to ROOT output.
Definition: rootoutput.h:205
int npart_
Property that is written to ROOT output.
Definition: rootoutput.h:207
std::vector< int > pdg_mother1_
Property that is written to ROOT output.
Definition: rootoutput.h:203
int nin_
Property that is written to ROOT output.
Definition: rootoutput.h:207
int ev_
Property that is written to ROOT output.
Definition: rootoutput.h:207
bool write_collisions_
Option to write collisions tree.
Definition: rootoutput.h:214
std::vector< int > charge_
Property that is written to ROOT output.
Definition: rootoutput.h:199
std::vector< double > px_
Property that is written to ROOT output.
Definition: rootoutput.h:186
const std::filesystem::path filename_
Filename of output.
Definition: rootoutput.h:132
std::vector< int > strangeness_
Property that is written to ROOT output.
Definition: rootoutput.h:206
bool write_initial_conditions_
Option to write particles tree for initial conditions.
Definition: rootoutput.h:220
double wgt_
Property that is written to ROOT output.
Definition: rootoutput.h:208
std::vector< double > py_
Property that is written to ROOT output.
Definition: rootoutput.h:187
std::vector< int > proc_id_origin_
Property that is written to ROOT output.
Definition: rootoutput.h:201
void particles_to_tree(T &particles)
Writes particles to a tree defined by treename.
Definition: rootoutput.cc:455
const bool part_extended_
Whether extended particle output is on.
Definition: rootoutput.h:237
void at_eventstart(const Particles &particles, const int event_number, const EventInfo &event) override
update event number and writes intermediate particles to a tree.
Definition: rootoutput.cc:362
double impact_b_
Property that is written to ROOT output.
Definition: rootoutput.h:208
RootOutput(const std::filesystem::path &path, const std::string &name, const OutputParameters &out_par)
Construct ROOT output.
Definition: rootoutput.cc:232
std::vector< double > z_
Property that is written to ROOT output.
Definition: rootoutput.h:192
std::vector< double > formation_time_
Property that is written to ROOT output.
Definition: rootoutput.h:193
void at_eventend(const Particles &particles, const int event_number, const EventInfo &event) override
update event number and impact parameter, and writes intermediate particles to a tree.
Definition: rootoutput.cc:401
std::vector< double > x_
Property that is written to ROOT output.
Definition: rootoutput.h:190
std::vector< double > p0_
Property that is written to ROOT output.
Definition: rootoutput.h:185
bool empty_event_
Property that is written to ROOT output.
Definition: rootoutput.h:210
static const int max_buffer_size_
Maximal buffer size.
Definition: rootoutput.h:178
std::unique_ptr< TFile > root_out_file_
Pointer to root output file.
Definition: rootoutput.h:136
std::vector< double > xsec_factor_
Property that is written to ROOT output.
Definition: rootoutput.h:195
double E_fields_tot_
Property that is written to ROOT output.
Definition: rootoutput.h:209
std::filesystem::path filename_unfinished_
Filename of output as long as simulation is still running.
Definition: rootoutput.h:134
const bool coll_extended_
Whether extended collisions output is on.
Definition: rootoutput.h:239
double par_wgt_
Property that is written to ROOT output.
Definition: rootoutput.h:208
void init_trees()
Basic initialization routine, creating the TTree objects for particles and collisions.
Definition: rootoutput.cc:252
int output_counter_
Number of output in a given event.
Definition: rootoutput.h:168
void at_intermediate_time(const Particles &particles, const std::unique_ptr< Clock > &clock, const DensityParameters &dens_param, const EventInfo &event) override
Writes intermediate particles to a tree defined by treename, if it is allowed (i.e....
Definition: rootoutput.cc:384
double E_tot_
Property that is written to ROOT output.
Definition: rootoutput.h:209
TTree * particles_tree_
TTree for particles output.
Definition: rootoutput.h:143
std::vector< double > pz_
Property that is written to ROOT output.
Definition: rootoutput.h:188
int nout_
Property that is written to ROOT output.
Definition: rootoutput.h:207
const bool ic_extended_
Whether extended ic output is on.
Definition: rootoutput.h:241
bool write_particles_
Option to write particles tree.
Definition: rootoutput.h:217
std::vector< int > pdgcode_
Property that is written to ROOT output.
Definition: rootoutput.h:198
std::vector< int > pdg_mother2_
Property that is written to ROOT output.
Definition: rootoutput.h:204
@ IfNotEmpty
Print only final-state particles, and those only if the event is not empty.
@ No
Print initial, intermediate and final-state particles.
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
constexpr int p
Proton.
Definition: action.h:24
static constexpr int LHyperSurfaceCrossing
Definition: binaryoutput.cc:22
@ HyperSurfaceCrossing
See here for a short description.
static constexpr int LOutput
Structure to contain custom data for output.
bool empty_event
True if no collisions happened.
bool impose_kinematic_cut_for_SMASH_IC
Whether or not kinematic cuts are employed for SMASH IC.
Helper structure for Experiment to hold output options and parameters.