Back to home page

sPhenix code displayed by LXR

 
 

    


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

0001 // TRENTO: Reduced Thickness Event-by-event Nuclear Topology
0002 // Copyright 2015 Jonah E. Bernhard, J. Scott Moreland
0003 // MIT License
0004 
0005 #include "../src/output.h"
0006 
0007 #include "catch.hpp"
0008 #include "util.h"
0009 
0010 #include <boost/filesystem.hpp>
0011 #include <boost/filesystem/fstream.hpp>
0012 
0013 #include "../src/event.h"
0014 #include "../src/hdf5_utils.h"
0015 #include "../src/nucleus.h"
0016 
0017 using namespace trento;
0018 
0019 TEST_CASE( "output" ) {
0020   auto var_map = make_var_map({
0021     {"normalization", 1.},
0022     {"reduced-thickness", 0.},
0023     {"grid-max", 9.},
0024     {"grid-step", 0.3},
0025     {"fluctuation", 1.},
0026     {"cross-section", 6.4},
0027     {"nucleon-width", 0.5}
0028   });
0029 
0030   // create a test event
0031   Event event{var_map};
0032   NucleonProfile profile{var_map};
0033 
0034   auto nucleusA = Nucleus::create("Pb");
0035   auto nucleusB = Nucleus::create("Pb");
0036 
0037   auto b = 4.*std::sqrt(random::canonical<>());
0038   nucleusA->sample_nucleons(+.5*b);
0039   nucleusB->sample_nucleons(-.5*b);
0040 
0041   for (auto&& A : *nucleusA)
0042     for (auto&& B : *nucleusB)
0043       profile.participate(A, B);
0044 
0045   event.compute(*nucleusA, *nucleusB, profile);
0046 
0047   SECTION( "no output" ) {
0048     capture_stdout capture;
0049     Output output{make_var_map({{"quiet", true}, {"number-events", 1}})};
0050     output(1, b, event);
0051     CHECK( capture.stream.str().empty() );  // stdout should be empty
0052   }
0053 
0054   SECTION( "stdout only" ) {
0055     // write event 0 to stdout for the given number of events
0056     auto first_line = [&b, &event](int nev) {
0057       capture_stdout capture;
0058       Output output{make_var_map({{"quiet", false}, {"number-events", nev}})};
0059       output(0, b, event);
0060       std::string line;
0061       std::getline(capture.stream, line);
0062       return line;
0063     };
0064 
0065     // verify event number padding
0066     CHECK( first_line(1).substr(0, 1) == "0" );
0067     CHECK( first_line(10).substr(0, 1) == "0" );
0068     CHECK( first_line(11).substr(0, 2) == " 0" );
0069     CHECK( first_line(100).substr(0, 2) == " 0" );
0070     CHECK( first_line(101).substr(0, 3) == "  0" );
0071 
0072     // output lines for different total event numbers should be identical except
0073     // for the padding
0074     CHECK( first_line(1).substr(1) == first_line(1000).substr(3) );
0075 
0076     // read an output line back into separate objects
0077     int num, npart;
0078     double impact, mult, e2, e3, e4, e5;
0079     char end;
0080     {
0081       capture_stdout capture;
0082       Output output{make_var_map({{"quiet", false}, {"number-events", 1}})};
0083       output(0, b, event);
0084       capture.stream >> num >> impact >> npart >> mult >> e2 >> e3 >> e4 >> e5 >> std::ws;
0085       end = capture.stream.get();
0086     }
0087 
0088     // verify output data is correct
0089     CHECK( num == 0 );
0090     CHECK( impact == Approx(b) );
0091     CHECK( npart == event.npart() );
0092     CHECK( mult == Approx(event.multiplicity()) );
0093     CHECK( e2 == Approx(event.eccentricity().at(2)) );
0094     CHECK( e3 == Approx(event.eccentricity().at(3)) );
0095     CHECK( e4 == Approx(event.eccentricity().at(4)) );
0096     CHECK( e5 == Approx(event.eccentricity().at(5)) );
0097 
0098     // verify the end character is really the end of file
0099     CHECK( end == std::char_traits<char>::eof() );
0100   }
0101 
0102   SECTION( "text and stdout" ) {
0103     // configure for writing text files to a random temporary path
0104     temporary_path temp{};
0105     auto output_var_map = make_var_map({
0106       {"quiet", false},
0107       {"no-header", false},
0108       {"number-events", 50},
0109       {"output", temp.path}
0110     });
0111     Output output{output_var_map};
0112 
0113     // output directory should have been created
0114     CHECK( fs::exists(temp.path) );
0115 
0116     {
0117       // output two events and verify two lines were printed to stdout
0118       capture_stdout capture;
0119       output(3, b, event);
0120       output(27, b, event);
0121       int n = 0;
0122       std::string line;
0123       while (std::getline(capture.stream, line)) { ++n; }
0124       CHECK( n == 2 );
0125     }
0126 
0127     // verify event files were created
0128     CHECK( fs::exists(temp.path/"03.dat") );
0129     CHECK( fs::exists(temp.path/"27.dat") );
0130 
0131     {
0132       // read event file back in
0133       fs::ifstream ifs{temp.path/"03.dat"};
0134       std::string line;
0135 
0136       // check header
0137       std::getline(ifs, line);
0138       CHECK( line == "# event 3" );
0139 
0140       std::getline(ifs, line);
0141       CHECK( line.substr(0, 10) == "# b     = " );
0142       CHECK( std::stod(line.substr(10)) == Approx(b) );
0143 
0144       std::getline(ifs, line);
0145       CHECK( line.substr(0, 10) == "# npart = " );
0146       CHECK( std::stoi(line.substr(10)) == event.npart() );
0147 
0148       std::getline(ifs, line);
0149       CHECK( line.substr(0, 10) == "# mult  = " );
0150       CHECK( std::stod(line.substr(10)) == Approx(event.multiplicity()) );
0151 
0152       for (const auto& ecc : event.eccentricity()) {
0153         std::getline(ifs, line);
0154         CHECK( line.substr(0, 10) == ("# e" + std::to_string(ecc.first) + "    = ") );
0155         CHECK( std::stod(line.substr(10)) == Approx(ecc.second) );
0156       }
0157 
0158       // read the grid back in and check each element
0159       const auto* iter = event.reduced_thickness_grid().origin();
0160       double check;
0161       bool all_correct = false;
0162       while (ifs >> check)
0163         all_correct = (check == Approx(*(iter++))) || all_correct;
0164       CHECK( all_correct );
0165 
0166       // verify that all grid elements were checked
0167       const auto* grid_end = event.reduced_thickness_grid().origin() +
0168                              event.reduced_thickness_grid().num_elements();
0169       CHECK( iter == grid_end );
0170     }
0171 
0172     {
0173       // check event number header in second file
0174       fs::ifstream ifs{temp.path/"27.dat"};
0175       std::string line;
0176       std::getline(ifs, line);
0177       CHECK( line == "# event 27" );
0178     }
0179 
0180     // attempting to output to the same directory again should throw an error
0181     CHECK_THROWS_AS( Output{output_var_map}, std::runtime_error );
0182   }
0183 
0184 #ifdef TRENTO_HDF5
0185   SECTION( "hdf5 only" ) {
0186     auto nev = 10;
0187 
0188     // configure for writing a random hdf5 file
0189     temporary_path temp{".hdf5"};
0190 
0191     auto output_var_map = make_var_map({
0192       {"quiet", true},
0193       {"number-events", nev},
0194       {"output", temp.path}
0195     });
0196     Output output{output_var_map};
0197 
0198     for (auto n = 0; n < nev; ++n)
0199       output(n, b, event);
0200 
0201     {
0202       H5::H5File file{temp.path.string(), H5F_ACC_RDONLY};
0203       CHECK( static_cast<int>(file.getNumObjs()) == nev );
0204 
0205       auto name = file.getObjnameByIdx(0);
0206       CHECK( name == "event_0" );
0207 
0208       auto dataset = file.openDataSet(name);
0209 
0210       // read back in the event grid to another array
0211       Event::Grid grid_check{event.reduced_thickness_grid()};
0212       dataset.read(grid_check.data(), H5::PredType::NATIVE_DOUBLE);
0213 
0214       // verify each grid element
0215       auto grid_correct = std::equal(
0216         grid_check.origin(),
0217         grid_check.origin() + grid_check.num_elements(),
0218         event.reduced_thickness_grid().origin(),
0219         [](const double& value_check, const double& value) {
0220           return value_check == Approx(value);
0221         }
0222       );
0223       CHECK( grid_correct );
0224 
0225       // verify attributes
0226       double double_check;
0227       int int_check;
0228 
0229       dataset.openAttribute("b").read(H5::PredType::NATIVE_DOUBLE, &double_check);
0230       CHECK( double_check == Approx(b) );
0231 
0232       dataset.openAttribute("npart").read(H5::PredType::NATIVE_INT, &int_check);
0233       CHECK( int_check == event.npart() );
0234 
0235       dataset.openAttribute("mult").read(H5::PredType::NATIVE_DOUBLE, &double_check);
0236       CHECK( double_check == Approx(event.multiplicity()) );
0237 
0238       for (const auto& ecc : event.eccentricity()) {
0239         dataset.openAttribute("e" + std::to_string(ecc.first))
0240           .read(H5::PredType::NATIVE_DOUBLE, &double_check);
0241         CHECK( double_check == Approx(ecc.second) );
0242       }
0243 
0244 #if H5_VERSION_GE(1, 8, 14)  // causes memory leak on earlier versions
0245       CHECK( dataset.getNumAttrs() == 7 );
0246 #endif
0247     }
0248 
0249     // attempting to output to the same file again should throw an error
0250     CHECK_THROWS_AS( Output{output_var_map}, std::runtime_error );
0251 
0252     // create another empty temporary file
0253     temporary_path temp2{".hdf5"};
0254     {
0255       fs::ofstream{temp2.path};
0256     }
0257 
0258     // should be able to write to an existing but empty file
0259     Output{
0260       make_var_map({
0261         {"quiet", true},
0262         {"number-events", 1},
0263         {"output", temp2.path}
0264       })
0265     }(0, b, event);
0266 
0267     CHECK( fs::file_size(temp2.path) > 0);
0268   }
0269 #endif  // TRENTO_HDF5
0270 }