Version: SMASH-3.3
rootoutput.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2025
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 
21 const int RootOutput::max_buffer_size_ = 500000;
22 
266 RootOutput::RootOutput(const std::filesystem::path &path,
267  const std::string &name, const OutputParameters &out_par)
268  : OutputInterface(name),
269  filename_(path / (name + ".root")),
270  write_collisions_(name == "Collisions" || name == "Dileptons" ||
271  name == "Photons"),
272  write_particles_(name == "Particles"),
273  write_initial_conditions_(name == "SMASH_IC"),
274  particles_only_final_(out_par.part_only_final),
275  part_extended_(out_par.part_extended),
276  coll_extended_(out_par.coll_extended),
277  ic_extended_(out_par.ic_extended) {
279  filename_unfinished_ += ".unfinished";
281  std::make_unique<TFile>(filename_unfinished_.native().c_str(), "NEW");
282  init_trees();
283 }
284 
287  particles_tree_ = new TTree("particles", "particles");
288 
289  particles_tree_->Branch("ev", &ev_, "ev/I");
290  particles_tree_->Branch("ens", &ens_, "ens/I");
291  particles_tree_->Branch("tcounter", &tcounter_, "tcounter/I");
292  particles_tree_->Branch("npart", &npart_, "npart/I");
293  particles_tree_->Branch("test_p", &test_p_, "test_p/I");
294  particles_tree_->Branch("modus_l", &modus_l_, "modus_l/D");
295  particles_tree_->Branch("current_t", &current_t_, "current_t/D");
296  particles_tree_->Branch("impact_b", &impact_b_, "impact_b/D");
297  particles_tree_->Branch("empty_event", &empty_event_, "empty_event/O");
298 
299  particles_tree_->Branch("id", &id_[0], "id[npart]/I");
300  particles_tree_->Branch("pdgcode", &pdgcode_[0], "pdgcode[npart]/I");
301  particles_tree_->Branch("charge", &charge_[0], "charge[npart]/I");
302  particles_tree_->Branch("formation_time", &formation_time_[0],
303  "formation_time[npart]/D");
304  particles_tree_->Branch("time_last_collision", &time_last_collision_[0],
305  "time_last_collision[npart]/D");
306 
307  particles_tree_->Branch("p0", &p0_[0], "p0[npart]/D");
308  particles_tree_->Branch("px", &px_[0], "px[npart]/D");
309  particles_tree_->Branch("py", &py_[0], "py[npart]/D");
310  particles_tree_->Branch("pz", &pz_[0], "pz[npart]/D");
311 
312  particles_tree_->Branch("t", &t_[0], "t[npart]/D");
313  particles_tree_->Branch("x", &x_[0], "x[npart]/D");
314  particles_tree_->Branch("y", &y_[0], "y[npart]/D");
315  particles_tree_->Branch("z", &z_[0], "z[npart]/D");
316 
317  particles_tree_->Branch("E_kinetic_tot", &E_kinetic_tot_,
318  "E_kinetic_tot/D");
319  particles_tree_->Branch("E_fields_tot", &E_fields_tot_, "E_fields_tot/D");
320  particles_tree_->Branch("E_tot", &E_tot_, "E_tot/D");
321 
322  if (part_extended_ || ic_extended_) {
323  particles_tree_->Branch("ncoll", &coll_per_part_[0], "ncoll[npart]/I");
324  particles_tree_->Branch("xsecfac", &xsec_factor_[0], "xsecfac[npart]/D");
325  particles_tree_->Branch("proc_id_origin", &proc_id_origin_[0],
326  "proc_id_origin[npart]/I");
327  particles_tree_->Branch("proc_type_origin", &proc_type_origin_[0],
328  "proc_type_origin[npart]/I");
329  particles_tree_->Branch("pdg_mother1", &pdg_mother1_[0],
330  "pdg_mother1[npart]/I");
331  particles_tree_->Branch("pdg_mother2", &pdg_mother2_[0],
332  "pdg_mother2[npart]/I");
333  particles_tree_->Branch("baryon_number", &baryon_number_[0],
334  "baryon_number[npart]/I");
335  particles_tree_->Branch("strangeness", &strangeness_[0],
336  "strangeness[npart]/I");
337  }
338  }
339 
340  if (write_collisions_) {
341  collisions_tree_ = new TTree("collisions", "collisions");
342 
343  collisions_tree_->Branch("ev", &ev_, "ev/I");
344  collisions_tree_->Branch("ens", &ens_, "ens/I");
345 
346  collisions_tree_->Branch("nin", &nin_, "nin/I");
347  collisions_tree_->Branch("nout", &nout_, "nout/I");
348  collisions_tree_->Branch("npart", &npart_, "npart/I");
349  collisions_tree_->Branch("weight", &wgt_, "weight/D");
350  collisions_tree_->Branch("partial_weight", &par_wgt_, "partial_weight/D");
351 
352  collisions_tree_->Branch("id", &id_[0], "id[npart]/I");
353  collisions_tree_->Branch("pdgcode", &pdgcode_[0], "pdgcode[npart]/I");
354  collisions_tree_->Branch("charge", &charge_[0], "charge[npart]/I");
355  collisions_tree_->Branch("formation_time", &formation_time_[0],
356  "formation_time[npart]/D");
357  collisions_tree_->Branch("time_last_collision", &time_last_collision_[0],
358  "time_last_collision[npart]/D");
359 
360  collisions_tree_->Branch("p0", &p0_[0], "p0[npart]/D");
361  collisions_tree_->Branch("px", &px_[0], "px[npart]/D");
362  collisions_tree_->Branch("py", &py_[0], "py[npart]/D");
363  collisions_tree_->Branch("pz", &pz_[0], "pz[npart]/D");
364 
365  collisions_tree_->Branch("t", &t_[0], "t[npart]/D");
366  collisions_tree_->Branch("x", &x_[0], "x[npart]/D");
367  collisions_tree_->Branch("y", &y_[0], "y[npart]/D");
368  collisions_tree_->Branch("z", &z_[0], "z[npart]/D");
369 
370  if (coll_extended_) {
371  collisions_tree_->Branch("ncoll", &coll_per_part_[0], "ncoll[npart]/I");
372  collisions_tree_->Branch("xsecfac", &xsec_factor_[0], "xsecfac[npart]/D");
373  collisions_tree_->Branch("proc_id_origin", &proc_id_origin_[0],
374  "proc_id_origin[npart]/I");
375  collisions_tree_->Branch("proc_type_origin", &proc_type_origin_[0],
376  "proc_type_origin[npart]/I");
377  collisions_tree_->Branch("pdg_mother1", &pdg_mother1_[0],
378  "pdg_mother1[npart]/I");
379  collisions_tree_->Branch("pdg_mother2", &pdg_mother2_[0],
380  "pdg_mother2[npart]/I");
381  collisions_tree_->Branch("baryon_number", &baryon_number_[0],
382  "baryon_number[npart]/I");
383  collisions_tree_->Branch("strangeness", &strangeness_[0],
384  "strangeness[npart]/I");
385  }
386  }
387 }
388 
394  // kOverwrite option prevents from writing extra TKey objects into root file
395  root_out_file_->Write("", TObject::kOverwrite);
396  root_out_file_->Close();
397  std::filesystem::rename(filename_unfinished_, filename_);
398 }
399 
400 void RootOutput::at_eventstart(const Particles &particles,
401  const EventLabel &event_label,
402  const EventInfo &event) {
403  // save event and ensemble numbers
404  current_event_ = event_label.event_number;
405  current_ensemble_ = event_label.ensemble_number;
406 
407  modus_l_ = event.modus_length;
408  test_p_ = event.test_particles;
409  current_t_ = event.current_time;
410  E_kinetic_tot_ = event.total_kinetic_energy;
411  E_fields_tot_ = event.total_mean_field_energy;
412  E_tot_ = event.total_energy;
413 
415  output_counter_ = 0;
416  // This is to have only one output of positive impact parameter per event
417  impact_b_ = -1.0;
418  empty_event_ = false;
419  particles_to_tree(particles);
420  output_counter_++;
421  }
422 }
423 
425  const std::unique_ptr<Clock> &,
426  const DensityParameters &,
427  const EventLabel &event_label,
428  const EventInfo &event) {
429  current_ensemble_ = event_label.ensemble_number;
430  modus_l_ = event.modus_length;
431  test_p_ = event.test_particles;
432  current_t_ = event.current_time;
433  E_kinetic_tot_ = event.total_kinetic_energy;
434  E_fields_tot_ = event.total_mean_field_energy;
435  E_tot_ = event.total_energy;
436 
438  particles_to_tree(particles);
439  output_counter_++;
440  }
441 }
442 
443 void RootOutput::at_eventend(const Particles &particles,
444  const EventLabel &event_label,
445  const EventInfo &event) {
446  current_ensemble_ = event_label.ensemble_number;
447  test_p_ = event.test_particles;
448  modus_l_ = event.modus_length;
449  current_t_ = event.current_time;
450  impact_b_ = event.impact_parameter;
451  empty_event_ = event.empty_event;
452 
453  E_kinetic_tot_ = event.total_kinetic_energy;
454  E_fields_tot_ = event.total_mean_field_energy;
455  E_tot_ = event.total_energy;
456 
457  if (write_particles_ &&
458  !(event.empty_event &&
460  particles_to_tree(particles);
461  }
462  /* Calculate the autosave frequency to be used. In case multiple ensembles are
463  * used this has to be at least 1, so set it to 1 if the number of ensembles
464  * is larger than the initial value of the autosave frequency. The evaluation
465  * of the actual frequency to be used has to be done only once, i.e. when the
466  * autosave_frequency_ has still its meaningless initial negative value. */
467  if (autosave_frequency_ < 0) {
468  autosave_frequency_ = 1000 / event.n_ensembles;
469  if (autosave_frequency_ == 0) {
471  }
472  }
473  /* Forced regular dump from operational memory to disk. Very demanding!
474  * If program crashes written data will NOT be lost. */
477  particles_tree_->AutoSave("SaveSelf");
478  }
479  if (write_collisions_) {
480  collisions_tree_->AutoSave("SaveSelf");
481  }
482  }
483 }
484 
486  const double /*density*/) {
487  if (write_collisions_) {
489  action.get_total_weight(), action.get_partial_weight());
490  }
491 
493  (action.get_type() == ProcessType::Fluidization ||
496  }
497 }
498 
499 template <typename T>
500 void RootOutput::particles_to_tree(T &particles) {
501  int i = 0;
502 
506  bool exceeded_buffer_message = true;
507 
508  for (const auto &p : particles) {
509  // Buffer full - flush to tree, else fill with particles
510  if (i >= max_buffer_size_) {
511  if (exceeded_buffer_message) {
512  logg[LOutput].warn()
513  << "\nThe number of particles N = " << particles.size()
514  << " exceeds the maximum buffer size B = " << max_buffer_size_
515  << ".\nceil(N/B) = "
516  << std::ceil(particles.size() /
517  static_cast<double>(max_buffer_size_))
518  << " separate ROOT Tree entries will be created at this output."
519  << "\nMaximum buffer size (max_buffer_size_) can be changed in "
520  << "rootoutput.h\n\n";
521  exceeded_buffer_message = false;
522  }
524  i = 0;
525  particles_tree_->Fill();
526  } else {
527  id_[i] = p.id();
528  pdgcode_[i] = p.pdgcode().get_decimal();
529  charge_[i] = p.type().charge();
530  formation_time_[i] = p.formation_time();
531  time_last_collision_[i] = p.get_history().time_last_collision;
532 
533  p0_[i] = p.momentum().x0();
534  px_[i] = p.momentum().x1();
535  py_[i] = p.momentum().x2();
536  pz_[i] = p.momentum().x3();
537 
538  t_[i] = p.position().x0();
539  x_[i] = p.position().x1();
540  y_[i] = p.position().x2();
541  z_[i] = p.position().x3();
542 
543  if (part_extended_ || ic_extended_) {
544  const auto h = p.get_history();
545  coll_per_part_[i] = h.collisions_per_particle;
546  xsec_factor_[i] = p.xsec_scaling_factor();
547  proc_id_origin_[i] = h.id_process;
548  proc_type_origin_[i] = static_cast<int>(h.process_type);
549  pdg_mother1_[i] = h.p1.get_decimal();
550  pdg_mother2_[i] = h.p2.get_decimal();
551  baryon_number_[i] = p.type().baryon_number();
552  strangeness_[i] = p.type().strangeness();
553  }
554 
555  i++;
556  }
557  }
558  // Flush rest to tree
559  if (i > 0) {
560  npart_ = i;
561  particles_tree_->Fill();
562  }
563 }
564 
565 void RootOutput::collisions_to_tree(const ParticleList &incoming,
566  const ParticleList &outgoing,
567  const double weight,
568  const double partial_weight) {
571  nin_ = incoming.size();
572  nout_ = outgoing.size();
573  npart_ = nin_ + nout_;
574  wgt_ = weight;
575  par_wgt_ = partial_weight;
576 
577  int i = 0;
578 
579  /* It is assumed that nin + nout < max_buffer_size_
580  * This is true for any possible reaction for current buffer size
581  * But if one wants initial/final particles written to collisions
582  * then implementation should be updated. */
583 
584  for (const ParticleList &plist : {incoming, outgoing}) {
585  for (const auto &p : plist) {
586  id_[i] = p.id();
587  pdgcode_[i] = p.pdgcode().get_decimal();
588  charge_[i] = p.type().charge();
589  formation_time_[i] = p.formation_time();
590  time_last_collision_[i] = p.get_history().time_last_collision;
591 
592  p0_[i] = p.momentum().x0();
593  px_[i] = p.momentum().x1();
594  py_[i] = p.momentum().x2();
595  pz_[i] = p.momentum().x3();
596 
597  t_[i] = p.position().x0();
598  x_[i] = p.position().x1();
599  y_[i] = p.position().x2();
600  z_[i] = p.position().x3();
601 
602  if (coll_extended_) {
603  const auto h = p.get_history();
604  coll_per_part_[i] = h.collisions_per_particle;
605  xsec_factor_[i] = p.xsec_scaling_factor();
606  proc_id_origin_[i] = h.id_process;
607  proc_type_origin_[i] = static_cast<int>(h.process_type);
608  pdg_mother1_[i] = h.p1.get_decimal();
609  pdg_mother2_[i] = h.p2.get_decimal();
610  baryon_number_[i] = p.type().baryon_number();
611  strangeness_[i] = p.type().strangeness();
612  }
613 
614  i++;
615  }
616  }
617 
618  collisions_tree_->Fill();
619 }
620 } // 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:92
Abstraction of generic output.
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
void at_interaction(const Action &action, const double density) override
Writes collisions to a tree defined by treename.
Definition: rootoutput.cc:485
std::vector< double > t_
Property that is written to ROOT output.
Definition: rootoutput.h:177
std::vector< double > y_
Property that is written to ROOT output.
Definition: rootoutput.h:179
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 intermediate particles to a tree defined by treename, if it is allowed (i.e....
Definition: rootoutput.cc:424
double current_t_
Property that is written to ROOT output.
Definition: rootoutput.h:163
OutputOnlyFinal particles_only_final_
Print only final particles in the event (no intermediate output).
Definition: rootoutput.h:208
int current_event_
Number of current event.
Definition: rootoutput.h:140
TTree * collisions_tree_
TTree for collision output.
Definition: rootoutput.h:120
~RootOutput()
Destructor.
Definition: rootoutput.cc:393
double E_kinetic_tot_
Property that is written to ROOT output.
Definition: rootoutput.h:181
int test_p_
Property that is written to ROOT output.
Definition: rootoutput.h:161
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:565
void at_eventstart(const Particles &particles, const EventLabel &event_label, const EventInfo &event) override
update event number and writes intermediate particles to a tree.
Definition: rootoutput.cc:400
int autosave_frequency_
ROOT file cannot be read if it was not properly closed and finalized.
Definition: rootoutput.h:222
std::vector< int > proc_type_origin_
Property that is written to ROOT output.
Definition: rootoutput.h:187
double modus_l_
Property that is written to ROOT output.
Definition: rootoutput.h:162
std::vector< int > coll_per_part_
Property that is written to ROOT output.
Definition: rootoutput.h:184
int tcounter_
Property that is written to ROOT output.
Definition: rootoutput.h:159
std::vector< int > baryon_number_
Property that is written to ROOT output.
Definition: rootoutput.h:190
int npart_
Property that is written to ROOT output.
Definition: rootoutput.h:160
std::vector< int > pdg_mother1_
Property that is written to ROOT output.
Definition: rootoutput.h:188
int nin_
Property that is written to ROOT output.
Definition: rootoutput.h:192
int ev_
Property that is written to ROOT output.
Definition: rootoutput.h:157
bool write_collisions_
Option to write collisions tree.
Definition: rootoutput.h:199
std::vector< int > charge_
Property that is written to ROOT output.
Definition: rootoutput.h:168
std::vector< double > px_
Property that is written to ROOT output.
Definition: rootoutput.h:174
const std::filesystem::path filename_
Filename of output.
Definition: rootoutput.h:102
std::vector< int > strangeness_
Property that is written to ROOT output.
Definition: rootoutput.h:191
bool write_initial_conditions_
Option to write particles tree for initial conditions.
Definition: rootoutput.h:205
double wgt_
Property that is written to ROOT output.
Definition: rootoutput.h:194
std::vector< double > py_
Property that is written to ROOT output.
Definition: rootoutput.h:175
std::vector< int > proc_id_origin_
Property that is written to ROOT output.
Definition: rootoutput.h:186
void particles_to_tree(T &particles)
Writes particles to a tree defined by treename.
Definition: rootoutput.cc:500
const bool part_extended_
Whether extended particle output is on.
Definition: rootoutput.h:225
double impact_b_
Property that is written to ROOT output.
Definition: rootoutput.h:164
RootOutput(const std::filesystem::path &path, const std::string &name, const OutputParameters &out_par)
Construct ROOT output.
Definition: rootoutput.cc:266
std::vector< double > z_
Property that is written to ROOT output.
Definition: rootoutput.h:180
std::vector< int > id_
Property that is written to ROOT output.
Definition: rootoutput.h:166
std::vector< double > formation_time_
Property that is written to ROOT output.
Definition: rootoutput.h:169
int current_ensemble_
Number of current ensemble.
Definition: rootoutput.h:142
void at_eventend(const Particles &particles, const EventLabel &event_label, const EventInfo &event) override
update event number and impact parameter, and writes intermediate particles to a tree.
Definition: rootoutput.cc:443
std::vector< double > x_
Property that is written to ROOT output.
Definition: rootoutput.h:178
std::vector< double > p0_
Property that is written to ROOT output.
Definition: rootoutput.h:173
bool empty_event_
Property that is written to ROOT output.
Definition: rootoutput.h:165
static const int max_buffer_size_
Maximal buffer size.
Definition: rootoutput.h:150
std::unique_ptr< TFile > root_out_file_
Pointer to root output file.
Definition: rootoutput.h:106
std::vector< double > xsec_factor_
Property that is written to ROOT output.
Definition: rootoutput.h:185
double E_fields_tot_
Property that is written to ROOT output.
Definition: rootoutput.h:182
std::filesystem::path filename_unfinished_
Filename of output as long as simulation is still running.
Definition: rootoutput.h:104
const bool coll_extended_
Whether extended collisions output is on.
Definition: rootoutput.h:227
std::vector< double > time_last_collision_
Property that is written to ROOT output.
Definition: rootoutput.h:171
double par_wgt_
Property that is written to ROOT output.
Definition: rootoutput.h:195
void init_trees()
Basic initialization routine, creating the TTree objects for particles and collisions.
Definition: rootoutput.cc:285
int output_counter_
Number of output in a given event.
Definition: rootoutput.h:138
double E_tot_
Property that is written to ROOT output.
Definition: rootoutput.h:183
TTree * particles_tree_
TTree for particles output.
Definition: rootoutput.h:113
int ens_
Property that is written to ROOT output.
Definition: rootoutput.h:158
std::vector< double > pz_
Property that is written to ROOT output.
Definition: rootoutput.h:176
int nout_
Property that is written to ROOT output.
Definition: rootoutput.h:193
const bool ic_extended_
Whether extended ic output is on.
Definition: rootoutput.h:229
bool write_particles_
Option to write particles tree.
Definition: rootoutput.h:202
std::vector< int > pdgcode_
Property that is written to ROOT output.
Definition: rootoutput.h:167
std::vector< int > pdg_mother2_
Property that is written to ROOT output.
Definition: rootoutput.h:189
@ 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:40
constexpr int p
Proton.
Definition: action.h:24
@ FluidizationNoRemoval
See here for a short description.
@ Fluidization
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.
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.
Helper structure for Experiment to hold output options and parameters.