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));
129 if (std::count(mGens.begin(), mGens.end(),
"external") > 0) {
133 if (mExternalGenConfigs[confextIndex]->iniFile.size() > 0) {
134 LOG(info) <<
"Setting up external gen using the given INI file";
141 std::string tmp_config_file =
"configkey_tmp_backup_" +
std::to_string(getpid()) + std::string(
".ini");
149 LOG(info) <<
"Setting up external generator with following parameters";
151 auto extgen_filename =
params.fileName;
152 auto extgen_func =
params.funcName;
153 auto extgen = std::shared_ptr<o2::eventgen::Generator>(o2::conf::GetFromMacro<o2::eventgen::Generator*>(extgen_filename, extgen_func,
"FairGenerator*",
"extgen"));
155 LOG(fatal) <<
"Failed to retrieve \'extgen\': problem with configuration ";
160 std::filesystem::remove(tmp_config_file);
162 gens.push_back(std::move(extgen));
163 mGens.push_back(
gen);
165 LOG(info) <<
"Setting up external gen using the given fileName and funcName";
167 auto& extgen_filename = mExternalGenConfigs[confextIndex]->fileName;
168 auto& extgen_func = mExternalGenConfigs[confextIndex]->funcName;
169 auto extGen = std::shared_ptr<o2::eventgen::Generator>(o2::conf::GetFromMacro<o2::eventgen::Generator*>(extgen_filename, extgen_func,
"FairGenerator*",
"extgen"));
171 LOG(fatal) <<
"Failed to load external generator from " << extgen_filename <<
" with function " << extgen_func;
173 gens.push_back(std::move(extGen));
174 mGens.push_back(
gen);
176 }
else if (
gen.compare(
"hepmc") == 0) {
177 int confHepMCIndex = std::stoi(mConfigs[
index].substr(6));
178 gens.push_back(std::make_shared<o2::eventgen::GeneratorHepMC>());
180 dynamic_cast<o2::eventgen::GeneratorHepMC*
>(gens.back().get())->setup(*mFileOrCmdGenConfigs[confHepMCIndex], *mHepMCGenConfigs[confHepMCIndex], globalConfig);
181 mGens.push_back(
gen);
184 LOG(fatal) <<
"Generator " <<
gen <<
" not found in the list of available generators \n";
191GeneratorHybrid::~GeneratorHybrid()
193 LOG(info) <<
"Destructor of generator hybrid called";
201 for (
auto&
gen : mGens) {
202 if (
gen ==
"pythia8pp") {
203 auto config = std::string(std::getenv(
"O2_ROOT")) +
"/share/Generators/egconfig/pythia8_inel.cfg";
204 LOG(info) <<
"Setting \'Pythia8\' base configuration: " << config << std::endl;
206 }
else if (
gen ==
"pythia8hf") {
207 auto config = std::string(std::getenv(
"O2_ROOT")) +
"/share/Generators/egconfig/pythia8_hf.cfg";
208 LOG(info) <<
"Setting \'Pythia8\' base configuration: " << config << std::endl;
210 }
else if (
gen ==
"pythia8hi") {
211 auto config = std::string(std::getenv(
"O2_ROOT")) +
"/share/Generators/egconfig/pythia8_hi.cfg";
212 LOG(info) <<
"Setting \'Pythia8\' base configuration: " << config << std::endl;
214 }
else if (
gen ==
"pythia8powheg") {
215 auto config = std::string(std::getenv(
"O2_ROOT")) +
"/share/Generators/egconfig/pythia8_powheg.cfg";
216 LOG(info) <<
"Setting \'Pythia8\' base configuration: " << config << std::endl;
222 gens[
count]->setTriggerMode(mTriggerModes[
count]);
223 LOG(info) <<
"Setting Trigger mode of generator " <<
gen <<
" to: " << mTriggerModes[
count];
226 for (
int trg = 0; trg < mTriggerMacros[
count].size(); trg++) {
231 LOG(info) <<
"Setting trigger " << trg <<
" of generator " <<
gen <<
" with following parameters";
232 LOG(info) <<
"Macro filename: " << expandedMacro;
233 LOG(info) <<
"Function name: " << mTriggerFuncs[
count][trg];
234 trigger = o2::conf::GetFromMacro<o2::eventgen::Trigger>(expandedMacro, mTriggerFuncs[
count][trg],
"o2::eventgen::Trigger",
"trigger");
236 LOG(info) <<
"Trying to retrieve a \'o2::eventgen::DeepTrigger\' type";
237 deeptrigger = o2::conf::GetFromMacro<o2::eventgen::DeepTrigger>(expandedMacro, mTriggerFuncs[
count][trg],
"o2::eventgen::DeepTrigger",
"deeptrigger");
239 if (!trigger && !deeptrigger) {
240 LOG(warn) <<
"Failed to retrieve \'external trigger\': problem with configuration";
241 LOG(warn) <<
"Trigger " << trg <<
" of generator " <<
gen <<
" will not be included";
244 LOG(info) <<
"Trigger " << trg <<
" of generator " <<
gen <<
" successfully set";
247 gens[
count]->addTrigger(trigger);
249 gens[
count]->addDeepTrigger(deeptrigger);
257 auto groupLabel = [
this](
int k) {
259 for (
auto subIndex : mGroups[k]) {
260 if (!
label.empty()) {
263 label += (mConfigs[subIndex] ==
"" ? mGens[subIndex] : mConfigs[subIndex]);
268 if (std::all_of(mFractions.begin(), mFractions.end(), [](
int i) {
return i == 1; })) {
269 LOG(info) <<
"Full randomisation of generators order";
271 LOG(info) <<
"Randomisation based on fractions";
273 for (
auto&
f : mFractions) {
279 for (
int k = 0; k < mFractions.size(); k++) {
280 if (mFractions[k] == 0) {
282 mRngFractions.push_back(-1);
283 LOG(info) <<
"Generator " << groupLabel(k) <<
" will not be used";
285 chance =
static_cast<float>(mFractions[k]) / allfracs;
287 mRngFractions.push_back(
sum);
288 LOG(info) <<
"Generator " << groupLabel(k) <<
" has a " << chance * 100 <<
"% chance of being used";
293 LOG(info) <<
"Generators will be used in sequence, following provided fractions";
296 mGenIsInitialized.resize(gens.size(),
false);
297 if (mGenerationMode == GenMode::kParallel) {
299 mResultQueue.resize(1);
302 mResultQueue.resize(gens.size());
310 auto process_generator_task = [
this](std::vector<std::shared_ptr<o2::eventgen::Generator>>
const& generatorvec,
int task) {
311 LOG(
debug) <<
"Starting eventgen for task " << task;
312 auto& generator = generatorvec[task];
322 bool isTriggered =
false;
323 while (!isTriggered) {
324 generator->clearParticles();
325 generator->generateEvent();
326 generator->importParticles();
327 isTriggered = generator->triggerEvent();
329 LOG(
debug) <<
"eventgen finished for task " << task;
331 if (mGenerationMode == GenMode::kParallel) {
332 mResultQueue[0].push(task);
334 mResultQueue[task].push(task);
340 auto worker_function = [
this, process_generator_task]() {
344 auto generators_copy = gens;
348 if (mInputTaskQueue.try_pop(task)) {
349 process_generator_task(generators_copy, task);
351 std::this_thread::sleep_for(std::chrono::milliseconds(10));
357 mTBBTaskPoolRunner = std::thread([
this, worker_function]() { mTaskArena.execute([&]() { tbb::parallel_for(0, mTaskArena.max_concurrency(), [&](
int) { worker_function(); }); }); });
358 mTBBTaskPoolRunner.detach();
362 for (
size_t genindex = 0; genindex < gens.size(); ++genindex) {
363 mInputTaskQueue.push(genindex);
366 mIsInitialized =
true;
372 if (!mIsInitialized) {
375 if (mGenerationMode == GenMode::kParallel) {
384 if (mRngFractions.size() != 0) {
386 float rnum = gRandom->Rndm();
388 for (
int k = 0; k < mRngFractions.size(); k++) {
389 if (rnum <= mRngFractions[k]) {
395 mIndex = gRandom->Integer(mFractions.size());
398 while (mFractions[mCurrentFraction] == 0 || mseqCounter == mFractions[mCurrentFraction]) {
399 if (mFractions[mCurrentFraction] != 0) {
402 mCurrentFraction = (mCurrentFraction + 1) % mFractions.size();
404 mIndex = mCurrentFraction;
414 std::vector<int> subGenIndex = {};
417 mResultQueue[0].pop(genIndex);
420 if (!mCocktailMode) {
421 mResultQueue[mIndex].pop(genIndex);
424 subGenIndex.resize(mGroups[mIndex].
size());
425 for (
size_t pos = 0;
pos < mGroups[mIndex].size(); ++
pos) {
426 int subIndex = mGroups[mIndex][
pos];
427 LOG(info) <<
"Getting generator " << mGens[subIndex] <<
" from cocktail group " << mIndex;
428 mResultQueue[subIndex].pop(subGenIndex[
pos]);
433 auto unit_transformer = [](
auto& p,
auto pos_unit,
auto time_unit,
auto en_unit,
auto mom_unit) {
434 p.SetMomentum(p.Px() * mom_unit, p.Py() * mom_unit, p.Pz() * mom_unit, p.Energy() * en_unit);
435 p.SetProductionVertex(p.Vx() * pos_unit, p.Vy() * pos_unit, p.Vz() * pos_unit, p.T() * time_unit);
438 auto index_transformer = [](
auto& p,
int offset) {
439 for (
int i = 0;
i < 2; ++
i) {
440 if (p.GetMother(
i) != -1) {
441 const auto newindex = p.GetMother(
i) +
offset;
442 p.SetMother(
i, newindex);
445 if (p.GetNDaughters() > 0) {
446 for (
int i = 0;
i < 2; ++
i) {
447 const auto newindex = p.GetDaughter(
i) +
offset;
448 p.SetDaughter(
i, newindex);
458 mMCEventHeader.
Reset();
461 for (
auto subIndex : subGenIndex) {
463 LOG(info) <<
"Importing particles for task " << subIndex;
465 auto subParticles = gens[subIndex]->getParticles();
467 auto time_unit = gens[subIndex]->getTimeUnit();
468 auto pos_unit = gens[subIndex]->getPositionUnit();
469 auto mom_unit = gens[subIndex]->getMomentumUnit();
470 auto energy_unit = gens[subIndex]->getEnergyUnit();
476 for (
auto& p : subParticles) {
478 index_transformer(p,
offset);
480 unit_transformer(p, pos_unit, time_unit, energy_unit, mom_unit);
485 if (mHeaderGeneratorIndex == -1) {
486 gens[subIndex]->updateHeader(&mMCEventHeader);
487 mHeaderGeneratorIndex = subIndex;
489 mInputTaskQueue.push(subIndex);
494 LOG(info) <<
"Importing particles for task " << genIndex;
499 auto time_unit = gens[genIndex]->getTimeUnit();
500 auto pos_unit = gens[genIndex]->getPositionUnit();
501 auto mom_unit = gens[genIndex]->getMomentumUnit();
502 auto energy_unit = gens[genIndex]->getEnergyUnit();
507 unit_transformer(p, pos_unit, time_unit, energy_unit, mom_unit);
511 gens[genIndex]->updateHeader(&mMCEventHeader);
512 mHeaderGeneratorIndex = genIndex;
513 mInputTaskQueue.push(genIndex);
520 LOG(info) <<
"HybridGen: Stopping TBB task pool";
532 mMCEventHeader.SetVertex(eventHeader->GetX(), eventHeader->GetY(), eventHeader->GetZ());
533 mHeaderGeneratorIndex = -1;
540 eventHeader->
putInfo<std::string>(
"forwarding-generator",
"HybridGen");
547 rapidjson::StringBuffer
buffer;
548 rapidjson::Writer<rapidjson::StringBuffer> writer(
buffer);
549 value.Accept(writer);
550 return buffer.GetString();
555 std::string
name =
gen[
"name"].GetString();
556 mInputGens.push_back(
name);
557 if (
gen.HasMember(
"config")) {
558 if (
name ==
"boxgen") {
559 const auto& boxconf =
gen[
"config"];
560 auto boxConfig = TBufferJSON::FromJSON<o2::eventgen::BoxGenConfig>(
jsonValueToString(boxconf).c_str());
561 mBoxGenConfigs.push_back(std::move(boxConfig));
562 mConfigs.push_back(
"boxgen_" +
std::to_string(mBoxGenConfigs.size() - 1));
563 }
else if (
name ==
"pythia8") {
564 const auto& pythia8conf =
gen[
"config"];
565 auto pythia8Config = TBufferJSON::FromJSON<o2::eventgen::Pythia8GenConfig>(
jsonValueToString(pythia8conf).c_str());
566 mPythia8GenConfigs.push_back(std::move(pythia8Config));
567 mConfigs.push_back(
"pythia8_" +
std::to_string(mPythia8GenConfigs.size() - 1));
568 }
else if (
name ==
"evtpool") {
569 const auto& o2kineconf =
gen[
"config"];
570 auto poolConfig = TBufferJSON::FromJSON<o2::eventgen::EventPoolGenConfig>(
jsonValueToString(o2kineconf).c_str());
571 mEventPoolConfigs.push_back(*poolConfig);
572 mConfigs.push_back(
"evtpool_" +
std::to_string(mEventPoolConfigs.size() - 1));
573 }
else if (
name ==
"external") {
574 const auto& extconf =
gen[
"config"];
575 auto extConfig = TBufferJSON::FromJSON<o2::eventgen::ExternalGenConfig>(
jsonValueToString(extconf).c_str());
576 mExternalGenConfigs.push_back(std::move(extConfig));
577 mConfigs.push_back(
"external_" +
std::to_string(mExternalGenConfigs.size() - 1));
578 }
else if (
name ==
"hepmc") {
579 const auto& genconf =
gen[
"config"];
580 const auto& cmdconf = genconf[
"configcmd"];
581 const auto& hepmcconf = genconf[
"confighepmc"];
582 auto cmdConfig = TBufferJSON::FromJSON<o2::eventgen::FileOrCmdGenConfig>(
jsonValueToString(cmdconf).c_str());
583 auto hepmcConfig = TBufferJSON::FromJSON<o2::eventgen::HepMCGenConfig>(
jsonValueToString(hepmcconf).c_str());
584 mFileOrCmdGenConfigs.push_back(std::move(cmdConfig));
585 mHepMCGenConfigs.push_back(std::move(hepmcConfig));
586 mConfigs.push_back(
"hepmc_" +
std::to_string(mFileOrCmdGenConfigs.size() - 1));
588 mConfigs.push_back(
"");
591 if (
name ==
"boxgen" ||
name ==
"pythia8" ||
name ==
"external" ||
name ==
"hepmc") {
592 LOG(fatal) <<
"No configuration provided for generator " <<
name;
595 mConfigs.push_back(
"");
598 if (
gen.HasMember(
"triggers")) {
599 const auto& trigger =
gen[
"triggers"];
600 auto trigger_specs = [
this, &trigger]() {
601 mTriggerMacros.push_back({});
602 mTriggerFuncs.push_back({});
603 if (trigger.HasMember(
"specs")) {
604 for (
auto& spec : trigger[
"specs"].GetArray()) {
605 if (spec.HasMember(
"macro")) {
606 const auto& macro = spec[
"macro"].GetString();
607 if (!(strcmp(macro,
"") == 0)) {
608 mTriggerMacros.back().push_back(macro);
610 mTriggerMacros.back().push_back(
"");
613 mTriggerMacros.back().push_back(
"");
615 if (spec.HasMember(
"function")) {
616 const auto& function = spec[
"function"].GetString();
617 if (!(strcmp(function,
"") == 0)) {
618 mTriggerFuncs.back().push_back(function);
620 mTriggerFuncs.back().push_back(
"");
623 mTriggerFuncs.back().push_back(
"");
627 mTriggerMacros.back().push_back(
"");
628 mTriggerFuncs.back().push_back(
"");
631 if (trigger.HasMember(
"mode")) {
632 const auto& trmode = trigger[
"mode"].GetString();
633 if (strcmp(trmode,
"or") == 0) {
636 }
else if (strcmp(trmode,
"and") == 0) {
639 }
else if (strcmp(trmode,
"off") == 0) {
641 mTriggerMacros.push_back({
""});
642 mTriggerFuncs.push_back({
""});
644 LOG(warn) <<
"Wrong trigger mode provided for generator " <<
name <<
", keeping trigger OFF";
646 mTriggerMacros.push_back({
""});
647 mTriggerFuncs.push_back({
""});
650 LOG(warn) <<
"No trigger mode provided for generator " <<
name <<
", turning trigger OFF";
652 mTriggerMacros.push_back({
""});
653 mTriggerFuncs.push_back({
""});
657 mTriggerMacros.push_back({
""});
658 mTriggerFuncs.push_back({
""});
667 if (gSystem->AccessPathName(expandedPath.c_str())) {
668 LOG(fatal) <<
"Configuration file " << expandedPath <<
" for hybrid generator does not exist";
672 std::ifstream fileStream(expandedPath, std::ios::in);
673 if (!fileStream.is_open()) {
674 LOG(error) <<
"Cannot open " << expandedPath;
677 rapidjson::IStreamWrapper isw(fileStream);
678 rapidjson::Document doc;
679 doc.ParseStream(isw);
680 if (doc.HasParseError()) {
681 LOG(error) <<
"Error parsing provided json file " << expandedPath;
682 LOG(error) <<
" - Error -> " << rapidjson::GetParseError_En(doc.GetParseError());
687 if (doc.HasMember(
"mode")) {
688 const auto&
mode = doc[
"mode"].GetString();
689 if (strcmp(
mode,
"sequential") == 0) {
691 mGenerationMode = GenMode::kSeq;
693 if (strcmp(
mode,
"parallel") == 0) {
696 mGenerationMode = GenMode::kParallel;
697 LOG(info) <<
"Setting mode to parallel";
702 if (doc.HasMember(
"generators")) {
703 const auto& gens = doc[
"generators"];
704 for (
const auto&
gen : gens.GetArray()) {
705 mGroups.push_back({});
707 if (
gen.HasMember(
"cocktail")) {
708 mCocktailMode =
true;
709 for (
const auto& subgen :
gen[
"cocktail"].GetArray()) {
711 mGroups.back().push_back(mInputGens.size() - 1);
724 mGroups.back().push_back(mInputGens.size() - 1);
730 if (doc.HasMember(
"fractions")) {
731 const auto& fractions = doc[
"fractions"];
732 for (
const auto& frac : fractions.GetArray()) {
734 LOG(fatal) <<
"Fractions must be integers. Wrong type found in JSON";
737 mFractions.push_back(frac.GetInt());
741 const auto& gens = doc[
"generators"];
742 for (
const auto&
gen : gens.GetArray()) {
743 mFractions.push_back(1);
default_random_engine gen(dev())
std::vector< double > sum
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)
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"