Project
Loading...
Searching...
No Matches
GeneratorHepMC.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 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
13
18#include "SimConfig/SimConfig.h"
19#include "HepMC3/ReaderFactory.h"
20#include "HepMC3/ReaderAscii.h"
21#include "HepMC3/ReaderAsciiHepMC2.h"
22#include "HepMC3/GenEvent.h"
23#include "HepMC3/GenParticle.h"
24#include "HepMC3/GenVertex.h"
25#include "HepMC3/FourVector.h"
26#include "HepMC3/Version.h"
27#include "TParticle.h"
28#include "TRandom.h"
29
30#include <fairlogger/Logger.h>
31#include "FairPrimaryGenerator.h"
32#include <algorithm>
33#include <cmath>
34#include <fstream>
35#include <numeric>
36#include <sstream>
37
38namespace o2
39{
40namespace eventgen
41{
42
43/*****************************************************************/
44/*****************************************************************/
45
47 : GeneratorHepMC("ALICEo2", "ALICEo2 HepMC Generator")
48{
49}
50
51/*****************************************************************/
52
53GeneratorHepMC::GeneratorHepMC(const Char_t* name, const Char_t* title)
54 : Generator(name, title)
55{
58 mEvent = new HepMC3::GenEvent();
59 mInterface = reinterpret_cast<void*>(mEvent);
60 mInterfaceName = "hepmc";
61}
62
63/*****************************************************************/
64
66{
68 LOG(info) << "Destructing GeneratorHepMC";
69 if (mReader) {
70 mReader->close();
71 }
72 if (mEvent) {
73 delete mEvent;
74 }
75 stop();
76 removeTemp();
77}
78
79/*****************************************************************/
80
82{
83 if (mCmd.empty()) {
84 return;
85 }
86 // Close our end of the pipe first: a generator still blocked writing to it
87 // then sees EPIPE and exits promptly, instead of sitting out the whole grace
88 // period and being killed - which would lose its CPU time (see terminateCmd).
89 if (mReader) {
90 mReader->close();
91 }
92 // Must be executed before removing the temporary file
93 // otherwise the current child process might still be writing on it
94 // causing unwanted stdout messages which could slow down the system
96}
97
98/*****************************************************************/
100 const HepMCGenConfig& param,
101 const conf::SimConfig& config)
102{
103 GeneratorFileOrCmd::setup(param0, config);
105}
106
107/*****************************************************************/
109 const HepMCGenConfig& param,
110 const conf::SimConfig& config)
111{
112 GeneratorFileOrCmd::setup(param0, config);
114}
115
116/*****************************************************************/
117
119{
120 if (not param.fileName.empty()) {
121 LOG(warn) << "The use of the key \"HepMC.fileName\" is "
122 << "deprecated, use \"GeneratorFileOrCmd.fileNames\" instead";
123 setFileNames(param.fileName);
124 }
125
126 mVersion = param.version;
127 mPrune = param.prune;
128 mRandomize = param.randomize;
129 mRoundRobin = param.roundRobin;
130 mReshuffleOnRepeat = param.reshuffleOnRepeat;
131 mRngSeed = param.rngseed;
132 setEventsToSkip(param.eventsToSkip);
133
134 // we are skipping ahead with this method only in sequential mode
135 // check establishEventOrder for the random mode
136 if (not(mRandomize or mRoundRobin)) {
137 for (uint64_t i = 0; i < mEventsToSkip; ++i) {
139 }
140 }
141
142 if (param.version != 0 and mCmd.empty()) {
143 LOG(warn) << "The key \"HepMC.version\" is no longer needed when "
144 << "reading from files. The format version of the input files "
145 << "are automatically deduced. However, it is mandatory when reading "
146 << "from a pipe containing HepMC2 data.";
147 }
148}
149
150/*****************************************************************/
152{
153 // when the events are not simply served in the order they appear in the file,
154 // the entry to read is taken from the order established in Init
155 if (mRandomize or mRoundRobin) {
156 return generateEventOrdered();
157 }
158
159 LOG(debug) << "Generating an event";
161 int tries = 0;
162 constexpr int max_tries = 3;
163 do {
164 LOG(debug) << " try # " << tries;
165 if (not mReader and not makeReader()) {
166 return false;
167 }
168
170 mEvent->clear();
171 mReader->read_event(*mEvent);
172 if (not mReader->failed()) {
174 mEvent->set_units(HepMC3::Units::GEV, HepMC3::Units::MM);
175 LOG(debug) << "Read one event " << mEvent->event_number();
176 return true;
177 } else {
178 LOG(error) << "Event reading from HepMC failed ...";
179 }
180 tries++;
181 } while (tries < max_tries);
182
183 LOG(error) << "HepMC event gen failed (Does the file/stream have enough events)?";
184
186 return false;
187}
188
189/*****************************************************************/
191{
192 HepMC3::GenEvent& event = *mEvent;
193
194 auto particles = event.particles();
195 auto vertices = event.vertices();
196 std::list<HepMC3::GenParticlePtr> toRemove;
197
198 LOG(debug) << "HepMC events has " << particles.size()
199 << " particles and " << vertices.size()
200 << " vertices" << std::endl;
201
202 size_t nSelect = 0;
203 for (size_t i = 0; i < particles.size(); ++i) {
204 auto particle = particles[i];
205 if (select(particle)) {
206 nSelect++;
207 continue;
208 }
209
210 // Remove particle from the event
211 toRemove.push_back(particle);
212 LOG(debug) << " Remove " << std::setw(3) << particle->id();
213
214 auto endVtx = particle->end_vertex();
215 auto prdVtx = particle->production_vertex();
216 if (endVtx) {
217 // Disconnect this particle from its out going vertex
218 endVtx->remove_particle_in(particle);
219 LOG(debug) << " end " << std::setw(3) << endVtx->id();
220
221 if (prdVtx and prdVtx->id() != endVtx->id()) {
222 auto outbound = endVtx->particles_out();
223 auto inbound = endVtx->particles_in();
224 LOG(debug) << " prd " << std::setw(3) << prdVtx->id() << " "
225 << std::setw(3) << outbound.size() << " out "
226 << " "
227 << std::setw(3) << inbound.size() << " in ";
228
229 // Other out-bound particles of the end vertex are attached as
230 // out-going to the production vertex of this particle.
231 for (auto outgoing : outbound) {
232 // This should also detach the particle from its old
233 // end-vertex.
234 if (outgoing) {
235 auto ee = outgoing->end_vertex();
236 if (not ee or ee->id() != prdVtx->id()) {
237 prdVtx->add_particle_out(outgoing);
238 }
239 LOG(debug) << " " << std::setw(3) << outgoing->id();
240 }
241 }
242
243 // Other incoming particles to the end vertex of this
244 // particles are attached incoming particles to the production
245 // vertex of this particle.
246 for (auto incoming : inbound) {
247 if (incoming) {
248 auto pp = incoming->production_vertex();
249 if (not pp or pp->id() != prdVtx->id()) {
250 prdVtx->add_particle_in(incoming);
251 }
252
253 LOG(debug) << " " << std::setw(3) << incoming->id();
254 }
255 }
256 }
257 }
258 if (prdVtx) {
259 prdVtx->remove_particle_out(particle);
260 }
261 }
262
263 LOG(debug) << "Selected " << nSelect << " particles\n"
264 << "Removing " << toRemove.size() << " particles";
265 size_t oldSize = particles.size();
266 for (auto particle : toRemove) {
267 event.remove_particle(particle);
268 }
269
270 std::list<HepMC3::GenVertexPtr> remVtx;
271 for (auto vtx : event.vertices()) {
272 if (not vtx or
273 (vtx->particles_out().empty() and
274 vtx->particles_in().empty())) {
275 remVtx.push_back(vtx);
276 }
277 }
278 LOG(debug) << "Removing " << remVtx.size() << " vertexes";
279 for (auto vtx : remVtx) {
280 event.remove_vertex(vtx);
281 }
282
283 LOG(debug) << "HepMC events was pruned from " << oldSize
284 << " particles to " << event.particles().size()
285 << " particles and " << event.vertices().size()
286 << " vertices";
287}
288
289/*****************************************************************/
290
292{
294 if (mPrune) {
295 auto select = [](HepMC3::ConstGenParticlePtr particle) {
296 switch (particle->status()) {
297 case 1: // Final st
298 case 2: // Decayed
299 case 4: // Beam
300 return true;
301 }
302 // To also keep diffractive particles
303 // if (particle->pid() == 9902210) return true;
304 return false;
305 };
307 }
308
310 mParticles.clear();
311 auto particles = mEvent->particles();
312 for (int i = 0; i < particles.size(); ++i) {
313
315 auto particle = particles.at(i);
316 auto momentum = particle->momentum();
317 auto vertex = particle->production_vertex()->position();
318 auto parents = particle->parents();
319 auto children = particle->children();
320
322 auto m1 = parents.empty() ? -1 : parents.front()->id() - 1;
323 auto m2 = parents.empty() ? -1 : parents.back()->id() - 1;
324
326 auto d1 = children.empty() ? -1 : children.front()->id() - 1;
327 auto d2 = children.empty() ? -1 : children.back()->id() - 1;
328
330 mParticles.push_back(TParticle(particle->pid(), // Particle type
331 particle->status(), // Status code
332 m1, // First mother
333 m2, // Second mother
334 d1, // First daughter
335 d2, // Last daughter
336 momentum.x(), // X-momentum
337 momentum.y(), // Y-momentum
338 momentum.z(), // Z-momentum
339 momentum.t(), // Energy
340 vertex.x(), // Production X
341 vertex.y(), // Production Y
342 vertex.z(), // Production Z
343 vertex.t())); // Production time
345 mParticles.back(), // Add to back
346 particle->status() == 1); // only final state are to be propagated
347
348 }
351 return kTRUE;
352}
353
354namespace
355{
356template <typename AttributeType, typename TargetType>
357bool putAttributeInfoImpl(o2::dataformats::MCEventHeader* eventHeader,
358 const std::string& name,
359 const std::shared_ptr<HepMC3::Attribute>& a)
360{
361 if (auto* p = dynamic_cast<AttributeType*>(a.get())) {
362 eventHeader->putInfo<TargetType>(name, p->value());
363 return true;
364 }
365 return false;
366}
367
368void putAttributeInfo(o2::dataformats::MCEventHeader* eventHeader,
369 const std::string& name,
370 const std::shared_ptr<HepMC3::Attribute>& a)
371{
372 using IntAttribute = HepMC3::IntAttribute;
373 using LongAttribute = HepMC3::LongAttribute;
374 using FloatAttribute = HepMC3::FloatAttribute;
375 using DoubleAttribute = HepMC3::DoubleAttribute;
376 using StringAttribute = HepMC3::StringAttribute;
377 using CharAttribute = HepMC3::CharAttribute;
378 using LongLongAttribute = HepMC3::LongLongAttribute;
379 using LongDoubleAttribute = HepMC3::LongDoubleAttribute;
380 using UIntAttribute = HepMC3::UIntAttribute;
381 using ULongAttribute = HepMC3::ULongAttribute;
382 using ULongLongAttribute = HepMC3::ULongLongAttribute;
383 using BoolAttribute = HepMC3::BoolAttribute;
384
385 if (putAttributeInfoImpl<IntAttribute, int>(eventHeader, name, a)) {
386 return;
387 }
388 if (putAttributeInfoImpl<LongAttribute, int>(eventHeader, name, a)) {
389 return;
390 }
391 if (putAttributeInfoImpl<FloatAttribute, float>(eventHeader, name, a)) {
392 return;
393 }
394 if (putAttributeInfoImpl<DoubleAttribute, float>(eventHeader, name, a)) {
395 return;
396 }
397 if (putAttributeInfoImpl<StringAttribute, std::string>(eventHeader, name, a)) {
398 return;
399 }
400 if (putAttributeInfoImpl<CharAttribute, char>(eventHeader, name, a)) {
401 return;
402 }
403 if (putAttributeInfoImpl<LongLongAttribute, int>(eventHeader, name, a)) {
404 return;
405 }
406 if (putAttributeInfoImpl<LongDoubleAttribute, float>(eventHeader, name, a)) {
407 return;
408 }
409 if (putAttributeInfoImpl<UIntAttribute, int>(eventHeader, name, a)) {
410 return;
411 }
412 if (putAttributeInfoImpl<ULongAttribute, int>(eventHeader, name, a)) {
413 return;
414 }
415 if (putAttributeInfoImpl<ULongLongAttribute, int>(eventHeader, name, a)) {
416 return;
417 }
418 if (putAttributeInfoImpl<BoolAttribute, bool>(eventHeader, name, a)) {
419 return;
420 }
421}
422} // namespace
423
424/*****************************************************************/
425
427{
430
431 eventHeader->putInfo<std::string>(Key::generator, "hepmc");
432 eventHeader->putInfo<int>(Key::generatorVersion, HEPMC3_VERSION_CODE);
433
434 auto xSection = mEvent->cross_section();
435 auto pdfInfo = mEvent->pdf_info();
436 auto hiInfo = mEvent->heavy_ion();
437
438 // Workaround for a bug in HepMC3 (3.3.1 on 23/02/2026): GenHeavyIon::from_string() for the "v0"
439 // format skips reading user_cent_estimate, but to_string() always writes it.
440 // This shifts all subsequent fields by one, causing a istringstream failure and and heavy_ion()
441 // to return null even when the attribute is present and well-formed.
442 // For now we use this manual parser in case the infos are available
443 if (!hiInfo) {
444 auto attStr = mEvent->attribute_as_string("GenHeavyIon");
445 if (!attStr.empty() && attStr[0] == 'v') {
446 std::istringstream is(attStr);
447 std::string version;
448 is >> version;
449 if (version == "v0") {
450 auto hi = std::make_shared<HepMC3::GenHeavyIon>();
451 double spectNeutrons, spectProtons, eccentricity, userCentEst;
452 is >> hi->Ncoll_hard >> hi->Npart_proj >> hi->Npart_targ >> hi->Ncoll >> spectNeutrons >> spectProtons // deprecated v0 fields
453 >> hi->N_Nwounded_collisions >> hi->Nwounded_N_collisions >> hi->Nwounded_Nwounded_collisions >> hi->impact_parameter >> hi->event_plane_angle >> eccentricity // deprecated v0 field
454 >> hi->sigma_inel_NN >> hi->centrality >> userCentEst // GenHeavyIon::to_string always writes this, but GenHeavyIon::from_string skips it for v0 (HepMC3 bug to fix)
455 >> hi->Nspec_proj_n >> hi->Nspec_targ_n >> hi->Nspec_proj_p >> hi->Nspec_targ_p;
456 if (!is.fail()) {
457 LOG(debug) << "GenHeavyIon: using manual v0 parser (workaround for HepMC3 from_string bug)";
458 hiInfo = hi;
459 } else {
460 LOG(warn) << "GenHeavyIon: manual v0 parser also failed on: [" << attStr << "]";
461 }
462 }
463 }
464 }
465
466 // Set default cross-section
467 if (xSection) {
468 eventHeader->putInfo<float>(Key::xSection, xSection->xsec());
469 eventHeader->putInfo<float>(Key::xSectionError, xSection->xsec_err());
470 eventHeader->putInfo<int>(Key::acceptedEvents,
471 xSection->get_accepted_events());
472 eventHeader->putInfo<int>(Key::attemptedEvents,
473 xSection->get_attempted_events());
474 }
475
476 // Set weights and cross sections
477 size_t iw = 0;
478 for (auto w : mEvent->weights()) {
479 std::string post = (iw > 0 ? "_" + std::to_string(iw) : "");
480 eventHeader->putInfo<float>(Key::weight + post, w);
481 if (xSection) {
482 eventHeader->putInfo<float>(Key::xSection, xSection->xsec(iw));
483 eventHeader->putInfo<float>(Key::xSectionError, xSection->xsec_err(iw));
484 }
485 iw++;
486 }
487
488 // Set the PDF information
489 if (pdfInfo) {
490 eventHeader->putInfo<int>(Key::pdfParton1Id, pdfInfo->parton_id[0]);
491 eventHeader->putInfo<int>(Key::pdfParton2Id, pdfInfo->parton_id[1]);
492 eventHeader->putInfo<float>(Key::pdfX1, pdfInfo->x[0]);
493 eventHeader->putInfo<float>(Key::pdfX2, pdfInfo->x[1]);
494 eventHeader->putInfo<float>(Key::pdfScale, pdfInfo->scale);
495 eventHeader->putInfo<float>(Key::pdfXF1, pdfInfo->xf[0]);
496 eventHeader->putInfo<float>(Key::pdfXF2, pdfInfo->xf[1]);
497 eventHeader->putInfo<int>(Key::pdfCode1, pdfInfo->pdf_id[0]);
498 eventHeader->putInfo<int>(Key::pdfCode2, pdfInfo->pdf_id[1]);
499 }
500
501 // Set heavy-ion information
502 if (hiInfo) {
503 eventHeader->SetB(hiInfo->impact_parameter); // sets the impact parameter to the FairMCEventHeader field for quick access in the AO2D
504 eventHeader->putInfo<float>(Key::impactParameter,
505 hiInfo->impact_parameter);
506 eventHeader->putInfo<int>(Key::nPart,
507 hiInfo->Npart_proj + hiInfo->Npart_targ);
508 eventHeader->putInfo<int>(Key::nPartProjectile, hiInfo->Npart_proj);
509 eventHeader->putInfo<int>(Key::nPartTarget, hiInfo->Npart_targ);
510 eventHeader->putInfo<int>(Key::nColl, hiInfo->Ncoll);
511 eventHeader->putInfo<int>(Key::nCollHard, hiInfo->Ncoll_hard);
512 eventHeader->putInfo<int>(Key::nCollNNWounded,
513 hiInfo->N_Nwounded_collisions);
514 eventHeader->putInfo<int>(Key::nCollNWoundedN,
515 hiInfo->Nwounded_N_collisions);
516 eventHeader->putInfo<int>(Key::nCollNWoundedNwounded,
517 hiInfo->Nwounded_Nwounded_collisions);
518 eventHeader->putInfo<double>(Key::planeAngle, hiInfo->event_plane_angle);
519 eventHeader->putInfo<float>(Key::sigmaInelNN, hiInfo->sigma_inel_NN);
520 eventHeader->putInfo<float>(Key::centrality, hiInfo->centrality);
521 eventHeader->putInfo<int>(Key::nSpecProjectileProton, hiInfo->Nspec_proj_p);
522 eventHeader->putInfo<int>(Key::nSpecProjectileNeutron, hiInfo->Nspec_proj_n);
523 eventHeader->putInfo<int>(Key::nSpecTargetProton, hiInfo->Nspec_targ_p);
524 eventHeader->putInfo<int>(Key::nSpecTargetNeutron, hiInfo->Nspec_targ_n);
525 }
526
527 for (auto na : mEvent->attributes()) {
528 std::string name = na.first;
529 if (name == "GenPdfInfo" ||
530 name == "GenCrossSection" ||
531 name == "GenHeavyIon") {
532 continue;
533 }
534
535 for (auto ia : na.second) {
536 int no = ia.first;
537 auto at = ia.second;
538 std::string post = (no == 0 ? "" : std::to_string(no));
539
540 putAttributeInfo(eventHeader, name + post, at);
541 }
542 }
543
544 // When randomised is enabled, the header comes from the last served event
545 if (mRandomize or mRoundRobin) {
546 eventHeader->putInfo<std::string>("forwarding-generator", "generatorHepMC");
547 eventHeader->putInfo<std::string>("forwarding-generator_inputFile", mCurrentFileName);
548 eventHeader->putInfo<int>("forwarding-generator_inputEventNumber", mLastEntryRead);
549 }
550}
551
552/*****************************************************************/
553
555{
556 // Reset the reader smart pointer
557 LOG(debug) << "Reseting the reader";
558 mReader.reset();
559
560 // Check that we have any file names left
561 if (mFileNames.size() < 1) {
562 LOG(debug) << "No more files to read, return false";
563 return false;
564 }
565
566 // If we have file names left, pop the top of the list (LIFO)
567 auto filename = mFileNames.front();
568 mFileNames.pop_front();
569
570 LOG(debug) << "Next file to read: \"" << filename << "\" "
571 << mFileNames.size() << " left";
572
573 if (not mCmd.empty()) {
574 // For FIFO reading, we assume straight ASCII output always.
575 // Unfortunately, the HepMC3::deduce_reader `stat`s the filename
576 // which isn't supported on a FIFO, so we have to use the reader
577 // directly. Here, we allow for version 2 formats if the user
578 // specifies that
579 LOG(info) << "Creating ASCII reader of " << filename;
580 if (mVersion == 2) {
581 mReader = std::make_shared<HepMC3::ReaderAsciiHepMC2>(filename);
582 } else {
583 mReader = std::make_shared<HepMC3::ReaderAscii>(filename);
584 }
585 } else {
586 LOG(info) << "Deduce a reader of " << filename;
587 mReader = HepMC3::deduce_reader(filename);
588 }
589
590 bool ret = bool(mReader) and not mReader->failed();
591 LOG(info) << "Reader is " << mReader.get() << " " << ret;
592 return ret;
593}
594
595/*****************************************************************/
596
597bool GeneratorHepMC::buildIndex(const std::string& filename)
598{
599 // Going through the file once to know how many events it holds and to
600 // record where each of them starts. This way a single
601 // seek is performed instead of a scan from the current position
602 mEventOffsets.clear();
603 mIndexedStream.reset();
604 mIndexedHepMC2 = false;
605
606 HepMC3::InputInfo info(filename);
607 if (info.m_error or info.m_remote or info.m_pipe or
608 not(info.m_asciiv3 or info.m_iogenevent)) {
609 return false;
610 }
611 mIndexedHepMC2 = info.m_iogenevent;
612
613 auto stream = std::make_shared<std::ifstream>(filename);
614 if (not stream->good()) {
615 LOG(error) << "Could not open " << filename << " to index its events";
616 return false;
617 }
618 std::shared_ptr<HepMC3::Reader> reader;
619 if (mIndexedHepMC2) {
620 reader = std::make_shared<HepMC3::ReaderAsciiHepMC2>(stream);
621 } else {
622 reader = std::make_shared<HepMC3::ReaderAscii>(stream);
623 }
624 if (not reader or reader->failed()) {
625 LOG(error) << "Could not open " << filename << " to index its events";
626 return false;
627 }
628
629 // the offsets come from the parser itself rather than from guessing at line prefixes
630 constexpr int max_events = 100000000;
631 HepMC3::GenEvent event;
632 while ((int)mEventOffsets.size() < max_events) {
633 auto here = (std::streamoff)stream->tellg();
634 event.clear();
635 reader->read_event(event);
636 if (reader->failed()) {
637 break;
638 }
639 mEventOffsets.push_back(here);
640 }
641 if ((int)mEventOffsets.size() >= max_events) {
642 LOG(warn) << "Stopped indexing the events of " << filename << " at " << max_events;
643 }
644 if (mEventOffsets.empty()) {
645 LOG(error) << "No event found in HepMC file " << filename;
646 return false;
647 }
648
649 // keep the reader and its stream: serving an entry is now a seek plus a read.
652 mReader = reader;
653 mLastEntryRead = -1;
654 LOG(info) << "Indexed " << mEventOffsets.size() << " events of " << filename;
655 return true;
656}
657
658/*****************************************************************/
659
661{
662 // The entry starts at a byte offset recorded by buildIndex
663 if (entry < 0 or entry >= (int)mEventOffsets.size() or not mIndexedStream or not mReader) {
664 LOG(error) << "No entry " << entry << " in " << mCurrentFileName;
665 return false;
666 }
667 mIndexedStream->clear();
669
671 mEvent->clear();
672 mReader->read_event(*mEvent);
673 if (mReader->failed()) {
674 LOG(error) << "Reading entry " << entry << " of " << mCurrentFileName << " failed";
675 return false;
676 }
678 mEvent->set_units(HepMC3::Units::GEV, HepMC3::Units::MM);
680 LOG(debug) << "Read one event " << mEvent->event_number();
681 return true;
682}
683
684/*****************************************************************/
685
687{
688 // Decide the order in which the entries of the input file are served
689 // The events to skip at the start of the file are left out of the read
690 auto first = (int)std::min<uint64_t>(mEventsToSkip, (uint64_t)std::max(mEventsAvailable, 0));
691 mEventOrder.resize(std::max(mEventsAvailable, 0) - first);
692 std::iota(mEventOrder.begin(), mEventOrder.end(), first);
693 if (mRandomize) {
694 // Shuffle based on the ROOT random generator
695 for (int i = (int)mEventOrder.size() - 1; i > 0; --i) {
696 auto j = (int)gRandom->Integer(i + 1);
697 std::swap(mEventOrder[i], mEventOrder[j]);
698 }
699 }
700}
701
702/*****************************************************************/
703
705{
706 // The entry to be read is fixed by the event order established at file opening
707 if (mEventCounter >= (int)mEventOrder.size()) {
708 if (not mRoundRobin) {
709 auto requested = getTotalNEvents();
710 LOG(fatal) << "GeneratorHepMC: ran out of events after " << mEventsServed
711 << " event(s) from " << mCurrentFileName
712 << (requested > 0 ? " (" + std::to_string(requested) + " were requested)" : "")
713 << ". Provide more events or allow reusing them via roundRobin";
714 return false;
715 }
716 // start over from the beginning of the file, with a fresh order if requested
717 LOG(info) << "GeneratorHepMC - Reached the end of the input; reusing its events";
718 mEventCounter = 0;
719 if (mReshuffleOnRepeat) {
721 }
722 }
723 if (mEventOrder.empty()) {
724 LOG(error) << "GeneratorHepMC: no usable event in " << mCurrentFileName;
725 return false;
726 }
727
729 if (mRandomize) {
730 LOG(info) << "GeneratorHepMC - Picking event " << entry;
731 }
732 if (not readEntry(entry)) {
733 return false;
734 }
737 return true;
738}
739
740/*****************************************************************/
741
743{
748
749 // If a EG command line is given, then we make a fifo on a temporary
750 // file, and directs the EG to write to that fifo. We will then set
751 // up the HepMC3 reader to read from that fifo.
752 //
753 // o2-sim -g hepmc --configKeyValues "HepMC.progCmd=<cmd>" ...
754 //
755 // where <cmd> is the command line to run an event generator. The
756 // event generator should output HepMC event records to standard
757 // output. Nothing else, but the HepMC event record may be output
758 // to standard output. If the EG has other output to standard
759 // output, then a filter can be set-up. For example
760 //
761 // crmc -n 3 -o hepmc3 -c /optsw/inst/etc/crmc.param -f /dev/stdout \
762 // | sed -n 's/^\‍(HepMC::\|[EAUWVP] \‍)/\1/p'
763 //
764 // What's more, the event generator program _must_ accept the
765 // following command line argument
766 //
767 // `-n NEVENTS` to set the number of events to produce.
768 //
769 // Optionally, the command line should also accept
770 //
771 // `-s SEED` to set the random number seed
772 // `-b FM` to set the maximum impact parameter to sample
773 // `-o OUTPUT` to set the output file name
774 //
775 // All of this can conviniently be achieved via a wrapper script
776 // around the actual EG program.
777 if (not mCmd.empty()) {
778 if (mFileNames.empty()) {
779 // Set filename to be a temporary name
780 if (not makeTemp(false)) {
781 return false;
782 }
783 } else {
784 // Use the first filename as output for cmd line
785 if (not makeTemp(true)) {
786 return false;
787 }
788 }
789
790 // Make a fifo
791 if (not makeFifo()) {
792 return false;
793 }
794
795 // Build command line, rediret stdout to our fifo and put
796 std::string cmd = makeCmdLine();
797 LOG(debug) << "EG command line is \"" << cmd << "\"";
798
799 // Execute the command line
800 if (not executeCmdLine(cmd)) {
801 LOG(fatal) << "Failed to spawn \"" << cmd << "\"";
802 return false;
803 }
804 } else {
805 // If no command line was given, ensure that all files are present
806 // on the system. Note, in principle, HepMC3 can read from remote
807 // files
808 //
809 // root:// XRootD served
810 // http[s]:// Web served
811 // gsidcap:// DCap served
812 //
813 // These will all be handled in HepMC3 via ROOT's TFile protocol
814 // and the files are assumed to contain a TTree named
815 // `hepmc3_tree` and that tree has the branches
816 //
817 // `hepmc3_event` with object of type `HepMC3::GenEventData`
818 // `GenRunInfo` with object of type `HepMC3::GenRunInfoData`
819 //
820 // where the last branch is optional.
821 //
822 // However, here we will assume system local files. If _any_ of
823 // the listed files do not exist, then we fail.
824 if (not ensureFiles()) {
825 return false;
826 }
827 }
828
829 // Serving the events in random order
830 if (mRandomize or mRoundRobin) {
831 if (not mCmd.empty()) {
832 LOG(fatal) << "HepMC.randomize/HepMC.roundRobin cannot be used when the events "
833 << "come from a command, as the pipe can only be read once";
834 return false;
835 }
836 if (mFileNames.size() != 1) {
837 LOG(fatal) << "HepMC.randomize/HepMC.roundRobin need exactly one input file, but "
838 << mFileNames.size() << " were given";
839 return false;
840 }
841 if (mRngSeed > 0) {
842 // with a zero the seed given to the driver (o2-sim --seed) stays in control
843 gRandom->SetSeed(mRngSeed);
844 }
845 LOG(info) << "GeneratorHepMC: the event order is drawn with gRandom (" << gRandom->ClassName()
846 << ") seeded with " << gRandom->GetSeed();
847
848 auto const& filename = mFileNames.front();
849 // Indexing the file gives us both the number of events and constant-time access
850 // to any of them, and creates the reader we then serve the events from
851 if (not buildIndex(filename)) {
852 LOG(fatal) << "HepMC.randomize/HepMC.roundRobin need an input the events can be "
853 << "picked from in any order, which means a plain HepMC3 or HepMC2 "
854 << "ASCII file; " << filename << " is not one. Convert it, or convert "
855 << "it to O2 kinematics and read it back with -g extkinO2, which "
856 << "randomizes over a TTree";
857 return false;
858 }
860 if (mEventsToSkip >= (uint64_t)mEventsAvailable) {
861 LOG(fatal) << "HepMC.eventsToSkip (" << mEventsToSkip << ") leaves no event of the "
862 << mEventsAvailable << " contained in " << filename;
863 return false;
864 }
866 auto requested = getTotalNEvents();
867 if (requested > 0 and not mRoundRobin and mEventOrder.size() < requested) {
868 LOG(warn) << "This job will request " << requested << " events, but the input holds "
869 << "only " << mEventOrder.size() << " usable event(s). The job will stop "
870 << "with 'ran out of events' - provide more events or enable roundRobin";
871 }
872 LOG(info) << "Reading events from HepMC file " << filename << " (" << mEventsAvailable
873 << " events, " << (mRandomize ? "randomized" : "sequential") << " order)";
874 }
875
876 // Create reader for current (first) file
877 return true;
878}
879
880/*****************************************************************/
881/*****************************************************************/
882
883} /* namespace eventgen */
884} /* namespace o2 */
o2::monitoring::tags::Key Key
std::ostringstream debug
uint64_t vertex
Definition RawEventData.h:9
int32_t i
Utility functions for MC particles.
uint32_t j
Definition RawData.h:0
uint32_t version
Definition RawData.h:8
void putInfo(std::string const &key, T const &value)
void updateHeader(o2::dataformats::MCEventHeader *eventHeader) override
void setEventsToSkip(uint64_t val)
std::vector< int > mEventOrder
std::vector< std::streamoff > mEventOffsets
void setupHepMC(const HepMCGenConfig &param)
std::shared_ptr< std::istream > mIndexedStream
void setup(const GeneratorFileOrCmdParam &param0, const HepMCGenConfig &param, const conf::SimConfig &config)
std::shared_ptr< HepMC3::Reader > mReader
bool buildIndex(const std::string &filename)
std::string mInterfaceName
Definition Generator.h:131
static unsigned int getTotalNEvents()
Definition Generator.h:100
std::vector< TParticle > mParticles
Definition Generator.h:151
Bool_t Init() override
static void encodeParticleStatusAndTracking(TParticle &particle, bool wanttracking=true)
Definition MCUtils.cxx:211
struct _cl_event * event
Definition glcorearb.h:2982
GLuint entry
Definition glcorearb.h:5735
GLuint const GLchar * name
Definition glcorearb.h:781
GLint first
Definition glcorearb.h:399
GLenum GLfloat param
Definition glcorearb.h:271
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLuint GLuint stream
Definition glcorearb.h:1806
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
std::vector< InputSpec > select(char const *matcher="")
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
std::string filename()
void setFileNames(const std::string &filenames)
void setup(const GeneratorFileOrCmdParam &param, const conf::SimConfig &config)
std::list< std::string > mFileNames
static constexpr unsigned int sStopGraceMillis
virtual bool terminateCmd(unsigned int graceMillis=0)
virtual std::string makeCmdLine() const
virtual bool executeCmdLine(const std::string &cmd)
virtual bool makeTemp(const bool &)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"