Skip to content

Commit 40c5d90

Browse files
sawenzelalcaliva
authored andcommitted
New event generator/wrapper facilitating event-pools (#13766)
A new event generator for event-pools. This is a convenience wrapper on top of GeneratorO2Kine which handles event pools better. It can talk to a pool as a collection, instead of only to specific files. In particular it offers functionality to - self-pick a file from a pool - discover available files in a pool be it on AliEn or local - makes it easier to generate generic JSON configs, where users don't need to provide a full file path to use (which would be impractical for productions) (cherry picked from commit 808730c)
1 parent ce17a83 commit 40c5d90

File tree

9 files changed

+467
-3
lines changed

9 files changed

+467
-3
lines changed

Generators/include/Generators/GeneratorFromFile.h

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
#include "FairGenerator.h"
1818
#include "Generators/Generator.h"
1919
#include "Generators/GeneratorFromO2KineParam.h"
20+
#include "SimulationDataFormat/MCEventHeader.h"
2021
#include <TRandom3.h>
21-
#include <TGrid.h>
22+
#include <random>
2223

2324
class TBranch;
2425
class TFile;
2526
class TParticle;
27+
class TGrid;
2628

2729
namespace o2
2830
{
@@ -109,6 +111,52 @@ class GeneratorFromO2Kine : public o2::eventgen::Generator
109111
ClassDefOverride(GeneratorFromO2Kine, 2);
110112
};
111113

114+
/// Special generator for event pools.
115+
/// What do we like to have:
116+
/// - ability to give a file which contains the list of files to read
117+
/// - ability to give directly a file to read the event from
118+
/// - ability to give a pool path and to find the top N list of files closest to myself
119+
/// - ability to select itself one file from the pool
120+
class GeneratorFromEventPool : public o2::eventgen::Generator
121+
{
122+
public:
123+
constexpr static std::string_view eventpool_filename = "evtpool.root";
124+
constexpr static std::string_view alien_protocol_prefix = "alien://";
125+
126+
GeneratorFromEventPool() = default; // mainly for ROOT IO
127+
GeneratorFromEventPool(EventPoolGenConfig const& pars);
128+
129+
bool Init() override;
130+
131+
// the o2 Generator interface methods
132+
bool generateEvent() override
133+
{ /* trivial - actual work in importParticles */
134+
return mO2KineGenerator->generateEvent();
135+
}
136+
bool importParticles() override
137+
{
138+
auto import_good = mO2KineGenerator->importParticles();
139+
// transfer the particles (could be avoided)
140+
mParticles = mO2KineGenerator->getParticles();
141+
return import_good;
142+
}
143+
144+
// determine the collection of available files
145+
std::vector<std::string> setupFileUniverse(std::string const& path) const;
146+
147+
std::vector<std::string> const& getFileUniverse() const { return mPoolFilesAvailable; }
148+
149+
private:
150+
EventPoolGenConfig mConfig; //! Configuration object
151+
std::unique_ptr<o2::eventgen::GeneratorFromO2Kine> mO2KineGenerator = nullptr; //! actual generator doing the work
152+
std::vector<std::string> mPoolFilesAvailable; //! container keeping the collection of files in the event pool
153+
std::string mFileChosen; //! the file chosen for the pool
154+
// random number generator to determine a concrete file name
155+
std::mt19937 mRandomEngine; //!
156+
157+
ClassDefOverride(GeneratorFromEventPool, 1);
158+
};
159+
112160
} // end namespace eventgen
113161
} // end namespace o2
114162

Generators/include/Generators/GeneratorFromO2KineParam.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ struct O2KineGenConfig {
4949
std::string fileName = ""; // filename to read from - takes precedence over SimConfig if given
5050
};
5151

52+
struct EventPoolGenConfig {
53+
std::string eventPoolPath = ""; // In that order: The path where an event pool can be found ;
54+
// or .. a local file containing a list of files to use
55+
// or .. a concrete file path to a kinematics file
56+
bool skipNonTrackable = true; // <--- do we need this?
57+
bool roundRobin = false; // read events with period boundary conditions
58+
bool randomize = true; // randomize the order of events
59+
unsigned int rngseed = 0; // randomizer seed, 0 for random value
60+
bool randomphi = false; // randomize phi angle; rotates tracks in events by some phi-angle
61+
};
62+
63+
// construct a configurable param singleton out of the
64+
struct GeneratorEventPoolParam : public o2::conf::ConfigurableParamPromoter<GeneratorEventPoolParam, EventPoolGenConfig> {
65+
O2ParamDef(GeneratorEventPoolParam, "GeneratorEventPool");
66+
};
67+
5268
} // end namespace eventgen
5369
} // end namespace o2
5470

Generators/include/Generators/GeneratorHybrid.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class GeneratorHybrid : public Generator
6363
private:
6464
o2::eventgen::Generator* currentgen = nullptr;
6565
std::vector<std::unique_ptr<o2::eventgen::Generator>> gens;
66-
const std::vector<std::string> generatorNames = {"extkinO2", "boxgen", "external", "hepmc", "pythia8", "pythia8pp", "pythia8hi", "pythia8hf", "pythia8powheg"};
66+
const std::vector<std::string> generatorNames = {"extkinO2", "evtpool", "boxgen", "external", "hepmc", "pythia8", "pythia8pp", "pythia8hi", "pythia8hf", "pythia8powheg"};
6767
std::vector<std::string> mInputGens;
6868
std::vector<std::string> mGens;
6969
std::vector<std::string> mConfigs;
@@ -73,6 +73,7 @@ class GeneratorHybrid : public Generator
7373
std::vector<std::unique_ptr<o2::eventgen::BoxGenConfig>> mBoxGenConfigs;
7474
std::vector<std::unique_ptr<o2::eventgen::Pythia8GenConfig>> mPythia8GenConfigs;
7575
std::vector<std::unique_ptr<o2::eventgen::O2KineGenConfig>> mO2KineGenConfigs;
76+
std::vector<o2::eventgen::EventPoolGenConfig> mEventPoolConfigs;
7677
std::vector<std::unique_ptr<o2::eventgen::ExternalGenConfig>> mExternalGenConfigs;
7778
std::vector<std::unique_ptr<o2::eventgen::FileOrCmdGenConfig>> mFileOrCmdGenConfigs;
7879
std::vector<std::unique_ptr<o2::eventgen::HepMCGenConfig>> mHepMCGenConfigs;

Generators/src/GeneratorFactory.cxx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,13 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair
175175
}
176176
}
177177
LOG(info) << "using external O2 kinematics";
178+
} else if (genconfig.compare("evtpool") == 0) {
179+
// case of an "event-pool" which is a specialization of extkinO2
180+
// with some additional logic in file management and less configurability
181+
// and not features such as "continue transport"
182+
auto extGen = new o2::eventgen::GeneratorFromEventPool(o2::eventgen::GeneratorEventPoolParam::Instance().detach());
183+
primGen->AddGenerator(extGen);
184+
LOG(info) << "using the eventpool generator";
178185
} else if (genconfig.compare("tparticle") == 0) {
179186
// External ROOT file(s) with tree of TParticle in clones array,
180187
// or external program generating such a file

Generators/src/GeneratorFromFile.cxx

Lines changed: 231 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <TParticle.h>
2323
#include <TTree.h>
2424
#include <sstream>
25+
#include <filesystem>
26+
#include <TGrid.h>
2527

2628
namespace o2
2729
{
@@ -249,6 +251,7 @@ bool GeneratorFromO2Kine::importParticles()
249251
// Randomize the order of events in the input file
250252
if (mRandomize) {
251253
mEventCounter = gRandom->Integer(mEventsAvailable);
254+
LOG(info) << "GeneratorFromO2Kine - Picking event " << mEventCounter;
252255
}
253256

254257
double dPhi = 0.;
@@ -352,8 +355,235 @@ void GeneratorFromO2Kine::updateHeader(o2::dataformats::MCEventHeader* eventHead
352355
eventHeader->putInfo<int>("forwarding-generator_inputEventNumber", mEventCounter - 1);
353356
}
354357

358+
namespace
359+
{
360+
// some helper to execute a command and capture it's output in a vector
361+
std::vector<std::string> executeCommand(const std::string& command)
362+
{
363+
std::vector<std::string> result;
364+
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose);
365+
if (!pipe) {
366+
throw std::runtime_error("Failed to open pipe");
367+
}
368+
369+
char buffer[1024];
370+
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) {
371+
std::string line(buffer);
372+
// Remove trailing newline character, if any
373+
if (!line.empty() && line.back() == '\n') {
374+
line.pop_back();
375+
}
376+
result.push_back(line);
377+
}
378+
return result;
379+
}
380+
} // namespace
381+
382+
GeneratorFromEventPool::GeneratorFromEventPool(EventPoolGenConfig const& pars) : mConfig{pars}
383+
{
384+
}
385+
386+
bool GeneratorFromEventPool::Init()
387+
{
388+
// initialize the event pool
389+
if (mConfig.rngseed > 0) {
390+
mRandomEngine.seed(mConfig.rngseed);
391+
} else {
392+
std::random_device rd;
393+
mRandomEngine.seed(rd());
394+
}
395+
mPoolFilesAvailable = setupFileUniverse(mConfig.eventPoolPath);
396+
397+
if (mPoolFilesAvailable.size() == 0) {
398+
LOG(error) << "No file found that can be used with EventPool generator";
399+
return false;
400+
}
401+
402+
// now choose the actual file
403+
std::uniform_int_distribution<int> distribution(0, mPoolFilesAvailable.size());
404+
mFileChosen = mPoolFilesAvailable[distribution(mRandomEngine)];
405+
LOG(info) << "EventPool is using file " << mFileChosen;
406+
407+
// we bring up the internal mO2KineGenerator
408+
auto kine_config = O2KineGenConfig{
409+
.skipNonTrackable = mConfig.skipNonTrackable,
410+
.continueMode = false,
411+
.roundRobin = false,
412+
.randomize = mConfig.randomize,
413+
.rngseed = mConfig.rngseed,
414+
.randomphi = mConfig.randomphi,
415+
.fileName = mFileChosen};
416+
mO2KineGenerator.reset(new GeneratorFromO2Kine(kine_config));
417+
return mO2KineGenerator->Init();
418+
}
419+
420+
namespace
421+
{
422+
namespace fs = std::filesystem;
423+
// checks a single file name
424+
bool checkFileName(std::string const& pathStr)
425+
{
426+
// LOG(info) << "Checking filename " << pathStr;
427+
try {
428+
// Remove optional protocol prefix "alien://"
429+
const std::string protocol = "alien://";
430+
std::string finalPathStr(pathStr);
431+
if (pathStr.starts_with(protocol)) {
432+
finalPathStr = pathStr.substr(protocol.size());
433+
}
434+
fs::path path(finalPathStr);
435+
436+
// Check if the filename is "eventpool.root"
437+
return path.filename() == GeneratorFromEventPool::eventpool_filename;
438+
} catch (const fs::filesystem_error& e) {
439+
// Invalid path syntax will throw an exception
440+
std::cerr << "Filesystem error: " << e.what() << '\n';
441+
return false;
442+
} catch (...) {
443+
// Catch-all for other potential exceptions
444+
std::cerr << "An unknown error occurred while checking the path.\n";
445+
return false;
446+
}
447+
}
448+
449+
// checks a whole universe of file names
450+
bool checkFileUniverse(std::vector<std::string> const& universe)
451+
{
452+
if (universe.size() == 0) {
453+
return false;
454+
}
455+
for (auto& fn : universe) {
456+
if (!checkFileName(fn)) {
457+
return false;
458+
}
459+
}
460+
// TODO: also check for a common path structure with maximally 00X as only difference
461+
462+
return true;
463+
}
464+
465+
std::vector<std::string> readLines(const std::string& filePath)
466+
{
467+
std::vector<std::string> lines;
468+
469+
// Check if the file is a valid text file
470+
fs::path path(filePath);
471+
472+
// Open the file
473+
std::ifstream file(filePath);
474+
if (!file.is_open()) {
475+
throw std::ios_base::failure("Failed to open the file.");
476+
}
477+
478+
// Read up to n lines
479+
std::string line;
480+
while (std::getline(file, line)) {
481+
lines.push_back(line);
482+
}
483+
return lines;
484+
}
485+
486+
// Function to find all files named eventpool_filename under a given path
487+
std::vector<std::string> getLocalFileList(const fs::path& rootPath)
488+
{
489+
std::vector<std::string> result;
490+
491+
// Ensure the root path exists and is a directory
492+
if (!fs::exists(rootPath) || !fs::is_directory(rootPath)) {
493+
throw std::invalid_argument("The provided path is not a valid directory.");
494+
}
495+
496+
// Iterate over the directory and subdirectories
497+
for (const auto& entry : fs::recursive_directory_iterator(rootPath)) {
498+
if (entry.is_regular_file() && entry.path().filename() == GeneratorFromEventPool::eventpool_filename) {
499+
result.push_back(entry.path().string());
500+
}
501+
}
502+
return result;
503+
}
504+
505+
} // end anonymous namespace
506+
507+
/// A function determining the universe of event pool files, as determined by the path string
508+
/// returns empty vector if it fails
509+
std::vector<std::string> GeneratorFromEventPool::setupFileUniverse(std::string const& path) const
510+
{
511+
// the path could refer to a local or alien filesystem; find out first
512+
bool onAliEn = strncmp(path.c_str(), std::string(alien_protocol_prefix).c_str(), alien_protocol_prefix.size()) == 0;
513+
std::vector<std::string> result;
514+
515+
if (onAliEn) {
516+
// AliEn case
517+
// we support: (a) an actual evtgen file and (b) a path containing multiple eventfiles
518+
519+
auto alienStatTypeCommand = std::string("alien.py stat ") + mConfig.eventPoolPath + std::string(" 2>/dev/null | grep Type ");
520+
auto typeString = executeCommand(alienStatTypeCommand);
521+
if (typeString.size() == 0) {
522+
return result;
523+
} else if (typeString.size() == 1 && typeString.front() == std::string("Type: f")) {
524+
// this is a file ... simply use it
525+
result.push_back(mConfig.eventPoolPath);
526+
return result;
527+
} else if (typeString.size() == 1 && typeString.front() == std::string("Type: d")) {
528+
// this is a directory
529+
// construct command to find actual event files
530+
std::string alienSearchCommand = std::string("alien.py find ") +
531+
mConfig.eventPoolPath + "/ " + std::string(eventpool_filename);
532+
533+
auto universe_vector = executeCommand(alienSearchCommand);
534+
// check vector
535+
if (!checkFileUniverse(universe_vector)) {
536+
return result;
537+
}
538+
for (auto& f : universe_vector) {
539+
f = std::string(alien_protocol_prefix) + f;
540+
}
541+
542+
return universe_vector;
543+
} else {
544+
LOG(error) << "Unsupported file type";
545+
return result;
546+
}
547+
} else {
548+
// local file case
549+
// check if the path is a regular file
550+
auto is_actual_file = std::filesystem::is_regular_file(path);
551+
if (is_actual_file) {
552+
// The files must match a criteria of being canonical paths ending with eventpool_Kine.root
553+
if (checkFileName(path)) {
554+
TFile rootfile(path.c_str(), "OPEN");
555+
if (!rootfile.IsZombie()) {
556+
result.push_back(path);
557+
return result;
558+
}
559+
} else {
560+
// otherwise assume it is a text file containing a list of files themselves
561+
auto files = readLines(path);
562+
if (checkFileUniverse(files)) {
563+
result = files;
564+
return result;
565+
}
566+
}
567+
} else {
568+
// check if the path is just a path
569+
// In this case we need to search something and check
570+
auto is_dir = std::filesystem::is_directory(path);
571+
if (!is_dir) {
572+
return result;
573+
}
574+
auto files = getLocalFileList(path);
575+
if (checkFileUniverse(files)) {
576+
result = files;
577+
return result;
578+
}
579+
}
580+
}
581+
return result;
582+
}
583+
355584
} // namespace eventgen
356585
} // end namespace o2
357586

587+
ClassImp(o2::eventgen::GeneratorFromEventPool);
358588
ClassImp(o2::eventgen::GeneratorFromFile);
359-
ClassImp(o2::eventgen::GeneratorFromO2Kine);
589+
ClassImp(o2::eventgen::GeneratorFromO2Kine);

Generators/src/GeneratorFromO2KineParam.cxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313

1414
#include "Generators/GeneratorFromO2KineParam.h"
1515
O2ParamImpl(o2::eventgen::GeneratorFromO2KineParam);
16+
O2ParamImpl(o2::eventgen::GeneratorEventPoolParam);

0 commit comments

Comments
 (0)