Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
test_output.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file test_output.cxx
1 // TRENTO: Reduced Thickness Event-by-event Nuclear Topology
2 // Copyright 2015 Jonah E. Bernhard, J. Scott Moreland
3 // MIT License
4 
5 #include "../src/output.h"
6 
7 #include "catch.hpp"
8 #include "util.h"
9 
10 #include <boost/filesystem.hpp>
11 #include <boost/filesystem/fstream.hpp>
12 
13 #include "../src/event.h"
14 #include "../src/hdf5_utils.h"
15 #include "../src/nucleus.h"
16 
17 using namespace trento;
18 
19 TEST_CASE( "output" ) {
20  auto var_map = make_var_map({
21  {"normalization", 1.},
22  {"reduced-thickness", 0.},
23  {"grid-max", 9.},
24  {"grid-step", 0.3},
25  {"fluctuation", 1.},
26  {"cross-section", 6.4},
27  {"nucleon-width", 0.5}
28  });
29 
30  // create a test event
31  Event event{var_map};
32  NucleonProfile profile{var_map};
33 
34  auto nucleusA = Nucleus::create("Pb");
35  auto nucleusB = Nucleus::create("Pb");
36 
37  auto b = 4.*std::sqrt(random::canonical<>());
38  nucleusA->sample_nucleons(+.5*b);
39  nucleusB->sample_nucleons(-.5*b);
40 
41  for (auto&& A : *nucleusA)
42  for (auto&& B : *nucleusB)
43  profile.participate(A, B);
44 
45  event.compute(*nucleusA, *nucleusB, profile);
46 
47  SECTION( "no output" ) {
48  capture_stdout capture;
49  Output output{make_var_map({{"quiet", true}, {"number-events", 1}})};
50  output(1, b, event);
51  CHECK( capture.stream.str().empty() ); // stdout should be empty
52  }
53 
54  SECTION( "stdout only" ) {
55  // write event 0 to stdout for the given number of events
56  auto first_line = [&b, &event](int nev) {
57  capture_stdout capture;
58  Output output{make_var_map({{"quiet", false}, {"number-events", nev}})};
59  output(0, b, event);
61  std::getline(capture.stream, line);
62  return line;
63  };
64 
65  // verify event number padding
66  CHECK( first_line(1).substr(0, 1) == "0" );
67  CHECK( first_line(10).substr(0, 1) == "0" );
68  CHECK( first_line(11).substr(0, 2) == " 0" );
69  CHECK( first_line(100).substr(0, 2) == " 0" );
70  CHECK( first_line(101).substr(0, 3) == " 0" );
71 
72  // output lines for different total event numbers should be identical except
73  // for the padding
74  CHECK( first_line(1).substr(1) == first_line(1000).substr(3) );
75 
76  // read an output line back into separate objects
77  int num, npart;
78  double impact, mult, e2, e3, e4, e5;
79  char end;
80  {
81  capture_stdout capture;
82  Output output{make_var_map({{"quiet", false}, {"number-events", 1}})};
83  output(0, b, event);
84  capture.stream >> num >> impact >> npart >> mult >> e2 >> e3 >> e4 >> e5 >> std::ws;
85  end = capture.stream.get();
86  }
87 
88  // verify output data is correct
89  CHECK( num == 0 );
90  CHECK( impact == Approx(b) );
91  CHECK( npart == event.npart() );
92  CHECK( mult == Approx(event.multiplicity()) );
93  CHECK( e2 == Approx(event.eccentricity().at(2)) );
94  CHECK( e3 == Approx(event.eccentricity().at(3)) );
95  CHECK( e4 == Approx(event.eccentricity().at(4)) );
96  CHECK( e5 == Approx(event.eccentricity().at(5)) );
97 
98  // verify the end character is really the end of file
99  CHECK( end == std::char_traits<char>::eof() );
100  }
101 
102  SECTION( "text and stdout" ) {
103  // configure for writing text files to a random temporary path
104  temporary_path temp{};
105  auto output_var_map = make_var_map({
106  {"quiet", false},
107  {"no-header", false},
108  {"number-events", 50},
109  {"output", temp.path}
110  });
111  Output output{output_var_map};
112 
113  // output directory should have been created
114  CHECK( fs::exists(temp.path) );
115 
116  {
117  // output two events and verify two lines were printed to stdout
118  capture_stdout capture;
119  output(3, b, event);
120  output(27, b, event);
121  int n = 0;
123  while (std::getline(capture.stream, line)) { ++n; }
124  CHECK( n == 2 );
125  }
126 
127  // verify event files were created
128  CHECK( fs::exists(temp.path/"03.dat") );
129  CHECK( fs::exists(temp.path/"27.dat") );
130 
131  {
132  // read event file back in
133  fs::ifstream ifs{temp.path/"03.dat"};
135 
136  // check header
137  std::getline(ifs, line);
138  CHECK( line == "# event 3" );
139 
140  std::getline(ifs, line);
141  CHECK( line.substr(0, 10) == "# b = " );
142  CHECK( std::stod(line.substr(10)) == Approx(b) );
143 
144  std::getline(ifs, line);
145  CHECK( line.substr(0, 10) == "# npart = " );
146  CHECK( std::stoi(line.substr(10)) == event.npart() );
147 
148  std::getline(ifs, line);
149  CHECK( line.substr(0, 10) == "# mult = " );
150  CHECK( std::stod(line.substr(10)) == Approx(event.multiplicity()) );
151 
152  for (const auto& ecc : event.eccentricity()) {
153  std::getline(ifs, line);
154  CHECK( line.substr(0, 10) == ("# e" + std::to_string(ecc.first) + " = ") );
155  CHECK( std::stod(line.substr(10)) == Approx(ecc.second) );
156  }
157 
158  // read the grid back in and check each element
159  const auto* iter = event.reduced_thickness_grid().origin();
160  double check;
161  bool all_correct = false;
162  while (ifs >> check)
163  all_correct = (check == Approx(*(iter++))) || all_correct;
164  CHECK( all_correct );
165 
166  // verify that all grid elements were checked
167  const auto* grid_end = event.reduced_thickness_grid().origin() +
168  event.reduced_thickness_grid().num_elements();
169  CHECK( iter == grid_end );
170  }
171 
172  {
173  // check event number header in second file
174  fs::ifstream ifs{temp.path/"27.dat"};
176  std::getline(ifs, line);
177  CHECK( line == "# event 27" );
178  }
179 
180  // attempting to output to the same directory again should throw an error
181  CHECK_THROWS_AS( Output{output_var_map}, std::runtime_error );
182  }
183 
184 #ifdef TRENTO_HDF5
185  SECTION( "hdf5 only" ) {
186  auto nev = 10;
187 
188  // configure for writing a random hdf5 file
189  temporary_path temp{".hdf5"};
190 
191  auto output_var_map = make_var_map({
192  {"quiet", true},
193  {"number-events", nev},
194  {"output", temp.path}
195  });
196  Output output{output_var_map};
197 
198  for (auto n = 0; n < nev; ++n)
199  output(n, b, event);
200 
201  {
202  H5::H5File file{temp.path.string(), H5F_ACC_RDONLY};
203  CHECK( static_cast<int>(file.getNumObjs()) == nev );
204 
205  auto name = file.getObjnameByIdx(0);
206  CHECK( name == "event_0" );
207 
208  auto dataset = file.openDataSet(name);
209 
210  // read back in the event grid to another array
211  Event::Grid grid_check{event.reduced_thickness_grid()};
212  dataset.read(grid_check.data(), H5::PredType::NATIVE_DOUBLE);
213 
214  // verify each grid element
215  auto grid_correct = std::equal(
216  grid_check.origin(),
217  grid_check.origin() + grid_check.num_elements(),
218  event.reduced_thickness_grid().origin(),
219  [](const double& value_check, const double& value) {
220  return value_check == Approx(value);
221  }
222  );
223  CHECK( grid_correct );
224 
225  // verify attributes
226  double double_check;
227  int int_check;
228 
229  dataset.openAttribute("b").read(H5::PredType::NATIVE_DOUBLE, &double_check);
230  CHECK( double_check == Approx(b) );
231 
232  dataset.openAttribute("npart").read(H5::PredType::NATIVE_INT, &int_check);
233  CHECK( int_check == event.npart() );
234 
235  dataset.openAttribute("mult").read(H5::PredType::NATIVE_DOUBLE, &double_check);
236  CHECK( double_check == Approx(event.multiplicity()) );
237 
238  for (const auto& ecc : event.eccentricity()) {
239  dataset.openAttribute("e" + std::to_string(ecc.first))
240  .read(H5::PredType::NATIVE_DOUBLE, &double_check);
241  CHECK( double_check == Approx(ecc.second) );
242  }
243 
244 #if H5_VERSION_GE(1, 8, 14) // causes memory leak on earlier versions
245  CHECK( dataset.getNumAttrs() == 7 );
246 #endif
247  }
248 
249  // attempting to output to the same file again should throw an error
250  CHECK_THROWS_AS( Output{output_var_map}, std::runtime_error );
251 
252  // create another empty temporary file
253  temporary_path temp2{".hdf5"};
254  {
255  fs::ofstream{temp2.path};
256  }
257 
258  // should be able to write to an existing but empty file
259  Output{
260  make_var_map({
261  {"quiet", true},
262  {"number-events", 1},
263  {"output", temp2.path}
264  })
265  }(0, b, event);
266 
267  CHECK( fs::file_size(temp2.path) > 0);
268  }
269 #endif // TRENTO_HDF5
270 }