Project
Loading...
Searching...
No Matches
test_GeneratorHepMCIndexed.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
23
24#define BOOST_TEST_MODULE Test GeneratorHepMC indexed access
25#define BOOST_TEST_MAIN
26#define BOOST_TEST_DYN_LINK
27#include <boost/test/unit_test.hpp>
28
33
34#include <HepMC3/GenEvent.h>
35#include <HepMC3/GenParticle.h>
36#include <HepMC3/GenRunInfo.h>
37#include <HepMC3/ReaderAscii.h>
38#include <HepMC3/ReaderAsciiHepMC2.h>
39#include <HepMC3/WriterAsciiHepMC2.h>
40
41#include <algorithm>
42#include <cstdio>
43#include <fstream>
44#include <memory>
45#include <cmath>
46#include <numeric>
47#include <string>
48#include <vector>
49
50namespace
51{
54double expectedPx(int i) { return 10. + i; }
55
58std::string writeInput(const std::string& name, int nEvents)
59{
60 std::ofstream out(name);
61 out << "HepMC::Version 3.02.05\n"
62 << "HepMC::Asciiv3-START_EVENT_LISTING\n"
63 << "W Default\n"
64 << "T unit-test|indexed-access|\n";
65 for (int i = 0; i < nEvents; ++i) {
66 const double px = expectedPx(i);
67 out << "E " << i + 1 << " 1 4\n"
68 << "U GEV MM\n"
69 << "W 1.0\n"
70 << "P 1 0 2212 0.000000e+00 0.000000e+00 +6.500000e+03 6.500000e+03 9.383000e-01 4\n"
71 << "P 2 0 2212 0.000000e+00 0.000000e+00 -6.500000e+03 6.500000e+03 9.383000e-01 4\n"
72 << "V -1 0.000000e+00 0.000000e+00 0.000000e+00 [1,2]\n"
73 << "P 3 -1 211 " << px << " 1.000000e+00 1.000000e+00 " << px + 5. << " 1.395700e-01 1\n"
74 << "P 4 -1 -211 " << -px << " -1.000000e+00 -1.000000e+00 " << px + 5. << " 1.395700e-01 1\n";
75 }
76 out << "HepMC::Asciiv3-END_EVENT_LISTING\n";
77 out.close();
78 return name;
79}
80
82std::string writeInputHepMC2(const std::string& asciiv3, const std::string& name)
83{
84 HepMC3::ReaderAscii in(asciiv3);
85 HepMC3::WriterAsciiHepMC2 out(name);
86 while (true) {
87 HepMC3::GenEvent event;
88 in.read_event(event);
89 if (in.failed()) {
90 break;
91 }
92 out.write_event(event);
93 }
94 in.close();
95 out.close();
96 return name;
97}
98
100std::string fingerprint(const HepMC3::GenEvent& event)
101{
102 std::string out = "n=" + std::to_string(event.event_number()) +
103 " np=" + std::to_string(event.particles().size()) +
104 " nv=" + std::to_string(event.vertices().size()) +
105 " nw=" + std::to_string(event.weights().size());
106 char buf[128];
107 for (const auto& p : event.particles()) {
108 snprintf(buf, sizeof buf, " [%d,%d,%.9e,%.9e,%.9e,%.9e]", p->pid(), p->status(),
109 p->momentum().x(), p->momentum().y(), p->momentum().z(), p->momentum().t());
110 out += buf;
111 }
112 return out;
113}
115int announcedEntry(const o2::dataformats::MCEventHeader& header)
116{
117 const std::string key = "forwarding-generator_inputEventNumber";
118 if (!header.hasInfo(key)) {
119 return -1;
120 }
121 bool valid = false;
122 auto entry = header.getInfo<int>(key, valid);
123 return valid ? entry : -1;
124}
125
127int servedEntry(const std::vector<o2::MCTrack>& tracks)
128{
129 auto outgoing = std::find_if(tracks.begin(), tracks.end(),
130 [](const o2::MCTrack& t) { return t.GetPdgCode() == 211; });
131 if (outgoing == tracks.end()) {
132 return -1;
133 }
134 return (int)std::lround(outgoing->Px() - expectedPx(0));
135}
136
138void configure(const std::string& file, int eventsToSkip = 0)
139{
141 "GeneratorFileOrCmd.fileNames=" + file +
142 ";HepMC.randomize=true;HepMC.roundRobin=true;HepMC.reshuffleOnRepeat=false"
143 ";HepMC.rngseed=12345;HepMC.eventsToSkip=" +
144 std::to_string(eventsToSkip));
145}
146
147} // namespace
148
149namespace
150{
153void checkSeekEquivalence(const std::string& file, bool hepmc2)
154{
155 auto makeReader = [hepmc2](std::shared_ptr<std::istream> stream) -> std::shared_ptr<HepMC3::Reader> {
156 if (hepmc2) {
157 return std::make_shared<HepMC3::ReaderAsciiHepMC2>(stream);
158 }
159 return std::make_shared<HepMC3::ReaderAscii>(stream);
160 };
161
162 // read the file from start to end, recording where every event begins
163 std::vector<std::streamoff> offsets;
164 std::vector<std::string> sequential;
165 {
166 auto stream = std::make_shared<std::ifstream>(file);
167 BOOST_REQUIRE(stream->good());
168 auto reader = makeReader(stream);
169 while (true) {
170 auto here = (std::streamoff)stream->tellg();
171 HepMC3::GenEvent event;
172 reader->read_event(event);
173 if (reader->failed()) {
174 break;
175 }
176 offsets.push_back(here);
177 sequential.push_back(fingerprint(event));
178 }
179 }
180 BOOST_REQUIRE_MESSAGE(!offsets.empty(), "no event indexed in " << file);
181
182 // now read them by seeking, backwards, so that every jump goes against the stream
183 auto stream = std::make_shared<std::ifstream>(file);
184 BOOST_REQUIRE(stream->good());
185 auto reader = makeReader(stream);
186 // the run-level header sits ahead of the first event and has to be parsed once
187 HepMC3::GenEvent header;
188 reader->read_event(header);
189 for (int entry = (int)offsets.size() - 1; entry >= 0; --entry) {
190 stream->clear();
191 stream->seekg(offsets[entry]);
192 HepMC3::GenEvent event;
193 reader->read_event(event);
194 BOOST_REQUIRE_MESSAGE(!reader->failed(),
195 file << ": could not read entry " << entry << " by seeking");
196 BOOST_CHECK_MESSAGE(fingerprint(event) == sequential[entry],
197 file << ": entry " << entry << " read by seeking differs from the "
198 << "sequential read; the HepMC3 reader can no longer be "
199 << "repositioned and GeneratorHepMC's indexed access is unsafe");
200 BOOST_CHECK_MESSAGE(event.run_info() != nullptr,
201 file << ": entry " << entry << " lost its GenRunInfo");
202 }
203}
204} // namespace
205
209BOOST_AUTO_TEST_CASE(hepmc3_reader_can_be_seeked)
210{
211 constexpr int nEvents = 20;
212 auto asciiv3 = writeInput("test_GeneratorHepMCIndexed_seek.hepmc", nEvents);
213 auto hepmc2 = writeInputHepMC2(asciiv3, "test_GeneratorHepMCIndexed_seek2.hepmc");
214
215 checkSeekEquivalence(asciiv3, false);
216 checkSeekEquivalence(hepmc2, true);
217
218 std::remove(asciiv3.c_str());
219 std::remove(hepmc2.c_str());
220}
221
227BOOST_AUTO_TEST_CASE(generator_serves_a_permutation)
228{
229 constexpr int nEvents = 25;
230 auto name = writeInput("test_GeneratorHepMCIndexed_gen.hepmc", nEvents);
231 configure(name);
232
234 service.initService("hepmc", "", o2::eventgen::NoVertexOption());
235
236 // two full passes over the file, plus a bit
237 std::vector<int> served;
238 for (int i = 0; i < 2 * nEvents + 5; ++i) {
239 auto event = service.generateEvent();
240 auto entry = servedEntry(event.first);
241 BOOST_REQUIRE_MESSAGE(entry >= 0 && entry < nEvents,
242 "event " << i << " belongs to no entry of the input");
243 // the event handed out has to be the one the generator says it is serving; without
244 // this the index could be wrong by any amount and every other check would still pass
245 BOOST_CHECK_MESSAGE(announcedEntry(event.second) == entry,
246 "event " << i << ": the generator reports entry "
247 << announcedEntry(event.second)
248 << " but handed out the event stored at entry " << entry);
249 served.push_back(entry);
250 }
251
252 // each pass uses every event of the file exactly once ...
253 std::vector<int> all(nEvents);
254 std::iota(all.begin(), all.end(), 0);
255 std::vector<int> pass1(served.begin(), served.begin() + nEvents);
256 std::vector<int> pass2(served.begin() + nEvents, served.begin() + 2 * nEvents);
257 std::vector<int> sorted1 = pass1;
258 std::vector<int> sorted2 = pass2;
259 std::sort(sorted1.begin(), sorted1.end());
260 std::sort(sorted2.begin(), sorted2.end());
261 BOOST_CHECK(sorted1 == all);
262 BOOST_CHECK(sorted2 == all);
263 // ... the events are not simply served in file order ...
264 BOOST_CHECK(pass1 != all);
265 // ... with reshuffleOnRepeat off every pass repeats the first one ...
266 BOOST_CHECK(pass2 == pass1);
267 // ... and roundRobin keeps going past the end of the file
268 BOOST_CHECK(std::equal(served.begin() + 2 * nEvents, served.end(), pass1.begin()));
269
270 std::remove(name.c_str());
271}
272
276BOOST_AUTO_TEST_CASE(generator_honours_events_to_skip)
277{
278 constexpr int nEvents = 25;
279 constexpr int toSkip = 18;
280 auto name = writeInput("test_GeneratorHepMCIndexed_skip.hepmc", nEvents);
281 configure(name, toSkip);
282
284 service.initService("hepmc", "", o2::eventgen::NoVertexOption());
285
286 std::vector<int> served;
287 for (int i = 0; i < 2 * (nEvents - toSkip); ++i) {
288 auto event = service.generateEvent();
289 auto entry = servedEntry(event.first);
290 BOOST_CHECK_MESSAGE(entry >= toSkip && entry < nEvents,
291 "event " << i << " came from entry " << entry
292 << ", outside the requested range [" << toSkip << ", "
293 << nEvents << ")");
294 BOOST_CHECK_MESSAGE(announcedEntry(event.second) == entry,
295 "event " << i << ": the generator reports entry "
296 << announcedEntry(event.second)
297 << " but handed out the event stored at entry " << entry);
298 served.push_back(entry);
299 }
300
301 std::vector<int> usable(nEvents - toSkip);
302 std::iota(usable.begin(), usable.end(), toSkip);
303 std::vector<int> pass1(served.begin(), served.begin() + (nEvents - toSkip));
304 auto sorted = pass1;
305 std::sort(sorted.begin(), sorted.end());
306 BOOST_CHECK(sorted == usable);
307
308 std::remove(name.c_str());
309}
310
313BOOST_AUTO_TEST_CASE(generator_reads_hepmc2)
314{
315 constexpr int nEvents = 25;
316 auto asciiv3 = writeInput("test_GeneratorHepMCIndexed_h2src.hepmc", nEvents);
317 auto name = writeInputHepMC2(asciiv3, "test_GeneratorHepMCIndexed_h2.hepmc");
318 std::remove(asciiv3.c_str());
319 configure(name);
320
322 service.initService("hepmc", "", o2::eventgen::NoVertexOption());
323
324 std::vector<int> served;
325 for (int i = 0; i < nEvents; ++i) {
326 auto event = service.generateEvent();
327 auto entry = servedEntry(event.first);
328 BOOST_REQUIRE_MESSAGE(entry >= 0 && entry < nEvents,
329 "event " << i << " belongs to no entry of the HepMC2 input");
330 BOOST_CHECK_MESSAGE(announcedEntry(event.second) == entry,
331 "event " << i << ": the generator reports entry "
332 << announcedEntry(event.second)
333 << " but handed out the event stored at entry " << entry);
334 served.push_back(entry);
335 }
336 std::vector<int> all(nEvents);
337 std::iota(all.begin(), all.end(), 0);
338 auto sorted = served;
339 std::sort(sorted.begin(), sorted.end());
340 BOOST_CHECK(sorted == all);
341 BOOST_CHECK(served != all);
342
343 std::remove(name.c_str());
344}
int32_t i
Definition of the MCTrack class.
bool valid
StringRef key
static void updateFromString(std::string const &)
bool hasInfo(std::string const &key) const
const T & getInfo(std::string const &key, bool &isvalid) const
A class offering convenient generator configuration and encapsulation of lower level classes....
std::pair< std::vector< MCTrack >, o2::dataformats::MCEventHeader > generateEvent()
void initService(std::string const &generatorName, std::string const &triggerName, VertexOption const &vtxOption)
struct _cl_event * event
Definition glcorearb.h:2982
GLuint entry
Definition glcorearb.h:5735
GLuint GLsizei const GLuint const GLintptr * offsets
Definition glcorearb.h:2595
GLuint const GLchar * name
Definition glcorearb.h:781
GLuint GLuint stream
Definition glcorearb.h:1806
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glcorearb.h:2514
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
const int nEvents
Definition test_Fifo.cxx:27
BOOST_AUTO_TEST_CASE(hepmc3_reader_can_be_seeked)
BOOST_CHECK(tree)