From 07580073186dff721afcb9c0ccda78f6c5110f33 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Tue, 17 Dec 2024 19:14:16 +0100 Subject: [PATCH] Implementation of cocktail generation Works for sequence generation. SubFractions JSON syntax is implemented, but it does not work yet during the generation. Updated Hybrid example --- .../include/Generators/GeneratorHybrid.h | 5 + Generators/src/GeneratorHybrid.cxx | 121 +++++++++++++++--- run/SimExamples/Hybrid/README.md | 6 +- run/SimExamples/Hybrid/hybridconfig.json | 3 +- run/SimExamples/Hybrid_cocktail/README.md | 13 ++ .../Hybrid_cocktail/hybridcocktail.json | 69 ++++++++++ run/SimExamples/Hybrid_cocktail/runo2sim.sh | 68 ++++++++++ 7 files changed, 263 insertions(+), 22 deletions(-) create mode 100644 run/SimExamples/Hybrid_cocktail/README.md create mode 100644 run/SimExamples/Hybrid_cocktail/hybridcocktail.json create mode 100644 run/SimExamples/Hybrid_cocktail/runo2sim.sh diff --git a/Generators/include/Generators/GeneratorHybrid.h b/Generators/include/Generators/GeneratorHybrid.h index abce56f762f2a..3f6a85e498c8b 100644 --- a/Generators/include/Generators/GeneratorHybrid.h +++ b/Generators/include/Generators/GeneratorHybrid.h @@ -98,6 +98,11 @@ class GeneratorHybrid : public Generator int mEventCounter = 0; int mTasksStarted = 0; + // Cocktail mode + bool mCocktailMode = false; + std::vector> mGroups; + std::vector> mCocktailFractions; + // 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..833fe85f5a0a7 100644 --- a/Generators/src/GeneratorHybrid.cxx +++ b/Generators/src/GeneratorHybrid.cxx @@ -12,7 +12,7 @@ #include "Generators/GeneratorHybrid.h" #include #include - +#include #include #include #include @@ -42,16 +42,23 @@ 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; })) { LOG(fatal) << "All fractions provided are 0, no simulation will be performed"; return; } - } + } for (auto gen : mInputGens) { // Search if the generator name is inside generatorNames (which is a vector of strings) LOG(info) << "Checking if generator " << gen << " is in the list of available generators \n"; @@ -303,7 +310,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 +329,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 +377,7 @@ bool GeneratorHybrid::importParticles() LOG(info) << "HybridGen: Stopping TBB task pool"; mStopFlag = true; } + return true; } @@ -465,6 +495,63 @@ Bool_t GeneratorHybrid::parseJSON(const std::string& path) } } + if (doc.HasMember("cocktail")) { + mCocktailMode = true; + const auto& groups = doc["cocktail"]; + for (const auto& group : groups.GetArray()) { + if (!group.HasMember("group")) { + LOG(fatal) << "Group not provided for cocktail"; + return false; + } + const auto& sets = group["group"]; + mGroups.push_back({}); + for (const auto& subset : sets.GetArray()) { + mGroups.back().push_back(subset.GetInt()); + } + if(group.HasMember("fractions")) { + const auto& subfrac = group["fractions"]; + mCocktailFractions.push_back({}); + for (const auto& frac : subfrac.GetArray()) { + mCocktailFractions.back().push_back(frac.GetInt()); + } + if(mGroups.back().size() != mCocktailFractions.back().size()) { + LOG(fatal) << "Number of fractions does not match the number of generators in the cocktail"; + return false; + } + } else { + LOG(warn) << "Fractions not provided for cocktail group " << mGroups.size()-1 << " setting all fractions to 1"; + mCocktailFractions.push_back({}); + for (int i = 0; i < mGroups.back().size(); i++) { + mCocktailFractions.back().push_back(1); + } + } + } + // Check if mGroups items contain the same number twice and ensure all items are unique across groups + std::unordered_set allItems; + for (const auto& group : mGroups) { + std::unordered_set uniqueItems; + for (const auto& item : group) { + if (!uniqueItems.insert(item).second) { + LOG(fatal) << "Duplicate generator index " << item << " found in a cocktail group"; + return false; + } + if (!allItems.insert(item).second) { + LOG(fatal) << "Duplicate generator index " << item << " found in the cocktails"; + return false; + } + } + } + // Check if allItems is missing any generator index and create single gen groups for them + // Fractions for these generators are the back ones in mFractions + for (int i = 0; i < mInputGens.size(); i++) { + if (allItems.find(i) == allItems.end()) { + LOG(info) << "Generator index " << i << " is missing in the cocktails"; + LOG(info) << "Setting up a group for generator " << mInputGens[i]; + mGroups.push_back({i}); + } + } + } + // Get fractions and put them in mFractions if (doc.HasMember("fractions")) { const auto& fractions = doc["fractions"]; diff --git a/run/SimExamples/Hybrid/README.md b/run/SimExamples/Hybrid/README.md index 3c3cba37748bf..835c7d25c2dea 100644 --- a/run/SimExamples/Hybrid/README.md +++ b/run/SimExamples/Hybrid/README.md @@ -2,7 +2,7 @@ \page refrunSimExamplesHybrid Example Hybrid /doxy --> -The usage of the Hybrid generator with the o2-sim is presented in this short manual. +The usage of the Hybrid generator with o2-sim is presented in this short manual. All the other generators are implemented as sub-generators and they can be called thanks to a JSON file, fed to o2-sim via the GeneratorHybrid.configFile parameter. The O2sim package needs to be loaded in order to use this example. @@ -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..44a8d891eef07 --- /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. Subfractions are still not implemented, but the JSON syntax to be used is already in place. For now each generator will be pulled exactly once 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..26a345a9754da --- /dev/null +++ b/run/SimExamples/Hybrid_cocktail/hybridcocktail.json @@ -0,0 +1,69 @@ +{ + "generators": [ + { + "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": "" + } + }, + { + "name": "pythia8pp" + }, + { + "name": "extkinO2", + "config": { + "skipNonTrackable": true, + "continueMode": false, + "roundRobin": false, + "randomize": false, + "rngseed": 0, + "randomphi": false, + "fileName": "${PWD}/evtpool.root" + } + }, + { + "name": "pythia8hf", + "config": "" + } + ], + "cocktail": [ + { + "group": [ + 0, + 1 + ], + "fractions": [ + 1, + 1 + ] + }, + { + "group": [ + 2, + 3 + ], + "fractions": [ + 1, + 1 + ] + } + ], + "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