From 0c1ff2a5b54c5c2816ad9fa5cafa015ff7266887 Mon Sep 17 00:00:00 2001 From: swenzel Date: Tue, 10 Dec 2024 14:16:03 +0100 Subject: [PATCH] HybridGenerator: Asyncronous + parallel event generation This introduces: * asyncronous event generation * possibility for parallel event generation This is useful for: * hiding latency (IO) of certain generators * decoupling the actual work from the call sequence into HybridGenerator * collaboration from multiple clones of the same generator to generate a certain number of events The implementation relies on tbb::task_arena and input/output queues for decoupling the task_arena from the HybridGenerator thread. An example is added for parallel event gen. In addition, this commit has the following changes: - Small adjustments to seeding of Pythia8 in order to avoid same seeds in multiple parallel Pythia instances. - possibility to init external generator from an INI file (typically done or available in O2DPG) - use shared_ptr instead of unique_ptr to keep generators in order to avoid lifetime problems with async processing - preparion to run underlying generator Init() functions in async way (not active yet; needs checks if generators are thread safe) --- Generators/include/Generators/Generator.h | 5 + .../Generators/GeneratorExternalParam.h | 1 + .../include/Generators/GeneratorHybrid.h | 36 ++- .../include/Generators/GeneratorHybridParam.h | 1 + .../include/Generators/GeneratorPythia8.h | 3 + Generators/src/Generator.cxx | 6 + Generators/src/GeneratorFactory.cxx | 1 + Generators/src/GeneratorHybrid.cxx | 252 ++++++++++++++---- Generators/src/GeneratorPythia8.cxx | 14 +- run/SimExamples/Hybrid_parallel/README.md | 9 + .../hybridconfig_extern_parallel.json | 69 +++++ .../hybridconfig_parallel.json | 53 ++++ .../Hybrid_parallel/run_extgen_parallel.sh | 11 + .../Hybrid_parallel/run_parallel.sh | 11 + run/SimExamples/README.md | 1 + 15 files changed, 419 insertions(+), 54 deletions(-) create mode 100644 run/SimExamples/Hybrid_parallel/README.md create mode 100644 run/SimExamples/Hybrid_parallel/hybridconfig_extern_parallel.json create mode 100644 run/SimExamples/Hybrid_parallel/hybridconfig_parallel.json create mode 100755 run/SimExamples/Hybrid_parallel/run_extgen_parallel.sh create mode 100755 run/SimExamples/Hybrid_parallel/run_parallel.sh diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index a488e31ace2b9..72d287f8fd404 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -141,6 +141,11 @@ class Generator : public FairGenerator /** lorentz boost data members **/ Double_t mBoost; + // a unique generator instance counter + // this can be used to make sure no two generator instances have the same seed etc. + static std::atomic InstanceCounter; + int mThisInstanceID = 0; + private: void updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const; diff --git a/Generators/include/Generators/GeneratorExternalParam.h b/Generators/include/Generators/GeneratorExternalParam.h index 9dac84ed9c8e9..6d5b954ced639 100644 --- a/Generators/include/Generators/GeneratorExternalParam.h +++ b/Generators/include/Generators/GeneratorExternalParam.h @@ -37,6 +37,7 @@ struct GeneratorExternalParam : public o2::conf::ConfigurableParamHelper #include "TBufferJSON.h" +#include +#include +#include +#include +#include + namespace o2 { namespace eventgen @@ -50,25 +56,29 @@ class GeneratorHybrid : public Generator public: GeneratorHybrid() = default; GeneratorHybrid(const std::string& inputgens); - ~GeneratorHybrid() = default; + ~GeneratorHybrid(); Bool_t Init() override; Bool_t generateEvent() override; Bool_t importParticles() override; + void setNEvents(int n) { mNEvents = n; } + Bool_t parseJSON(const std::string& path); template std::string jsonValueToString(const T& value); private: o2::eventgen::Generator* currentgen = nullptr; - std::vector> gens; + std::vector> gens; const std::vector generatorNames = {"extkinO2", "evtpool", "boxgen", "external", "hepmc", "pythia8", "pythia8pp", "pythia8hi", "pythia8hf", "pythia8powheg"}; std::vector mInputGens; std::vector mGens; std::vector mConfigs; std::vector mConfsPythia8; + std::vector mGenIsInitialized; + // Parameters configurations std::vector> mBoxGenConfigs; std::vector> mPythia8GenConfigs; @@ -84,6 +94,28 @@ class GeneratorHybrid : public Generator int mseqCounter = 0; int mCurrentFraction = 0; int mIndex = 0; + int mEventCounter = 0; + int mTasksStarted = 0; + + // Create a task arena with a specified number of threads + std::thread mTBBTaskPoolRunner; + tbb::concurrent_bounded_queue mInputTaskQueue; + std::vector> mResultQueue; + tbb::task_arena mTaskArena; + std::atomic mStopFlag; + bool mIsInitialized = false; + + int mNEvents = -1; // the number of events to be done, if known (helps initiating cleanup) + + enum class GenMode { + kSeq, + kParallel + }; + + // hybrid gen operation mode - should be either 'sequential' or 'parallel' + // parallel means that we have clones of the same generator collaborating on event generation + // sequential means that events will be produced in the order given by fractions; async processing is still happening + GenMode mGenerationMode = GenMode::kSeq; //! }; } // namespace eventgen diff --git a/Generators/include/Generators/GeneratorHybridParam.h b/Generators/include/Generators/GeneratorHybridParam.h index a2c32637ad1af..c05b70dcb40ba 100644 --- a/Generators/include/Generators/GeneratorHybridParam.h +++ b/Generators/include/Generators/GeneratorHybridParam.h @@ -31,6 +31,7 @@ namespace eventgen struct GeneratorHybridParam : public o2::conf::ConfigurableParamHelper { std::string configFile = ""; // JSON configuration file for the generators bool randomize = false; // randomize the order of the generators, if not generator using fractions + int num_workers = 1; // number of threads available for asyn/parallel event generation O2ParamDef(GeneratorHybridParam, "GeneratorHybrid"); }; diff --git a/Generators/include/Generators/GeneratorPythia8.h b/Generators/include/Generators/GeneratorPythia8.h index 926003c55259b..9221338677d81 100644 --- a/Generators/include/Generators/GeneratorPythia8.h +++ b/Generators/include/Generators/GeneratorPythia8.h @@ -287,6 +287,9 @@ class GeneratorPythia8 : public Generator // Value of -1 means unitialized; 0 will be time-dependent and values >1 <= MAX_SEED concrete reproducible seeding Pythia8GenConfig mGenConfig; // configuration object + static std::atomic Pythia8InstanceCounter; + int mThisPythia8InstanceID = 0; + constexpr static long MAX_SEED = 900000000; ClassDefOverride(GeneratorPythia8, 1); diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 377ca291662f6..ed7bf0a99bbe3 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -28,6 +28,8 @@ namespace o2 namespace eventgen { +std::atomic Generator::InstanceCounter{0}; + /*****************************************************************/ /*****************************************************************/ @@ -35,6 +37,8 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), mBoost(0.) { /** default constructor **/ + mThisInstanceID = Generator::InstanceCounter; + Generator::InstanceCounter++; } /*****************************************************************/ @@ -43,6 +47,8 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na mBoost(0.) { /** constructor **/ + mThisInstanceID = Generator::InstanceCounter; + Generator::InstanceCounter++; } /*****************************************************************/ diff --git a/Generators/src/GeneratorFactory.cxx b/Generators/src/GeneratorFactory.cxx index 9f763635ac123..dc2f4f2159b4d 100644 --- a/Generators/src/GeneratorFactory.cxx +++ b/Generators/src/GeneratorFactory.cxx @@ -285,6 +285,7 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair return; } auto hybrid = new o2::eventgen::GeneratorHybrid(config); + hybrid->setNEvents(conf.getNEvents()); primGen->AddGenerator(hybrid); #endif } else { diff --git a/Generators/src/GeneratorHybrid.cxx b/Generators/src/GeneratorHybrid.cxx index a24d1ddc1c759..9c3957de9e335 100644 --- a/Generators/src/GeneratorHybrid.cxx +++ b/Generators/src/GeneratorHybrid.cxx @@ -13,10 +13,17 @@ #include #include +#include +#include +#include +#include +#include + namespace o2 { namespace eventgen { + GeneratorHybrid::GeneratorHybrid(const std::string& inputgens) { if (!parseJSON(inputgens)) { @@ -34,7 +41,7 @@ GeneratorHybrid::GeneratorHybrid(const std::string& inputgens) } } int index = 0; - if (!mRandomize) { + if (!(mRandomize || mGenerationMode == GenMode::kParallel)) { if (mFractions.size() != mInputGens.size()) { LOG(fatal) << "Number of fractions does not match the number of generators"; return; @@ -52,47 +59,82 @@ GeneratorHybrid::GeneratorHybrid(const std::string& inputgens) LOG(info) << "Found generator " << gen << " in the list of available generators \n"; if (gen.compare("boxgen") == 0) { if (mConfigs[index].compare("") == 0) { - gens.push_back(std::make_unique()); + gens.push_back(std::make_shared()); } else { // Get the index of boxgen configuration int confBoxIndex = std::stoi(mConfigs[index].substr(7)); - gens.push_back(std::make_unique(*mBoxGenConfigs[confBoxIndex])); + gens.push_back(std::make_shared(*mBoxGenConfigs[confBoxIndex])); } mGens.push_back(gen); } else if (gen.compare(0, 7, "pythia8") == 0) { // Check if mConfigs[index] contains pythia8_ and a number if (mConfigs[index].compare("") == 0) { auto pars = Pythia8GenConfig(); - gens.push_back(std::make_unique(pars)); + gens.push_back(std::make_shared(pars)); } else { // Get the index of pythia8 configuration int confPythia8Index = std::stoi(mConfigs[index].substr(8)); - gens.push_back(std::make_unique(*mPythia8GenConfigs[confPythia8Index])); + gens.push_back(std::make_shared(*mPythia8GenConfigs[confPythia8Index])); } mConfsPythia8.push_back(mConfigs[index]); mGens.push_back(gen); } else if (gen.compare("extkinO2") == 0) { int confO2KineIndex = std::stoi(mConfigs[index].substr(9)); - gens.push_back(std::make_unique(*mO2KineGenConfigs[confO2KineIndex])); + gens.push_back(std::make_shared(*mO2KineGenConfigs[confO2KineIndex])); mGens.push_back(gen); } else if (gen.compare("evtpool") == 0) { int confEvtPoolIndex = std::stoi(mConfigs[index].substr(8)); - gens.push_back(std::make_unique(mEventPoolConfigs[confEvtPoolIndex])); + gens.push_back(std::make_shared(mEventPoolConfigs[confEvtPoolIndex])); mGens.push_back(gen); } else if (gen.compare("external") == 0) { int confextIndex = std::stoi(mConfigs[index].substr(9)); - auto& extgen_filename = mExternalGenConfigs[confextIndex]->fileName; - auto& extgen_func = mExternalGenConfigs[confextIndex]->funcName; - auto extGen = std::unique_ptr(o2::conf::GetFromMacro(extgen_filename, extgen_func, "FairGenerator*", "extgen")); - if (!extGen) { - LOG(fatal) << "Failed to load external generator from " << extgen_filename << " with function " << extgen_func; - exit(1); + // we need analyse the ini file to update the config key param + if (mExternalGenConfigs[confextIndex]->iniFile.size() > 0) { + LOG(info) << "Setting up external gen using the given INI file"; + + // this means that we go via the ConfigurableParam system ---> in order not to interfere with other + // generators we use an approach with backup and restore of the system + + // we write the current state to a file + // create a tmp file name + std::string tmp_config_file = "configkey_tmp_backup_" + std::to_string(getpid()) + std::string(".ini"); + o2::conf::ConfigurableParam::writeINI(tmp_config_file); + + auto expandedFileName = o2::utils::expandShellVarsInFileName(mExternalGenConfigs[confextIndex]->iniFile); + o2::conf::ConfigurableParam::updateFromFile(expandedFileName); + // toDo: check that this INI file makes sense + + auto& params = GeneratorExternalParam::Instance(); + LOG(info) << "Setting up external generator with following parameters"; + LOG(info) << params; + auto extgen_filename = params.fileName; + auto extgen_func = params.funcName; + auto extgen = std::shared_ptr(o2::conf::GetFromMacro(extgen_filename, extgen_func, "FairGenerator*", "extgen")); + if (!extgen) { + LOG(fatal) << "Failed to retrieve \'extgen\': problem with configuration "; + } + // restore old state + o2::conf::ConfigurableParam::updateFromFile(tmp_config_file); + // delete tmp file + std::filesystem::remove(tmp_config_file); + + gens.push_back(std::move(extgen)); + mGens.push_back(gen); + } else { + LOG(info) << "Setting up external gen using the given fileName and funcName"; + // we need to restore the config key param system to what is was before + auto& extgen_filename = mExternalGenConfigs[confextIndex]->fileName; + auto& extgen_func = mExternalGenConfigs[confextIndex]->funcName; + auto extGen = std::shared_ptr(o2::conf::GetFromMacro(extgen_filename, extgen_func, "FairGenerator*", "extgen")); + if (!extGen) { + LOG(fatal) << "Failed to load external generator from " << extgen_filename << " with function " << extgen_func; + } + gens.push_back(std::move(extGen)); + mGens.push_back(gen); } - gens.push_back(std::move(extGen)); - mGens.push_back(gen); } else if (gen.compare("hepmc") == 0) { int confHepMCIndex = std::stoi(mConfigs[index].substr(6)); - gens.push_back(std::make_unique()); + gens.push_back(std::make_shared()); auto& globalConfig = o2::conf::SimConfig::Instance(); dynamic_cast(gens.back().get())->setup(*mFileOrCmdGenConfigs[confHepMCIndex], *mHepMCGenConfigs[confHepMCIndex], globalConfig); mGens.push_back(gen); @@ -105,6 +147,12 @@ GeneratorHybrid::GeneratorHybrid(const std::string& inputgens) } } +GeneratorHybrid::~GeneratorHybrid() +{ + LOG(info) << "Destructor of generator hybrid called"; + mStopFlag = true; +} + Bool_t GeneratorHybrid::Init() { // init all sub-gens @@ -127,7 +175,7 @@ Bool_t GeneratorHybrid::Init() LOG(info) << "Setting \'Pythia8\' base configuration: " << config << std::endl; dynamic_cast(gens[count].get())->setConfig(config); } - gens[count]->Init(); + gens[count]->Init(); // TODO: move this to multi-threaded addSubGenerator(count, gen); count++; } @@ -159,54 +207,135 @@ Bool_t GeneratorHybrid::Init() } else { LOG(info) << "Generators will be used in sequence, following provided fractions"; } + + mGenIsInitialized.resize(gens.size(), false); + if (mGenerationMode == GenMode::kParallel) { + // in parallel mode we just use one queue --> collaboration + mResultQueue.resize(1); + } else { + // in sequential mode we have one queue per generator + mResultQueue.resize(gens.size()); + } + // Create a task arena with a specified number of threads + mTaskArena.initialize(GeneratorHybridParam::Instance().num_workers); + + // the process task function actually calls event generation + // when it is done it notifies the outside world by pushing it's index into an appropriate queue + // This should be a lambda, which can be given at TaskPool creation time + auto process_generator_task = [this](std::vector> const& generatorvec, int task) { + LOG(debug) << "Starting eventgen for task " << task; + auto& generator = generatorvec[task]; + if (!mStopFlag) { + // TODO: activate this once we are use Init is threadsafe + // if (!mGenIsInitialized[task]) { + // if(!generator->Init()) { + // LOG(error) << "failed to init generator " << task; + // } + // mGenIsInitialized[task] = true; + // } + } + generator->clearParticles(); + generator->generateEvent(); + generator->importParticles(); + LOG(debug) << "eventgen finished for task " << task; + if (!mStopFlag) { + if (mGenerationMode == GenMode::kParallel) { + mResultQueue[0].push(task); + } else { + mResultQueue[task].push(task); + } + } + }; + + // fundamental tbb thread-worker function + auto worker_function = [this, process_generator_task]() { + // we increase the reference count in the generator pointers + // by making a copy of the vector. In this way we ensure that the lifetime + // of the generators is no shorter than the lifetime of the thread for this worker function + auto generators_copy = gens; + + while (!mStopFlag) { + int task; + if (mInputTaskQueue.try_pop(task)) { + process_generator_task(generators_copy, task); // Process the task + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Wait if no task + } + } + }; + + // let the TBB task system run in it's own thread + mTBBTaskPoolRunner = std::thread([this, worker_function]() { mTaskArena.execute([&]() { tbb::parallel_for(0, mTaskArena.max_concurrency(), [&](int) { worker_function(); }); }); }); + mTBBTaskPoolRunner.detach(); // detaching because we don't like to wait on the thread to finish + // some of the generators might still be generating when we are done + + // let's also push initial generation tasks for each event generator + for (size_t genindex = 0; genindex < gens.size(); ++genindex) { + mInputTaskQueue.push(genindex); + mTasksStarted++; + } + mIsInitialized = true; return Generator::Init(); } -Bool_t GeneratorHybrid::generateEvent() +bool GeneratorHybrid::generateEvent() { - // Order randomisation or sequence of generators - // following provided fractions. If not available generators will be used sequentially - if (mRandomize) { - if (mRngFractions.size() != 0) { - // Generate number between 0 and 1 - float rnum = gRandom->Rndm(); - // Find generator index - for (int k = 0; k < mRngFractions.size(); k++) { - if (rnum <= mRngFractions[k]) { - mIndex = k; - break; + if (!mIsInitialized) { + Init(); + } + if (mGenerationMode == GenMode::kParallel) { + mIndex = -1; // this means any index is welcome + notifySubGenerator(0); // we shouldn't distinguish the sub-gen ids + } else { + // Order randomisation or sequence of generators + // following provided fractions, if not generators are used in proper sequence + // Order randomisation or sequence of generators + // following provided fractions. If not available generators will be used sequentially + if (mRandomize) { + if (mRngFractions.size() != 0) { + // Generate number between 0 and 1 + float rnum = gRandom->Rndm(); + // Find generator index + for (int k = 0; k < mRngFractions.size(); k++) { + if (rnum <= mRngFractions[k]) { + mIndex = k; + break; + } } + } else { + mIndex = gRandom->Integer(mGens.size()); } } else { - mIndex = gRandom->Integer(mGens.size()); - } - } else { - while (mFractions[mCurrentFraction] == 0 || mseqCounter == mFractions[mCurrentFraction]) { - if (mFractions[mCurrentFraction] != 0) { - mseqCounter = 0; + while (mFractions[mCurrentFraction] == 0 || mseqCounter == mFractions[mCurrentFraction]) { + if (mFractions[mCurrentFraction] != 0) { + mseqCounter = 0; + } + mCurrentFraction = (mCurrentFraction + 1) % mFractions.size(); } - mCurrentFraction = (mCurrentFraction + 1) % mFractions.size(); + mIndex = mCurrentFraction; } - mIndex = mCurrentFraction; - } - if (mConfigs[mIndex].compare("") == 0) { - LOG(info) << "GeneratorHybrid: generating event with generator " << mGens[mIndex]; - } else { - LOG(info) << "GeneratorHybrid: generating event with generator " << mConfigs[mIndex]; + notifySubGenerator(mIndex); } - gens[mIndex]->clearParticles(); // clear container of this class - gens[mIndex]->generateEvent(); - // notify the sub event generator - notifySubGenerator(mIndex); - mseqCounter++; return true; } -Bool_t GeneratorHybrid::importParticles() +bool GeneratorHybrid::importParticles() { - mParticles.clear(); // clear container of mother class - gens[mIndex]->importParticles(); - std::copy(gens[mIndex]->getParticles().begin(), gens[mIndex]->getParticles().end(), std::back_insert_iterator(mParticles)); + int genIndex = -1; + 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); + } + LOG(info) << "Importing particles for task " << genIndex; + + // at this moment the mIndex-th generator is ready to be used + std::copy(gens[genIndex]->getParticles().begin(), gens[genIndex]->getParticles().end(), std::back_insert_iterator(mParticles)); + + mInputTaskQueue.push(genIndex); + mTasksStarted++; // we need to fix particles statuses --> need to enforce this on the importParticles level of individual generators for (auto& p : mParticles) { @@ -215,6 +344,12 @@ Bool_t GeneratorHybrid::importParticles() p.SetBit(ParticleStatus::kToBeDone, true); } + mseqCounter++; + mEventCounter++; + if (mEventCounter == mNEvents) { + LOG(info) << "HybridGen: Stopping TBB task pool"; + mStopFlag = true; + } return true; } @@ -244,6 +379,21 @@ Bool_t GeneratorHybrid::parseJSON(const std::string& path) return false; } + // check if there is a mode field + if (doc.HasMember("mode")) { + const auto& mode = doc["mode"].GetString(); + if (mode == "sequential") { + // events are generated in the order given by fractions or random weight + mGenerationMode = GenMode::kSeq; + } + if (mode == std::string("parallel")) { + // events are generated fully in parallel and the order will be random + // this is mainly for event pool generation or mono-type generators + mGenerationMode = GenMode::kParallel; + LOG(info) << "Setting mode to parallel"; + } + } + // Put the generator names in mInputGens if (doc.HasMember("generators")) { const auto& gens = doc["generators"]; diff --git a/Generators/src/GeneratorPythia8.cxx b/Generators/src/GeneratorPythia8.cxx index fef2c4d2e9a1c..7e546a840a8ca 100644 --- a/Generators/src/GeneratorPythia8.cxx +++ b/Generators/src/GeneratorPythia8.cxx @@ -42,6 +42,8 @@ namespace o2 namespace eventgen { +std::atomic GeneratorPythia8::Pythia8InstanceCounter; + /*****************************************************************/ /*****************************************************************/ @@ -57,6 +59,8 @@ GeneratorPythia8::GeneratorPythia8() : GeneratorPythia8(GeneratorPythia8Param::I GeneratorPythia8::GeneratorPythia8(Pythia8GenConfig const& config) : Generator("ALICEo2", "ALICEo2 Pythia8 Generator") { /** constructor **/ + mThisPythia8InstanceID = GeneratorPythia8::Pythia8InstanceCounter; + GeneratorPythia8::Pythia8InstanceCounter++; mInterface = reinterpret_cast(&mPythia); mInterfaceName = "pythia8"; @@ -116,7 +120,15 @@ void GeneratorPythia8::seedGenerator() // Otherwise will seed the generator with the state of // TRandom::GetSeed. This is the seed that is influenced from // SimConfig --seed command line options options. - seed = (gRandom->TRandom::GetSeed() % (MAX_SEED + 1)); + seed = gRandom->TRandom::GetSeed(); // this uses the "original" seed + // we advance the seed by one so that the next Pythia8 generator gets a different value + if (mThisPythia8InstanceID > 0) { + gRandom->Rndm(); + LOG(info) << "Multiple Pythia8 generator instances detected .. automatically adjusting seed further to avoid overlap "; + seed = seed ^ gRandom->GetSeed(); // this uses the "current" seed + } + // apply max seed cuttof + seed = seed % (MAX_SEED + 1); LOG(info) << "GeneratorPythia8: Using random seed from gRandom % 900000001: " << seed; } mPythia.readString("Random:setSeed on"); diff --git a/run/SimExamples/Hybrid_parallel/README.md b/run/SimExamples/Hybrid_parallel/README.md new file mode 100644 index 0000000000000..02e6b3b4c08a3 --- /dev/null +++ b/run/SimExamples/Hybrid_parallel/README.md @@ -0,0 +1,9 @@ + + +Demonstrating how the Hybrid generator can be setup with multiple clones of the same +generator to speedup event generation (for a single timeframe). + +- **run_parallel.sh** main example shell script +- **hybridconfig_parallel.json** → example JSON file for the hybrid generator configuration diff --git a/run/SimExamples/Hybrid_parallel/hybridconfig_extern_parallel.json b/run/SimExamples/Hybrid_parallel/hybridconfig_extern_parallel.json new file mode 100644 index 0000000000000..dfacbc56d90ea --- /dev/null +++ b/run/SimExamples/Hybrid_parallel/hybridconfig_extern_parallel.json @@ -0,0 +1,69 @@ +{ + "mode" : "parallel", + "generators": [ + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile" : "${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile" : "${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile" : "${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile" : "${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile" : "${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile" : "${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile" : "${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini" + } + }, + { + "name": "external", + "config": { + "fileName": "", + "funcName": "", + "iniFile" : "${O2DPG_ROOT}/MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_Mode2_OmegaC.ini" + } + } + ] +} diff --git a/run/SimExamples/Hybrid_parallel/hybridconfig_parallel.json b/run/SimExamples/Hybrid_parallel/hybridconfig_parallel.json new file mode 100644 index 0000000000000..9422e73500884 --- /dev/null +++ b/run/SimExamples/Hybrid_parallel/hybridconfig_parallel.json @@ -0,0 +1,53 @@ +{ + "mode": "parallel", + "generators": [ + { + "name": "pythia8", + "config": { + "config": "$O2_ROOT/share/Generators/egconfig/pythia8_inel.cfg", + "hooksFileName": "", + "hooksFuncName": "", + "includePartonEvent": false, + "particleFilter": "", + "verbose": 0 + } + }, + { + "name": "pythia8", + "config": { + "config": "$O2_ROOT/share/Generators/egconfig/pythia8_inel.cfg", + "hooksFileName": "", + "hooksFuncName": "", + "includePartonEvent": false, + "particleFilter": "", + "verbose": 0 + } + }, + { + "name": "pythia8", + "config": { + "config": "$O2_ROOT/share/Generators/egconfig/pythia8_inel.cfg", + "hooksFileName": "", + "hooksFuncName": "", + "includePartonEvent": false, + "particleFilter": "", + "verbose": 0 + } + }, + { + "name": "pythia8", + "config": { + "config": "$O2_ROOT/share/Generators/egconfig/pythia8_inel.cfg", + "hooksFileName": "", + "hooksFuncName": "", + "includePartonEvent": false, + "particleFilter": "", + "verbose": 0 + } + } + ], + "fractions": [ + 1, + 1 + ] +} diff --git a/run/SimExamples/Hybrid_parallel/run_extgen_parallel.sh b/run/SimExamples/Hybrid_parallel/run_extgen_parallel.sh new file mode 100755 index 0000000000000..ead47db321832 --- /dev/null +++ b/run/SimExamples/Hybrid_parallel/run_extgen_parallel.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# +# Hybrid generator simulation example in which multiple clones of the same generator +# are listed in a JSON file (hybridconfig_extern_parallel.json in this folder). These multiple +# clones are running in parallel to produce a targeted number of events faster. + +NEV=10 +WORKERS=8 + +# Starting simulation with Hybrid generator in parallel mode +${O2_ROOT}/bin/o2-sim --noGeant -j 1 --field ccdb --vertexMode kCCDB --run 300000 --configKeyValues "GeneratorHybrid.configFile=$PWD/hybridconfig_extern_parallel.json;GeneratorHybrid.randomize=false;GeneratorHybrid.num_workers=${WORKERS}" -g hybrid -o genevents_extern_parallel --timestamp 1546300800000 --seed 836302859 -n $NEV diff --git a/run/SimExamples/Hybrid_parallel/run_parallel.sh b/run/SimExamples/Hybrid_parallel/run_parallel.sh new file mode 100755 index 0000000000000..119b69dc93349 --- /dev/null +++ b/run/SimExamples/Hybrid_parallel/run_parallel.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# +# Hybrid generator simulation example in which multiple clones of the same generator +# are listed in a JSON file (hybridconfig_parallel.json in this folder). These multiple +# clones are running in parallel to produce events faster. + +NEV=100 +WORKERS=8 + +# Starting simulation with Hybrid generator in parallel mode +${O2_ROOT}/bin/o2-sim --noGeant -j 1 --field ccdb --vertexMode kCCDB --run 300000 --configKeyValues "GeneratorHybrid.configFile=$PWD/hybridconfig_parallel.json;GeneratorHybrid.randomize=false;GeneratorHybrid.num_workers=${WORKERS}" -g hybrid -o genevents_parallel --timestamp 1546300800000 --seed 836302859 -n $NEV diff --git a/run/SimExamples/README.md b/run/SimExamples/README.md index 139c24693b3ed..725d60c4854ca 100644 --- a/run/SimExamples/README.md +++ b/run/SimExamples/README.md @@ -16,6 +16,7 @@ * \subpage refrunSimExamplesHepMC_STARlight * \subpage refrunSimExamplesHepMC_EPOS4 * \subpage refrunSimExamplesHybrid +* \subpage refrunSimExamplesHybrid_parallel * \subpage refrunSimExamplesJet_Embedding_Pythia8 * \subpage refrunSimExamplesMcTracksToAOD * \subpage refrunSimExamplesMcTracksToAOD