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}
std::vector< std::string > header
int32_t i
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines the history is shortened to this when it reaches twice the size Time in seconds an info message fades out after setInfoTime h OpenGL display display Font size Do not use Freetype for font Renderer for event Frontend to use for event ROOT macros used as track filter Console script file
std::vector< o2::its::TrackITS > tracks
Definition of the MCTrack class.
bool valid
StringRef key
static void updateFromString(std::string 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)