13#include <fairlogger/Logger.h>
15#include <tbb/concurrent_queue.h>
16#include <tbb/task_arena.h>
17#include <tbb/parallel_for.h>
43 if (inputgens.starts_with(
"alien://")) {
45 TGrid::Connect(
"alien://");
47 LOG(fatal) <<
"AliEn connection failed, check token.";
51 TString aliencp = Form(
"alien_cp %s file:./%s",
52 inputgens.c_str(),
"hybridAlien.json");
53 if (gSystem->Exec(aliencp.Data()) != 0) {
54 LOG(fatal) <<
"Error: Issues in fetching file" << inputgens;
60 if (!
parseJSON(isAlien ?
"hybridAlien.json" : inputgens)) {
61 LOG(fatal) <<
"Failed to parse JSON configuration from input generators";
65 if (mConfigs.size() != mInputGens.size()) {
66 LOG(fatal) <<
"Number of configurations does not match the number of generators";
69 if (mConfigs.size() == 0) {
70 for (
auto gen : mInputGens) {
71 mConfigs.push_back(
"");
75 if (!(mRandomize || mGenerationMode == GenMode::kParallel)) {
77 if (mGroups.size() != mFractions.size()) {
78 LOG(fatal) <<
"Number of groups does not match the number of fractions";
82 if (mFractions.size() != mInputGens.size()) {
83 LOG(fatal) <<
"Number of fractions does not match the number of generators";
88 if (std::all_of(mFractions.begin(), mFractions.end(), [](
int i) { return i == 0; })) {
89 LOG(fatal) <<
"All fractions provided are 0, no simulation will be performed";
93 for (
auto gen : mInputGens) {
95 LOG(info) <<
"Checking if generator " <<
gen <<
" is in the list of available generators \n";
96 if (std::find(generatorNames.begin(), generatorNames.end(),
gen) != generatorNames.end()) {
97 LOG(info) <<
"Found generator " <<
gen <<
" in the list of available generators \n";
98 if (
gen.compare(
"boxgen") == 0) {
100 gens.push_back(std::make_shared<o2::eventgen::BoxGenerator>());
103 int confBoxIndex = std::stoi(mConfigs[
index].substr(7));
104 gens.push_back(std::make_shared<o2::eventgen::BoxGenerator>(*mBoxGenConfigs[confBoxIndex]));
106 mGens.push_back(
gen);
107 }
else if (
gen.compare(0, 7,
"pythia8") == 0) {
110 auto pars = Pythia8GenConfig();
111 gens.push_back(std::make_shared<o2::eventgen::GeneratorPythia8>(pars));
114 int confPythia8Index = std::stoi(mConfigs[
index].substr(8));
115 gens.push_back(std::make_shared<o2::eventgen::GeneratorPythia8>(*mPythia8GenConfigs[confPythia8Index]));
117 mConfsPythia8.push_back(mConfigs[
index]);
118 mGens.push_back(
gen);
119 }
else if (
gen.compare(
"evtpool") == 0) {
120 int confEvtPoolIndex = std::stoi(mConfigs[
index].substr(8));
121 gens.push_back(std::make_shared<o2::eventgen::GeneratorFromEventPool>(mEventPoolConfigs[confEvtPoolIndex]));
122 mGens.push_back(
gen);
123 }
else if (
gen.compare(
"external") == 0) {
124 int confextIndex = std::stoi(mConfigs[
index].substr(9));
126 if (mExternalGenConfigs[confextIndex]->iniFile.size() > 0) {
127 LOG(info) <<
"Setting up external gen using the given INI file";
134 std::string tmp_config_file =
"configkey_tmp_backup_" +
std::to_string(getpid()) + std::string(
".ini");
142 LOG(info) <<
"Setting up external generator with following parameters";
144 auto extgen_filename =
params.fileName;
145 auto extgen_func =
params.funcName;
146 auto extgen = std::shared_ptr<o2::eventgen::Generator>(o2::conf::GetFromMacro<o2::eventgen::Generator*>(extgen_filename, extgen_func,
"FairGenerator*",
"extgen"));
148 LOG(fatal) <<
"Failed to retrieve \'extgen\': problem with configuration ";
153 std::filesystem::remove(tmp_config_file);
155 gens.push_back(std::move(extgen));
156 mGens.push_back(
gen);
158 LOG(info) <<
"Setting up external gen using the given fileName and funcName";
160 auto& extgen_filename = mExternalGenConfigs[confextIndex]->fileName;
161 auto& extgen_func = mExternalGenConfigs[confextIndex]->funcName;
162 auto extGen = std::shared_ptr<o2::eventgen::Generator>(o2::conf::GetFromMacro<o2::eventgen::Generator*>(extgen_filename, extgen_func,
"FairGenerator*",
"extgen"));
164 LOG(fatal) <<
"Failed to load external generator from " << extgen_filename <<
" with function " << extgen_func;
166 gens.push_back(std::move(extGen));
167 mGens.push_back(
gen);
169 }
else if (
gen.compare(
"hepmc") == 0) {
170 int confHepMCIndex = std::stoi(mConfigs[
index].substr(6));
171 gens.push_back(std::make_shared<o2::eventgen::GeneratorHepMC>());
173 dynamic_cast<o2::eventgen::GeneratorHepMC*
>(gens.back().get())->setup(*mFileOrCmdGenConfigs[confHepMCIndex], *mHepMCGenConfigs[confHepMCIndex], globalConfig);
174 mGens.push_back(
gen);
177 LOG(fatal) <<
"Generator " <<
gen <<
" not found in the list of available generators \n";
184GeneratorHybrid::~GeneratorHybrid()
186 LOG(info) <<
"Destructor of generator hybrid called";
194 for (
auto&
gen : mGens) {
195 if (
gen ==
"pythia8pp") {
196 auto config = std::string(std::getenv(
"O2_ROOT")) +
"/share/Generators/egconfig/pythia8_inel.cfg";
197 LOG(info) <<
"Setting \'Pythia8\' base configuration: " << config << std::endl;
199 }
else if (
gen ==
"pythia8hf") {
200 auto config = std::string(std::getenv(
"O2_ROOT")) +
"/share/Generators/egconfig/pythia8_hf.cfg";
201 LOG(info) <<
"Setting \'Pythia8\' base configuration: " << config << std::endl;
203 }
else if (
gen ==
"pythia8hi") {
204 auto config = std::string(std::getenv(
"O2_ROOT")) +
"/share/Generators/egconfig/pythia8_hi.cfg";
205 LOG(info) <<
"Setting \'Pythia8\' base configuration: " << config << std::endl;
207 }
else if (
gen ==
"pythia8powheg") {
208 auto config = std::string(std::getenv(
"O2_ROOT")) +
"/share/Generators/egconfig/pythia8_powheg.cfg";
209 LOG(info) <<
"Setting \'Pythia8\' base configuration: " << config << std::endl;
215 gens[
count]->setTriggerMode(mTriggerModes[
count]);
216 LOG(info) <<
"Setting Trigger mode of generator " <<
gen <<
" to: " << mTriggerModes[
count];
219 for (
int trg = 0; trg < mTriggerMacros[
count].size(); trg++) {
224 LOG(info) <<
"Setting trigger " << trg <<
" of generator " <<
gen <<
" with following parameters";
225 LOG(info) <<
"Macro filename: " << expandedMacro;
226 LOG(info) <<
"Function name: " << mTriggerFuncs[
count][trg];
227 trigger = o2::conf::GetFromMacro<o2::eventgen::Trigger>(expandedMacro, mTriggerFuncs[
count][trg],
"o2::eventgen::Trigger",
"trigger");
229 LOG(info) <<
"Trying to retrieve a \'o2::eventgen::DeepTrigger\' type";
230 deeptrigger = o2::conf::GetFromMacro<o2::eventgen::DeepTrigger>(expandedMacro, mTriggerFuncs[
count][trg],
"o2::eventgen::DeepTrigger",
"deeptrigger");
232 if (!trigger && !deeptrigger) {
233 LOG(warn) <<
"Failed to retrieve \'external trigger\': problem with configuration";
234 LOG(warn) <<
"Trigger " << trg <<
" of generator " <<
gen <<
" will not be included";
237 LOG(info) <<
"Trigger " << trg <<
" of generator " <<
gen <<
" successfully set";
240 gens[
count]->addTrigger(trigger);
242 gens[
count]->addDeepTrigger(deeptrigger);
250 auto groupLabel = [
this](
int k) {
252 for (
auto subIndex : mGroups[k]) {
253 if (!
label.empty()) {
256 label += (mConfigs[subIndex] ==
"" ? mGens[subIndex] : mConfigs[subIndex]);
261 if (std::all_of(mFractions.begin(), mFractions.end(), [](
int i) {
return i == 1; })) {
262 LOG(info) <<
"Full randomisation of generators order";
264 LOG(info) <<
"Randomisation based on fractions";
266 for (
auto&
f : mFractions) {
272 for (
int k = 0; k < mFractions.size(); k++) {
273 if (mFractions[k] == 0) {
275 mRngFractions.push_back(-1);
276 LOG(info) <<
"Generator " << groupLabel(k) <<
" will not be used";
278 chance =
static_cast<float>(mFractions[k]) / allfracs;
280 mRngFractions.push_back(
sum);
281 LOG(info) <<
"Generator " << groupLabel(k) <<
" has a " << chance * 100 <<
"% chance of being used";
286 LOG(info) <<
"Generators will be used in sequence, following provided fractions";
289 mGenIsInitialized.resize(gens.size(),
false);
290 if (mGenerationMode == GenMode::kParallel) {
292 mResultQueue.resize(1);
295 mResultQueue.resize(gens.size());
303 auto process_generator_task = [
this](std::vector<std::shared_ptr<o2::eventgen::Generator>>
const& generatorvec,
int task) {
304 LOG(
debug) <<
"Starting eventgen for task " << task;
305 auto& generator = generatorvec[task];
315 bool isTriggered =
false;
316 while (!isTriggered) {
317 generator->clearParticles();
318 generator->generateEvent();
319 generator->importParticles();
320 isTriggered = generator->triggerEvent();
322 LOG(
debug) <<
"eventgen finished for task " << task;
324 if (mGenerationMode == GenMode::kParallel) {
325 mResultQueue[0].push(task);
327 mResultQueue[task].push(task);
333 auto worker_function = [
this, process_generator_task]() {
337 auto generators_copy = gens;
341 if (mInputTaskQueue.try_pop(task)) {
342 process_generator_task(generators_copy, task);
344 std::this_thread::sleep_for(std::chrono::milliseconds(10));
350 mTBBTaskPoolRunner = std::thread([
this, worker_function]() { mTaskArena.execute([&]() { tbb::parallel_for(0, mTaskArena.max_concurrency(), [&](
int) { worker_function(); }); }); });
351 mTBBTaskPoolRunner.detach();
355 for (
size_t genindex = 0; genindex < gens.size(); ++genindex) {
356 mInputTaskQueue.push(genindex);
359 mIsInitialized =
true;
365 if (!mIsInitialized) {
368 if (mGenerationMode == GenMode::kParallel) {
377 if (mRngFractions.size() != 0) {
379 float rnum = gRandom->Rndm();
381 for (
int k = 0; k < mRngFractions.size(); k++) {
382 if (rnum <= mRngFractions[k]) {
388 mIndex = gRandom->Integer(mFractions.size());
391 while (mFractions[mCurrentFraction] == 0 || mseqCounter == mFractions[mCurrentFraction]) {
392 if (mFractions[mCurrentFraction] != 0) {
395 mCurrentFraction = (mCurrentFraction + 1) % mFractions.size();
397 mIndex = mCurrentFraction;
407 std::vector<int> subGenIndex = {};
410 mResultQueue[0].pop(genIndex);
413 if (!mCocktailMode) {
414 mResultQueue[mIndex].pop(genIndex);
417 subGenIndex.resize(mGroups[mIndex].
size());
418 for (
size_t pos = 0;
pos < mGroups[mIndex].size(); ++
pos) {
419 int subIndex = mGroups[mIndex][
pos];
420 LOG(info) <<
"Getting generator " << mGens[subIndex] <<
" from cocktail group " << mIndex;
421 mResultQueue[subIndex].pop(subGenIndex[
pos]);
426 auto unit_transformer = [](
auto& p,
auto pos_unit,
auto time_unit,
auto en_unit,
auto mom_unit) {
427 p.SetMomentum(p.Px() * mom_unit, p.Py() * mom_unit, p.Pz() * mom_unit, p.Energy() * en_unit);
428 p.SetProductionVertex(p.Vx() * pos_unit, p.Vy() * pos_unit, p.Vz() * pos_unit, p.T() * time_unit);
431 auto index_transformer = [](
auto& p,
int offset) {
432 for (
int i = 0;
i < 2; ++
i) {
433 if (p.GetMother(
i) != -1) {
434 const auto newindex = p.GetMother(
i) +
offset;
435 p.SetMother(
i, newindex);
438 if (p.GetNDaughters() > 0) {
439 for (
int i = 0;
i < 2; ++
i) {
440 const auto newindex = p.GetDaughter(
i) +
offset;
441 p.SetDaughter(
i, newindex);
451 mMCEventHeader.
Reset();
454 for (
auto subIndex : subGenIndex) {
455 LOG(info) <<
"Importing particles for task " << subIndex;
456 auto subParticles = gens[subIndex]->getParticles();
458 auto time_unit = gens[subIndex]->getTimeUnit();
459 auto pos_unit = gens[subIndex]->getPositionUnit();
460 auto mom_unit = gens[subIndex]->getMomentumUnit();
461 auto energy_unit = gens[subIndex]->getEnergyUnit();
467 for (
auto& p : subParticles) {
469 index_transformer(p,
offset);
471 unit_transformer(p, pos_unit, time_unit, energy_unit, mom_unit);
476 if (mHeaderGeneratorIndex == -1) {
477 gens[subIndex]->updateHeader(&mMCEventHeader);
478 mHeaderGeneratorIndex = subIndex;
480 mInputTaskQueue.push(subIndex);
484 LOG(info) <<
"Importing particles for task " << genIndex;
488 auto time_unit = gens[genIndex]->getTimeUnit();
489 auto pos_unit = gens[genIndex]->getPositionUnit();
490 auto mom_unit = gens[genIndex]->getMomentumUnit();
491 auto energy_unit = gens[genIndex]->getEnergyUnit();
496 unit_transformer(p, pos_unit, time_unit, energy_unit, mom_unit);
500 gens[genIndex]->updateHeader(&mMCEventHeader);
501 mHeaderGeneratorIndex = genIndex;
502 mInputTaskQueue.push(genIndex);
509 LOG(info) <<
"HybridGen: Stopping TBB task pool";
521 mMCEventHeader.SetVertex(eventHeader->GetX(), eventHeader->GetY(), eventHeader->GetZ());
522 mHeaderGeneratorIndex = -1;
529 eventHeader->
putInfo<std::string>(
"forwarding-generator",
"HybridGen");
536 rapidjson::StringBuffer
buffer;
537 rapidjson::Writer<rapidjson::StringBuffer> writer(
buffer);
538 value.Accept(writer);
539 return buffer.GetString();
544 std::string
name =
gen[
"name"].GetString();
545 mInputGens.push_back(
name);
546 if (
gen.HasMember(
"config")) {
547 if (
name ==
"boxgen") {
548 const auto& boxconf =
gen[
"config"];
549 auto boxConfig = TBufferJSON::FromJSON<o2::eventgen::BoxGenConfig>(
jsonValueToString(boxconf).c_str());
550 mBoxGenConfigs.push_back(std::move(boxConfig));
551 mConfigs.push_back(
"boxgen_" +
std::to_string(mBoxGenConfigs.size() - 1));
552 }
else if (
name ==
"pythia8") {
553 const auto& pythia8conf =
gen[
"config"];
554 auto pythia8Config = TBufferJSON::FromJSON<o2::eventgen::Pythia8GenConfig>(
jsonValueToString(pythia8conf).c_str());
555 mPythia8GenConfigs.push_back(std::move(pythia8Config));
556 mConfigs.push_back(
"pythia8_" +
std::to_string(mPythia8GenConfigs.size() - 1));
557 }
else if (
name ==
"evtpool") {
558 const auto& o2kineconf =
gen[
"config"];
559 auto poolConfig = TBufferJSON::FromJSON<o2::eventgen::EventPoolGenConfig>(
jsonValueToString(o2kineconf).c_str());
560 mEventPoolConfigs.push_back(*poolConfig);
561 mConfigs.push_back(
"evtpool_" +
std::to_string(mEventPoolConfigs.size() - 1));
562 }
else if (
name ==
"external") {
563 const auto& extconf =
gen[
"config"];
564 auto extConfig = TBufferJSON::FromJSON<o2::eventgen::ExternalGenConfig>(
jsonValueToString(extconf).c_str());
565 mExternalGenConfigs.push_back(std::move(extConfig));
566 mConfigs.push_back(
"external_" +
std::to_string(mExternalGenConfigs.size() - 1));
567 }
else if (
name ==
"hepmc") {
568 const auto& genconf =
gen[
"config"];
569 const auto& cmdconf = genconf[
"configcmd"];
570 const auto& hepmcconf = genconf[
"confighepmc"];
571 auto cmdConfig = TBufferJSON::FromJSON<o2::eventgen::FileOrCmdGenConfig>(
jsonValueToString(cmdconf).c_str());
572 auto hepmcConfig = TBufferJSON::FromJSON<o2::eventgen::HepMCGenConfig>(
jsonValueToString(hepmcconf).c_str());
573 mFileOrCmdGenConfigs.push_back(std::move(cmdConfig));
574 mHepMCGenConfigs.push_back(std::move(hepmcConfig));
575 mConfigs.push_back(
"hepmc_" +
std::to_string(mFileOrCmdGenConfigs.size() - 1));
577 mConfigs.push_back(
"");
580 if (
name ==
"boxgen" ||
name ==
"pythia8" ||
name ==
"external" ||
name ==
"hepmc") {
581 LOG(fatal) <<
"No configuration provided for generator " <<
name;
584 mConfigs.push_back(
"");
587 if (
gen.HasMember(
"triggers")) {
588 const auto& trigger =
gen[
"triggers"];
589 auto trigger_specs = [
this, &trigger]() {
590 mTriggerMacros.push_back({});
591 mTriggerFuncs.push_back({});
592 if (trigger.HasMember(
"specs")) {
593 for (
auto& spec : trigger[
"specs"].GetArray()) {
594 if (spec.HasMember(
"macro")) {
595 const auto& macro = spec[
"macro"].GetString();
596 if (!(strcmp(macro,
"") == 0)) {
597 mTriggerMacros.back().push_back(macro);
599 mTriggerMacros.back().push_back(
"");
602 mTriggerMacros.back().push_back(
"");
604 if (spec.HasMember(
"function")) {
605 const auto& function = spec[
"function"].GetString();
606 if (!(strcmp(function,
"") == 0)) {
607 mTriggerFuncs.back().push_back(function);
609 mTriggerFuncs.back().push_back(
"");
612 mTriggerFuncs.back().push_back(
"");
616 mTriggerMacros.back().push_back(
"");
617 mTriggerFuncs.back().push_back(
"");
620 if (trigger.HasMember(
"mode")) {
621 const auto& trmode = trigger[
"mode"].GetString();
622 if (strcmp(trmode,
"or") == 0) {
625 }
else if (strcmp(trmode,
"and") == 0) {
628 }
else if (strcmp(trmode,
"off") == 0) {
630 mTriggerMacros.push_back({
""});
631 mTriggerFuncs.push_back({
""});
633 LOG(warn) <<
"Wrong trigger mode provided for generator " <<
name <<
", keeping trigger OFF";
635 mTriggerMacros.push_back({
""});
636 mTriggerFuncs.push_back({
""});
639 LOG(warn) <<
"No trigger mode provided for generator " <<
name <<
", turning trigger OFF";
641 mTriggerMacros.push_back({
""});
642 mTriggerFuncs.push_back({
""});
646 mTriggerMacros.push_back({
""});
647 mTriggerFuncs.push_back({
""});
656 if (gSystem->AccessPathName(expandedPath.c_str())) {
657 LOG(fatal) <<
"Configuration file " << expandedPath <<
" for hybrid generator does not exist";
661 std::ifstream fileStream(expandedPath, std::ios::in);
662 if (!fileStream.is_open()) {
663 LOG(error) <<
"Cannot open " << expandedPath;
666 rapidjson::IStreamWrapper isw(fileStream);
667 rapidjson::Document doc;
668 doc.ParseStream(isw);
669 if (doc.HasParseError()) {
670 LOG(error) <<
"Error parsing provided json file " << expandedPath;
671 LOG(error) <<
" - Error -> " << rapidjson::GetParseError_En(doc.GetParseError());
676 if (doc.HasMember(
"mode")) {
677 const auto&
mode = doc[
"mode"].GetString();
678 if (strcmp(
mode,
"sequential") == 0) {
680 mGenerationMode = GenMode::kSeq;
682 if (strcmp(
mode,
"parallel") == 0) {
685 mGenerationMode = GenMode::kParallel;
686 LOG(info) <<
"Setting mode to parallel";
691 if (doc.HasMember(
"generators")) {
692 const auto& gens = doc[
"generators"];
693 for (
const auto&
gen : gens.GetArray()) {
694 mGroups.push_back({});
696 if (
gen.HasMember(
"cocktail")) {
697 mCocktailMode =
true;
698 for (
const auto& subgen :
gen[
"cocktail"].GetArray()) {
700 mGroups.back().push_back(mInputGens.size() - 1);
713 mGroups.back().push_back(mInputGens.size() - 1);
719 if (doc.HasMember(
"fractions")) {
720 const auto& fractions = doc[
"fractions"];
721 for (
const auto& frac : fractions.GetArray()) {
723 LOG(fatal) <<
"Fractions must be integers. Wrong type found in JSON";
726 mFractions.push_back(frac.GetInt());
730 const auto& gens = doc[
"generators"];
731 for (
const auto&
gen : gens.GetArray()) {
732 mFractions.push_back(1);
default_random_engine gen(dev())
ClassImp(o2::eventgen::GeneratorHybrid)
static const GeneratorHybridParam & Instance()
static void writeINI(std::string const &filename, std::string const &keyOnly="")
static void updateFromFile(std::string const &, std::string const ¶msList="", bool unchangedOnly=false)
static SimConfig & Instance()
GeneratorHybrid(const GeneratorHybrid &)=delete
Bool_t parseJSON(const std::string &path)
std::string jsonValueToString(const T &value)
Bool_t generateEvent() override
void updateHeader(o2::dataformats::MCEventHeader *eventHeader) override
static GeneratorHybrid & Instance(const std::string &inputgens="")
Bool_t confSetter(const auto &gen)
Bool_t importParticles() override
void setPositionUnit(double val)
void setEnergyUnit(double val)
void notifySubGenerator(int subGeneratorId)
static unsigned int getTotalNEvents()
void setTimeUnit(double val)
void addSubGenerator(int subGeneratorId, std::string const &subGeneratorDescription)
std::vector< TParticle > mParticles
void setMomentumUnit(double val)
float sum(float s, o2::dcs::DataPointValue v)
GLuint const GLchar * name
GLsizei const GLfloat * value
GLenum const GLfloat * params
GLuint GLsizei const GLchar * label
GLsizei const GLchar *const * path
std::function< bool(void *, std::string)> DeepTrigger
std::function< bool(const std::vector< TParticle > &)> Trigger
std::string expandShellVarsInFileName(std::string const &input)
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)
void compare(std::string_view s1, std::string_view s2)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"