diff --git a/Generators/include/Generators/GeneratorHybrid.h b/Generators/include/Generators/GeneratorHybrid.h index abce56f762f2a..b0993c4fd82e2 100644 --- a/Generators/include/Generators/GeneratorHybrid.h +++ b/Generators/include/Generators/GeneratorHybrid.h @@ -66,6 +66,7 @@ class GeneratorHybrid : public Generator void setNEvents(int n) { mNEvents = n; } Bool_t parseJSON(const std::string& path); + Bool_t confSetter(const auto& gen); template std::string jsonValueToString(const T& value); @@ -98,6 +99,10 @@ class GeneratorHybrid : public Generator int mEventCounter = 0; int mTasksStarted = 0; + // Cocktail mode + bool mCocktailMode = false; + std::vector> mGroups; + // Create a task arena with a specified number of threads std::thread mTBBTaskPoolRunner; tbb::concurrent_bounded_queue mInputTaskQueue; diff --git a/Generators/src/GeneratorHybrid.cxx b/Generators/src/GeneratorHybrid.cxx index 932be0586ce4d..af6f2bea03052 100644 --- a/Generators/src/GeneratorHybrid.cxx +++ b/Generators/src/GeneratorHybrid.cxx @@ -12,7 +12,6 @@ #include "Generators/GeneratorHybrid.h" #include #include - #include #include #include @@ -42,9 +41,16 @@ GeneratorHybrid::GeneratorHybrid(const std::string& inputgens) } int index = 0; if (!(mRandomize || mGenerationMode == GenMode::kParallel)) { - if (mFractions.size() != mInputGens.size()) { - LOG(fatal) << "Number of fractions does not match the number of generators"; - return; + if (mCocktailMode) { + if (mGroups.size() != mFractions.size()) { + LOG(fatal) << "Number of groups does not match the number of fractions"; + return; + } + } else { + if (mFractions.size() != mInputGens.size()) { + LOG(fatal) << "Number of fractions does not match the number of generators"; + return; + } } // Check if all elements of mFractions are 0 if (std::all_of(mFractions.begin(), mFractions.end(), [](int i) { return i == 0; })) { @@ -303,7 +309,7 @@ bool GeneratorHybrid::generateEvent() } } } else { - mIndex = gRandom->Integer(mGens.size()); + mIndex = gRandom->Integer(mFractions.size()); } } else { while (mFractions[mCurrentFraction] == 0 || mseqCounter == mFractions[mCurrentFraction]) { @@ -322,25 +328,47 @@ bool GeneratorHybrid::generateEvent() bool GeneratorHybrid::importParticles() { int genIndex = -1; + std::vector subGenIndex = {}; if (mIndex == -1) { // this means parallel mode ---> we have a common queue mResultQueue[0].pop(genIndex); } else { // need to pop from a particular queue - mResultQueue[mIndex].pop(genIndex); + if (!mCocktailMode) { + mResultQueue[mIndex].pop(genIndex); + } else { + // in cocktail mode we need to pop from the group queue + subGenIndex.resize(mGroups[mIndex].size()); + for (size_t pos = 0; pos < mGroups[mIndex].size(); ++pos) { + int subIndex = mGroups[mIndex][pos]; + LOG(info) << "Getting generator " << mGens[subIndex] << " from cocktail group " << mIndex; + mResultQueue[subIndex].pop(subGenIndex[pos]); + } + } } - LOG(info) << "Importing particles for task " << genIndex; - - // at this moment the mIndex-th generator is ready to be used + // Clear particles and event header mParticles.clear(); - mParticles = gens[genIndex]->getParticles(); - - // fetch the event Header information from the underlying generator mMCEventHeader.clearInfo(); - gens[genIndex]->updateHeader(&mMCEventHeader); - - mInputTaskQueue.push(genIndex); - mTasksStarted++; + if (mCocktailMode) { + // in cocktail mode we need to merge the particles from the different generators + for (auto subIndex : subGenIndex) { + LOG(info) << "Importing particles for task " << subIndex; + auto subParticles = gens[subIndex]->getParticles(); + mParticles.insert(mParticles.end(), subParticles.begin(), subParticles.end()); + // fetch the event Header information from the underlying generator + gens[subIndex]->updateHeader(&mMCEventHeader); + mInputTaskQueue.push(subIndex); + mTasksStarted++; + } + } else { + LOG(info) << "Importing particles for task " << genIndex; + // at this moment the mIndex-th generator is ready to be used + mParticles = gens[genIndex]->getParticles(); + // fetch the event Header information from the underlying generator + gens[genIndex]->updateHeader(&mMCEventHeader); + mInputTaskQueue.push(genIndex); + mTasksStarted++; + } mseqCounter++; mEventCounter++; @@ -348,6 +376,7 @@ bool GeneratorHybrid::importParticles() LOG(info) << "HybridGen: Stopping TBB task pool"; mStopFlag = true; } + return true; } @@ -371,6 +400,59 @@ std::string GeneratorHybrid::jsonValueToString(const T& value) return buffer.GetString(); } +Bool_t GeneratorHybrid::confSetter(const auto& gen) +{ + std::string name = gen["name"].GetString(); + mInputGens.push_back(name); + if (gen.HasMember("config")) { + if (name == "boxgen") { + const auto& boxconf = gen["config"]; + auto boxConfig = TBufferJSON::FromJSON(jsonValueToString(boxconf).c_str()); + mBoxGenConfigs.push_back(std::move(boxConfig)); + mConfigs.push_back("boxgen_" + std::to_string(mBoxGenConfigs.size() - 1)); + } else if (name == "pythia8") { + const auto& pythia8conf = gen["config"]; + auto pythia8Config = TBufferJSON::FromJSON(jsonValueToString(pythia8conf).c_str()); + mPythia8GenConfigs.push_back(std::move(pythia8Config)); + mConfigs.push_back("pythia8_" + std::to_string(mPythia8GenConfigs.size() - 1)); + } else if (name == "extkinO2") { + const auto& o2kineconf = gen["config"]; + auto o2kineConfig = TBufferJSON::FromJSON(jsonValueToString(o2kineconf).c_str()); + mO2KineGenConfigs.push_back(std::move(o2kineConfig)); + mConfigs.push_back("extkinO2_" + std::to_string(mO2KineGenConfigs.size() - 1)); + } else if (name == "evtpool") { + const auto& o2kineconf = gen["config"]; + auto poolConfig = TBufferJSON::FromJSON(jsonValueToString(o2kineconf).c_str()); + mEventPoolConfigs.push_back(*poolConfig); + mConfigs.push_back("evtpool_" + std::to_string(mEventPoolConfigs.size() - 1)); + } else if (name == "external") { + const auto& extconf = gen["config"]; + auto extConfig = TBufferJSON::FromJSON(jsonValueToString(extconf).c_str()); + mExternalGenConfigs.push_back(std::move(extConfig)); + mConfigs.push_back("external_" + std::to_string(mExternalGenConfigs.size() - 1)); + } else if (name == "hepmc") { + const auto& genconf = gen["config"]; + const auto& cmdconf = genconf["configcmd"]; + const auto& hepmcconf = genconf["confighepmc"]; + auto cmdConfig = TBufferJSON::FromJSON(jsonValueToString(cmdconf).c_str()); + auto hepmcConfig = TBufferJSON::FromJSON(jsonValueToString(hepmcconf).c_str()); + mFileOrCmdGenConfigs.push_back(std::move(cmdConfig)); + mHepMCGenConfigs.push_back(std::move(hepmcConfig)); + mConfigs.push_back("hepmc_" + std::to_string(mFileOrCmdGenConfigs.size() - 1)); + } else { + mConfigs.push_back(""); + } + } else { + if (name == "boxgen" || name == "pythia8" || name == "extkinO2" || name == "external" || name == "hepmc") { + LOG(fatal) << "No configuration provided for generator " << name; + return false; + } else { + mConfigs.push_back(""); + } + } + return true; +} + Bool_t GeneratorHybrid::parseJSON(const std::string& path) { // Parse JSON file to build map @@ -407,60 +489,26 @@ Bool_t GeneratorHybrid::parseJSON(const std::string& path) if (doc.HasMember("generators")) { const auto& gens = doc["generators"]; for (const auto& gen : gens.GetArray()) { - // push in mInputGens the "name" of the generator - std::string name = gen["name"].GetString(); - mInputGens.push_back(name); - if (gen.HasMember("config")) { - if (name == "boxgen") { - const auto& boxconf = gen["config"]; - auto boxConfig = TBufferJSON::FromJSON(jsonValueToString(boxconf).c_str()); - mBoxGenConfigs.push_back(std::move(boxConfig)); - mConfigs.push_back("boxgen_" + std::to_string(mBoxGenConfigs.size() - 1)); - continue; - } else if (name == "pythia8") { - const auto& pythia8conf = gen["config"]; - auto pythia8Config = TBufferJSON::FromJSON(jsonValueToString(pythia8conf).c_str()); - mPythia8GenConfigs.push_back(std::move(pythia8Config)); - mConfigs.push_back("pythia8_" + std::to_string(mPythia8GenConfigs.size() - 1)); - continue; - } else if (name == "extkinO2") { - const auto& o2kineconf = gen["config"]; - auto o2kineConfig = TBufferJSON::FromJSON(jsonValueToString(o2kineconf).c_str()); - mO2KineGenConfigs.push_back(std::move(o2kineConfig)); - mConfigs.push_back("extkinO2_" + std::to_string(mO2KineGenConfigs.size() - 1)); - continue; - } else if (name == "evtpool") { - const auto& o2kineconf = gen["config"]; - auto poolConfig = TBufferJSON::FromJSON(jsonValueToString(o2kineconf).c_str()); - mEventPoolConfigs.push_back(*poolConfig); - mConfigs.push_back("evtpool_" + std::to_string(mEventPoolConfigs.size() - 1)); - continue; - } else if (name == "external") { - const auto& extconf = gen["config"]; - auto extConfig = TBufferJSON::FromJSON(jsonValueToString(extconf).c_str()); - mExternalGenConfigs.push_back(std::move(extConfig)); - mConfigs.push_back("external_" + std::to_string(mExternalGenConfigs.size() - 1)); - continue; - } else if (name == "hepmc") { - const auto& genconf = gen["config"]; - const auto& cmdconf = genconf["configcmd"]; - const auto& hepmcconf = genconf["confighepmc"]; - auto cmdConfig = TBufferJSON::FromJSON(jsonValueToString(cmdconf).c_str()); - auto hepmcConfig = TBufferJSON::FromJSON(jsonValueToString(hepmcconf).c_str()); - mFileOrCmdGenConfigs.push_back(std::move(cmdConfig)); - mHepMCGenConfigs.push_back(std::move(hepmcConfig)); - mConfigs.push_back("hepmc_" + std::to_string(mFileOrCmdGenConfigs.size() - 1)); - continue; - } else { - mConfigs.push_back(""); + mGroups.push_back({}); + // Check if gen is an array (cocktail mode) + if (gen.HasMember("cocktail")) { + mCocktailMode = true; + for (const auto& subgen : gen["cocktail"].GetArray()) { + if (confSetter(subgen)) { + mGroups.back().push_back(mInputGens.size() - 1); + } else { + return false; + } } } else { - if (name == "boxgen" || name == "pythia8" || name == "extkinO2" || name == "external" || name == "hepmc") { - LOG(fatal) << "No configuration provided for generator " << name; + if (!confSetter(gen)) { return false; - } else { - mConfigs.push_back(""); } + // Groups are created in case cocktail mode is activated, this way + // cocktails can be declared anywhere in the JSON file, without the need + // of grouping single generators. If no cocktail is defined + // groups will be ignored nonetheless. + mGroups.back().push_back(mInputGens.size() - 1); } } } diff --git a/run/SimExamples/Hybrid/README.md b/run/SimExamples/Hybrid/README.md index 3c3cba37748bf..21ccde29dece5 100644 --- a/run/SimExamples/Hybrid/README.md +++ b/run/SimExamples/Hybrid/README.md @@ -13,6 +13,4 @@ available generators in O2. The JSON template can be generated using the ${O2DPG - **runo2sim.sh** → allows to use the hybrid generator example - **hybridconfig.json** → example JSON file for the hybrid generator configuration -- **example.optns** → options file to be used in EPOS4 implemented as subgenerator in this example (the .optns must be available in the current working directory) -- **evtpool.root** → cached events to be used with the extkinO2 generator -- **epos4.hepmc** → EPOS4 events stored as hepmc file \ No newline at end of file +- **example.optns** → options file to be used in EPOS4 implemented as subgenerator in this example (the .optns must be available in the current working directory) \ No newline at end of file diff --git a/run/SimExamples/Hybrid/hybridconfig.json b/run/SimExamples/Hybrid/hybridconfig.json index ec36930c569fe..bd027963417b8 100644 --- a/run/SimExamples/Hybrid/hybridconfig.json +++ b/run/SimExamples/Hybrid/hybridconfig.json @@ -53,7 +53,8 @@ "name": "external", "config": { "fileName": "${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV.C", - "funcName": "GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV()" + "funcName": "GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV()", + "iniFile": "" } }, { diff --git a/run/SimExamples/Hybrid_cocktail/README.md b/run/SimExamples/Hybrid_cocktail/README.md new file mode 100644 index 0000000000000..6e07a847e0e6e --- /dev/null +++ b/run/SimExamples/Hybrid_cocktail/README.md @@ -0,0 +1,13 @@ + + +The usage of the Hybrid generator using cocktails is presented in this example. +The syntax of the JSON file shows how the cocktails can be grouped. Each generator will be pulled exactly once in order for each cocktail event generation. +The basic Hybrid Generator mechanisms are shown in the Hybrid and Hybrid_parallel example folders. +The cocktail configuration can not be setup with the template script for now. + +# Files description + +- **runo2sim.sh** → allows to use the hybrid generator example +- **hybridcocktail.json** → example JSON file for the hybrid generator configuration using cocktails \ No newline at end of file diff --git a/run/SimExamples/Hybrid_cocktail/hybridcocktail.json b/run/SimExamples/Hybrid_cocktail/hybridcocktail.json new file mode 100644 index 0000000000000..7bffc334e174c --- /dev/null +++ b/run/SimExamples/Hybrid_cocktail/hybridcocktail.json @@ -0,0 +1,55 @@ +{ + "generators": [ + { + "cocktail": [ + { + "name": "pythia8", + "config": { + "config": "$O2_ROOT/share/Generators/egconfig/pythia8_inel.cfg", + "hooksFileName": "", + "hooksFuncName": "", + "includePartonEvent": false, + "particleFilter": "", + "verbose": 0 + } + }, + { + "name": "external", + "config": { + "fileName": "${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV.C", + "funcName": "GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV()", + "iniFile": "" + } + } + ] + }, + { + "cocktail": [ + { + "name": "pythia8pp" + }, + { + "name": "extkinO2", + "config": { + "skipNonTrackable": true, + "continueMode": false, + "roundRobin": false, + "randomize": false, + "rngseed": 0, + "randomphi": false, + "fileName": "${PWD}/evtpool.root" + } + } + ] + }, + { + "name": "pythia8hf", + "config": "" + } + ], + "fractions": [ + 1, + 1, + 1 + ] +} \ No newline at end of file diff --git a/run/SimExamples/Hybrid_cocktail/runo2sim.sh b/run/SimExamples/Hybrid_cocktail/runo2sim.sh new file mode 100644 index 0000000000000..64e44fefdd21b --- /dev/null +++ b/run/SimExamples/Hybrid_cocktail/runo2sim.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# +# Hybrid generator simulation example using cocktails: +# the simulation is configured using a JSON file (hybridcocktail.json in this folder) +set -x +if [ ! "${O2DPG_ROOT}" ]; then + echo "This needs O2DPG loaded; alienv enter ..." + exit 1 +fi + +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 2 + +NEV=-1 +more="" +JOBS=2 + +usage() +{ + cat </dev/stderr + exit 3 + ;; + esac + shift +done + +# Set number of events in optns file +if [ ! $NEV -eq -1 ]; then + echo "Setting number of events to $NEV" +else + echo "Number of events not set, defaulting to 10..." + NEV=10 +fi + +# Generation of event pool with pythia8 (10000 events) in a evtpool.root file +${O2DPG_ROOT}/MC/run/examples/event_pool.sh --make + +# Starting simulation with Hybrid generator +${O2_ROOT}/bin/o2-sim --noGeant -j $JOBS --field ccdb --vertexMode kCCDB --run 300000 --configKeyValues "MFTBase.buildAlignment=true;GeneratorHybrid.configFile=$PWD/hybridcocktail.json;GeneratorHybrid.randomize=false;${more}" -g hybrid -o genevents --timestamp 1546300800000 --seed 836302859 -n $NEV \ No newline at end of file