Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:19:07

0001 // TRENTO: Reduced Thickness Event-by-event Nuclear Topology
0002 // Copyright 2015 Jonah E. Bernhard, J. Scott Moreland
0003 // TRENTO3D: Three-dimensional extension of TRENTO by Weiyao Ke
0004 // MIT License
0005 
0006 #include "output.h"
0007 
0008 #include <algorithm>
0009 #include <cmath>
0010 #include <iomanip>
0011 #include <iostream>
0012 
0013 #include <boost/filesystem.hpp>
0014 #include <boost/filesystem/fstream.hpp>
0015 #include <boost/program_options/variables_map.hpp>
0016 
0017 #include "event.h"
0018 #include "hdf5_utils.h"
0019 
0020 namespace trento {
0021 
0022 namespace {
0023 
0024 // These output functions are invoked by the Output class.
0025 
0026 void write_stream(std::ostream& os, int width,
0027     int num, double impact_param, const Event& event) {
0028   using std::fixed;
0029   using std::setprecision;
0030   using std::setw;
0031   using std::scientific;
0032 
0033   // Write a nicely-formatted line of event properties.
0034   os << setprecision(10)
0035      << setw(width)            << num
0036      << setw(15) << fixed      << impact_param
0037      << setw(5)                << event.npart();
0038   if (event.with_ncoll()) os << setw(8)                << event.ncoll();
0039   os   << setw(18) << scientific << event.multiplicity()            
0040      << fixed;
0041 
0042   for (const auto& ecc : event.eccentricity())
0043     os << setw(14) << ecc.second;
0044 
0045   //for (const auto& psi : event.participant_plane())
0046   //  os << setw(14) << psi.second;
0047 
0048   os << '\n';
0049 }
0050 
0051 void write_text_file(const fs::path& output_dir, int width,
0052     int num, double impact_param, const Event& event, bool header) {
0053   // Open a numbered file in the output directory.
0054   // Pad the filename with zeros.
0055   std::ostringstream padded_fname{};
0056   padded_fname << std::setw(width) << std::setfill('0') << num << ".dat";
0057   fs::ofstream ofs{output_dir / padded_fname.str()};
0058 
0059   if (header) {
0060     // Write a commented header of event properties as key = value pairs.
0061     ofs << std::setprecision(10)
0062         << "# event "   << num                  << '\n'
0063         << "# b     = " << impact_param         << '\n'
0064         << "# npart = " << event.npart()        << '\n'
0065         << "# mult  = " << event.multiplicity() << '\n';
0066 
0067     for (const auto& ecc : event.eccentricity())
0068       ofs << "# e" << ecc.first << "    = " << ecc.second << '\n';
0069 
0070     for (const auto& psi : event.participant_plane())
0071       ofs << "# psi" << psi.first << "    = " << psi.second << '\n';
0072   }
0073 
0074   // Write IC profile as a block grid.  Use C++ default float format (not
0075   // fixed-width) so that trailing zeros are omitted.  This significantly
0076   // increases output speed and saves disk space since many grid elements are
0077   // zero.
0078   bool is3d;
0079   if (event.density_grid().shape()[2] == 1) is3d = false;
0080   else is3d = true;
0081 
0082 
0083   for (const auto& slice : event.density_grid()) {
0084     for (const auto& row : slice) {
0085       for (const auto& item : row) {
0086          ofs << item << " ";
0087       }
0088       if (is3d) ofs << std::endl;
0089     }
0090     if (!is3d) ofs << std::endl;
0091   }
0092 }
0093 
0094 #ifdef TRENTO_HDF5
0095 
0096 /// Simple functor to write many events to an HDF5 file.
0097 class HDF5Writer {
0098  public:
0099   /// Prepare an HDF5 file for writing.
0100   HDF5Writer(const fs::path& filename);
0101 
0102   /// Write an event.
0103   void operator()(int num, double impact_param, const Event& event) const;
0104 
0105  private:
0106   /// Internal storage of the file object.
0107   H5::H5File file_;
0108 };
0109 
0110 // Add a simple scalar attribute to an HDF5 dataset.
0111 //template <typename T>
0112 //void hdf5_add_scalar_attr(
0113 //    const H5::DataSet& dataset, const std::string& name, const T& value) {
0114 //  const auto& datatype = hdf5::type<T>();
0115 //  auto attr = dataset.createAttribute(name, datatype, H5::DataSpace{});
0116 //  attr.write(datatype, &value);
0117 //}
0118 template <typename T>
0119 void hdf5_add_scalar_attr(
0120     const H5::Group& group, const std::string& name, const T& value) {
0121   const auto& datatype = hdf5::type<T>();
0122   auto attr = group.createAttribute(name, datatype, H5::DataSpace{});
0123   attr.write(datatype, &value);
0124 }
0125 
0126 HDF5Writer::HDF5Writer(const fs::path& filename)
0127     : file_(filename.string(), H5F_ACC_TRUNC)
0128 {}
0129 
0130 void HDF5Writer::operator()(
0131     int num, double impact_param, const Event& event) const {
0132   const auto& grid1 = event.density_grid();
0133   const auto& grid2 = event.TAB_grid();
0134 
0135   // The dataset name is a prefix plus the event number.
0136   const std::string gp_name{"/event_" + std::to_string(num)};
0137   const std::string sd_name{gp_name + "/matter_density"};
0138   const std::string tab_name{gp_name + "/Ncoll_density"};
0139 
0140   // create a group called event_i
0141   auto group = H5::Group(file_.createGroup(gp_name));
0142   // Write event attributes.
0143   hdf5_add_scalar_attr(group, "b", impact_param);
0144   hdf5_add_scalar_attr(group, "npart", event.npart());
0145   hdf5_add_scalar_attr(group, "ncoll", event.ncoll());
0146   hdf5_add_scalar_attr(group, "mult", event.multiplicity());
0147   hdf5_add_scalar_attr(group, "dxy", event.dxy());
0148   hdf5_add_scalar_attr(group, "deta", event.deta());
0149   hdf5_add_scalar_attr(group, "Ny", grid1.shape()[0]);
0150   hdf5_add_scalar_attr(group, "Nx", grid1.shape()[1]);
0151   hdf5_add_scalar_attr(group, "Nz", grid1.shape()[2]);
0152   for (const auto& ecc : event.eccentricity())
0153     hdf5_add_scalar_attr(group, "e" + std::to_string(ecc.first), ecc.second);
0154   for (const auto& psi : event.participant_plane())
0155     hdf5_add_scalar_attr(group, "psi" + std::to_string(psi.first), psi.second);
0156 
0157   ////////////////////////////////////////////////////////////////////
0158   // Define HDF5 datatype and dataspace to match the density (3D) grid.
0159   
0160   const auto& datatype1 = hdf5::type<Event::Grid3D::element>();
0161   std::array<hsize_t, Event::Grid3D::dimensionality> shape1;
0162   std::copy(grid1.shape(), grid1.shape() + shape1.size(), shape1.begin());
0163   auto dataspace1 = hdf5::make_dataspace(shape1);
0164 
0165   // Set dataset storage properties.
0166   H5::DSetCreatPropList proplist1{};
0167   // Set chunk size to the entire grid.  For typical grid sizes (~100x100), this
0168   // works out to ~80 KiB, which is pretty optimal.  Anyway, it makes logical
0169   // sense to chunk this way, since chunks must be read contiguously and there's
0170   // no reason to read a partial grid.
0171   proplist1.setChunk(shape1.size(), shape1.data());
0172   // Set gzip compression level.  4 is the default in h5py.
0173   proplist1.setDeflate(4);
0174 
0175   // Create the new dataset and write the grid for matter density
0176   auto dataset1 = file_.createDataSet(sd_name, datatype1, dataspace1, proplist1);
0177   dataset1.write(grid1.data(), datatype1);
0178 
0179   //////////////////////////////////////////////////////////////////
0180   // Define HDF5 datatype and dataspace to match the Ncoll (2D) grid.
0181   const auto& datatype2 = hdf5::type<Event::Grid::element>();
0182   std::array<hsize_t, Event::Grid::dimensionality> shape2;
0183   std::copy(grid2.shape(), grid2.shape() + shape2.size(), shape2.begin());
0184   auto dataspace2 = hdf5::make_dataspace(shape2);
0185   
0186   // Set dataset storage properties.
0187   H5::DSetCreatPropList proplist2{};
0188   // Set chunk size to the entire grid.  For typical grid sizes (~100x100), this
0189   // works out to ~80 KiB, which is pretty optimal.  Anyway, it makes logical
0190   // sense to chunk this way, since chunks must be read contiguously and there's
0191   // no reason to read a partial grid.
0192   proplist1.setChunk(shape2.size(), shape2.data());
0193   // Set gzip compression level.  4 is the default in h5py.
0194   proplist1.setDeflate(4);
0195 
0196   // Create the new dataset and write the grid for matter density
0197   auto dataset2 = file_.createDataSet(tab_name, datatype2, dataspace2, proplist2);
0198   dataset2.write(grid2.data(), datatype2);
0199 }
0200 
0201 #endif  // TRENTO_HDF5
0202 
0203 }  // unnamed namespace
0204 
0205 Output::Output(const VarMap& var_map){
0206   // Determine the required width (padding) of the event number.  For example if
0207   // there are 10 events, the numbers are 0-9 and so no padding is necessary.
0208   // However given 11 events, the numbers are 00-10 with padded 00, 01, ...
0209   auto nevents = var_map["number-events"].as<int>();
0210   auto width = static_cast<int>(std::ceil(std::log10(nevents)));
0211 
0212   // Write to stdout unless the quiet option was specified.
0213   if (!var_map["quiet"].as<bool>()) {
0214     writers_.emplace_back(
0215       [width](int num, double impact_param, const Event& event) {
0216         write_stream(std::cout, width, num, impact_param, event);
0217       }
0218     );
0219   }
0220 
0221   // Possibly write to text or HDF5 files.
0222   if (var_map.count("output")) {
0223     const auto& output_path = var_map["output"].as<fs::path>();
0224     if (hdf5::filename_is_hdf5(output_path)) {
0225 #ifdef TRENTO_HDF5
0226       if (fs::exists(output_path) && !fs::is_empty(output_path))
0227         throw std::runtime_error{"file '" + output_path.string() +
0228                                  "' exists, will not overwrite"};
0229       writers_.emplace_back(HDF5Writer{output_path});
0230 #else
0231       throw std::runtime_error{"HDF5 output was not compiled"};
0232 #endif  // TRENTO_HDF5
0233     } else {
0234       // Text files are all written into a single directory.  Require the
0235       // directory to be initially empty to avoid accidental overwriting and/or
0236       // spewing files into an already-used location.  If the directory does not
0237       // exist, create it.
0238       if (fs::exists(output_path)) {
0239         if (!fs::is_empty(output_path)) {
0240           throw std::runtime_error{"output directory '" + output_path.string() +
0241                                    "' must be empty"};
0242         }
0243       } else {
0244         fs::create_directories(output_path);
0245       }
0246       auto header = !var_map["no-header"].as<bool>();
0247       writers_.emplace_back(
0248         [output_path, width, header](
0249             int num, double impact_param, const Event& event) {
0250           write_text_file(output_path, width, num, impact_param, event, header);
0251         }
0252       );
0253     }
0254   }
0255 }
0256 
0257 }  // namespace trento