Version: SMASH-3.0
rootoutput.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2022
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  }
301  }
302 
303  if (write_collisions_) {
304  collisions_tree_ = new TTree("collisions", "collisions");
305 
306  collisions_tree_->Branch("nin", &nin_, "nin/I");
307  collisions_tree_->Branch("nout", &nout_, "nout/I");
308  collisions_tree_->Branch("npart", &npart_, "npart/I");
309  collisions_tree_->Branch("ev", &ev_, "ev/I");
310  collisions_tree_->Branch("weight", &wgt_, "weight/D");
311  collisions_tree_->Branch("partial_weight", &par_wgt_, "partial_weight/D");
312 
313  collisions_tree_->Branch("pdgcode", &pdgcode_[0], "pdgcode[npart]/I");
314  collisions_tree_->Branch("charge", &charge_[0], "charge[npart]/I");
315 
316  collisions_tree_->Branch("p0", &p0_[0], "p0[npart]/D");
317  collisions_tree_->Branch("px", &px_[0], "px[npart]/D");
318  collisions_tree_->Branch("py", &py_[0], "py[npart]/D");
319  collisions_tree_->Branch("pz", &pz_[0], "pz[npart]/D");
320 
321  collisions_tree_->Branch("t", &t_[0], "t[npart]/D");
322  collisions_tree_->Branch("x", &x_[0], "x[npart]/D");
323  collisions_tree_->Branch("y", &y_[0], "y[npart]/D");
324  collisions_tree_->Branch("z", &z_[0], "z[npart]/D");
325 
326  if (coll_extended_) {
327  collisions_tree_->Branch("ncoll", &coll_per_part_[0], "ncoll[npart]/I");
328  collisions_tree_->Branch("form_time", &formation_time_[0],
329  "form_time[npart]/D");
330  collisions_tree_->Branch("xsecfac", &xsec_factor_[0], "xsecfac[npart]/D");
331  collisions_tree_->Branch("proc_id_origin", &proc_id_origin_[0],
332  "proc_id_origin[npart]/I");
333  collisions_tree_->Branch("proc_type_origin", &proc_type_origin_[0],
334  "proc_type_origin[npart]/I");
335  collisions_tree_->Branch("time_last_coll", &time_last_coll_[0],
336  "time_last_coll[npart]/D");
337  collisions_tree_->Branch("pdg_mother1", &pdg_mother1_[0],
338  "pdg_mother1[npart]/I");
339  collisions_tree_->Branch("pdg_mother2", &pdg_mother2_[0],
340  "pdg_mother2[npart]/I");
341  collisions_tree_->Branch("baryon_number", &baryon_number_[0],
342  "baryon_number[npart]/I");
343  }
344  }
345 }
346 
352  // kOverwrite option prevents from writing extra TKey objects into root file
353  root_out_file_->Write("", TObject::kOverwrite);
354  root_out_file_->Close();
355  std::filesystem::rename(filename_unfinished_, filename_);
356 }
357 
358 void RootOutput::at_eventstart(const Particles &particles,
359  const int event_number, const EventInfo &event) {
360  // save event number
361  current_event_ = event_number;
362 
363  modus_l_ = event.modus_length;
364  test_p_ = event.test_particles;
365  current_t_ = event.current_time;
366  E_kinetic_tot_ = event.total_kinetic_energy;
367  E_fields_tot_ = event.total_mean_field_energy;
368  E_tot_ = event.total_energy;
369 
371  output_counter_ = 0;
372  // This is to have only one output of positive impact parameter per event
373  impact_b_ = -1.0;
374  empty_event_ = false;
375  particles_to_tree(particles);
376  output_counter_++;
377  }
378 }
379 
381  const std::unique_ptr<Clock> &,
382  const DensityParameters &,
383  const EventInfo &event) {
384  modus_l_ = event.modus_length;
385  test_p_ = event.test_particles;
386  current_t_ = event.current_time;
387  E_kinetic_tot_ = event.total_kinetic_energy;
388  E_fields_tot_ = event.total_mean_field_energy;
389  E_tot_ = event.total_energy;
390 
392  particles_to_tree(particles);
393  output_counter_++;
394  }
395 }
396 
397 void RootOutput::at_eventend(const Particles &particles,
398  const int /*event_number*/,
399  const EventInfo &event) {
400  modus_l_ = event.modus_length;
401  test_p_ = event.test_particles;
402  current_t_ = event.current_time;
403  E_kinetic_tot_ = event.total_kinetic_energy;
404  E_fields_tot_ = event.total_mean_field_energy;
405  E_tot_ = event.total_energy;
406 
407  impact_b_ = event.impact_parameter;
408  empty_event_ = event.empty_event;
409  if (write_particles_ &&
410  !(event.empty_event &&
412  particles_to_tree(particles);
413  }
414  /* Forced regular dump from operational memory to disk. Very demanding!
415  * If program crashes written data will NOT be lost. */
418  particles_tree_->AutoSave("SaveSelf");
419  }
420  if (write_collisions_) {
421  collisions_tree_->AutoSave("SaveSelf");
422  }
423  }
424 
426  // If the runtime is too short some particles might not yet have
427  // reached the hypersurface. Warning is printed.
428  if (particles.size() != 0 && !event.impose_kinematic_cut_for_SMASH_IC) {
430  "End time might be too small for initial conditions output. "
431  "Hypersurface has not yet been crossed by ",
432  particles.size(), " particle(s).");
433  }
434  }
435 }
436 
438  const double /*density*/) {
439  if (write_collisions_) {
441  action.get_total_weight(), action.get_partial_weight());
442  }
443 
447  }
448 }
449 
450 template <typename T>
451 void RootOutput::particles_to_tree(T &particles) {
452  int i = 0;
453 
456  bool exceeded_buffer_message = true;
457 
458  for (const auto &p : particles) {
459  // Buffer full - flush to tree, else fill with particles
460  if (i >= max_buffer_size_) {
461  if (exceeded_buffer_message) {
462  logg[LOutput].warn()
463  << "\nThe number of particles N = " << particles.size()
464  << " exceeds the maximum buffer size B = " << max_buffer_size_
465  << ".\nceil(N/B) = "
466  << std::ceil(particles.size() /
467  static_cast<double>(max_buffer_size_))
468  << " separate ROOT Tree entries will be created at this output."
469  << "\nMaximum buffer size (max_buffer_size_) can be changed in "
470  << "rootoutput.h\n\n";
471  exceeded_buffer_message = false;
472  }
474  i = 0;
475  particles_tree_->Fill();
476  } else {
477  pdgcode_[i] = p.pdgcode().get_decimal();
478  charge_[i] = p.type().charge();
479 
480  p0_[i] = p.momentum().x0();
481  px_[i] = p.momentum().x1();
482  py_[i] = p.momentum().x2();
483  pz_[i] = p.momentum().x3();
484 
485  t_[i] = p.position().x0();
486  x_[i] = p.position().x1();
487  y_[i] = p.position().x2();
488  z_[i] = p.position().x3();
489 
490  if (part_extended_ || ic_extended_) {
491  const auto h = p.get_history();
492  formation_time_[i] = p.formation_time();
493  xsec_factor_[i] = p.xsec_scaling_factor();
494  time_last_coll_[i] = h.time_last_collision;
495  coll_per_part_[i] = h.collisions_per_particle;
496  proc_id_origin_[i] = h.id_process;
497  proc_type_origin_[i] = static_cast<int>(h.process_type);
498  pdg_mother1_[i] = h.p1.get_decimal();
499  pdg_mother2_[i] = h.p2.get_decimal();
500  baryon_number_[i] = p.type().baryon_number();
501  }
502 
503  i++;
504  }
505  }
506  // Flush rest to tree
507  if (i > 0) {
508  npart_ = i;
509  particles_tree_->Fill();
510  }
511 }
512 
513 void RootOutput::collisions_to_tree(const ParticleList &incoming,
514  const ParticleList &outgoing,
515  const double weight,
516  const double partial_weight) {
518  nin_ = incoming.size();
519  nout_ = outgoing.size();
520  npart_ = nin_ + nout_;
521  wgt_ = weight;
522  par_wgt_ = partial_weight;
523 
524  int i = 0;
525 
526  /* It is assumed that nin + nout < max_buffer_size_
527  * This is true for any possible reaction for current buffer size: 10000
528  * But if one wants initial/final particles written to collisions
529  * then implementation should be updated. */
530 
531  for (const ParticleList &plist : {incoming, outgoing}) {
532  for (const auto &p : plist) {
533  pdgcode_[i] = p.pdgcode().get_decimal();
534  charge_[i] = p.type().charge();
535 
536  p0_[i] = p.momentum().x0();
537  px_[i] = p.momentum().x1();
538  py_[i] = p.momentum().x2();
539  pz_[i] = p.momentum().x3();
540 
541  t_[i] = p.position().x0();
542  x_[i] = p.position().x1();
543  y_[i] = p.position().x2();
544  z_[i] = p.position().x3();
545 
546  if (coll_extended_) {
547  const auto h = p.get_history();
548  formation_time_[i] = p.formation_time();
549  xsec_factor_[i] = p.xsec_scaling_factor();
550  time_last_coll_[i] = h.time_last_collision;
551  coll_per_part_[i] = h.collisions_per_particle;
552  proc_id_origin_[i] = h.id_process;
553  proc_type_origin_[i] = static_cast<int>(h.process_type);
554  pdg_mother1_[i] = h.p1.get_decimal();
555  pdg_mother2_[i] = h.p2.get_decimal();
556  baryon_number_[i] = p.type().baryon_number();
557  }
558 
559  i++;
560  }
561  }
562 
563  collisions_tree_->Fill();
564 }
565 } // 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:437
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:207
OutputOnlyFinal particles_only_final_
Print only final particles in the event, no intermediate output.
Definition: rootoutput.h:222
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:351
double E_kinetic_tot_
Property that is written to ROOT output.
Definition: rootoutput.h:208
int test_p_
Property that is written to ROOT output.
Definition: rootoutput.h:206
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:513
int autosave_frequency_
Root file cannot be read if it was not properly closed and finalized.
Definition: rootoutput.h:233
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:207
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:206
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:206
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:206
int ev_
Property that is written to ROOT output.
Definition: rootoutput.h:206
bool write_collisions_
Option to write collisions tree.
Definition: rootoutput.h:213
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
bool write_initial_conditions_
Option to write particles tree for initial conditions.
Definition: rootoutput.h:219
double wgt_
Property that is written to ROOT output.
Definition: rootoutput.h:207
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:451
const bool part_extended_
Whether extended particle output is on.
Definition: rootoutput.h:236
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:358
double impact_b_
Property that is written to ROOT output.
Definition: rootoutput.h:207
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:397
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:209
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:208
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:238
double par_wgt_
Property that is written to ROOT output.
Definition: rootoutput.h:207
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:380
double E_tot_
Property that is written to ROOT output.
Definition: rootoutput.h:208
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:206
const bool ic_extended_
Whether extended ic output is on.
Definition: rootoutput.h:240
bool write_particles_
Option to write particles tree.
Definition: rootoutput.h:216
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:21
@ 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.