diff --git a/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C b/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C index 6ca884dda..5c3afd206 100644 --- a/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C +++ b/MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C @@ -4,6 +4,7 @@ R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) #include "Pythia8/Pythia.h" #include "Generators/GeneratorPythia8Param.h" #include "CommonUtils/FileSystemUtils.h" +#include // Pythia8 generator with POWHEG // // Author: Marco Giacalone (marco.giacalone@cern.ch) @@ -13,7 +14,7 @@ R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT) generator/generator_pythia8_powheg.C;GeneratorExternal.funcName=\ getGeneratorJEPythia8POWHEG(\"powheg.input\",\"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg\")" // or with iniFile -// o2-sim -g external --noGeant -n 2 -j 8 --configFile $O2DPG_MC_CONFIG_ROOT/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG.ini +// o2-sim -g external --noGeant -n 2 -j 8 --configFile $O2DPG_MC_CONFIG_ROOT/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty.ini namespace o2 { @@ -22,10 +23,11 @@ namespace eventgen using namespace Pythia8; -// Pythia8 generator using POWHEG data that are generated during the initialization -// of the external generator. The POWHEG configuration file is copied to the current -// directory with the right name and the POWHEG events are generated using the executable -// specified via the type parameter, namely: +// Pythia8 generator using POWHEG data that are generated partially during the initialization +// of the external generator and then during the generateEvent when mNMaxPerJob is reached. The first time +// all the configuration files are created so that the other jobs can be run much faster (and in parallel in the future) +// The POWHEG configuration file is copied to the current directory with the right name and the POWHEG events are generated +// using the executable specified via the type parameter, namely: // 0: pwhg_main_hvq // 1: pwhg_main_W // 2: pwhg_main_Z @@ -35,7 +37,7 @@ class GeneratorJEPythia8POWHEG : public o2::eventgen::GeneratorPythia8 { public: /// default constructor - GeneratorJEPythia8POWHEG(std::string confpath = "pwgpath", short int type = 0) + GeneratorJEPythia8POWHEG(std::string confpath = "pwgpath", short int type = 0, int maxEventsPerJob = 50) { // Assign events to generate with POWHEG unsigned int nPowhegEvents = getTotalNEvents(); @@ -43,23 +45,58 @@ public: LOG(fatal) << "Number of events not set or set to 0."; exit(1); } - // Check on nEvents to generate with POWHEG - // due to integer limit hardcoded in the generator - if (nPowhegEvents > std::numeric_limits::max()) { - LOG(fatal) << "Number of events for POWHEG exceeds the maximum allowed value"; + // POWHEG has an integer limit hardcoded for the nEvents, but + // with the multiple jobs setup this is not an issue (an error will automatically be thrown) + mNMaxPerJob = maxEventsPerJob; + if (mNMaxPerJob < 1) { + LOG(fatal) << "Number of events per job are set to 0 or lower."; exit(1); } + mNFiles = nPowhegEvents / mNMaxPerJob; + if (nPowhegEvents % mNMaxPerJob != 0) + { + mNFiles++; + } + gRandom->SetSeed(0); + if(!confMaker(confpath)) + { + LOG(fatal) << "Failed to edit POWHEG configuration file"; + exit(1); + } + mPowhegConf = confpath; + // Get POWHEG executable to use + if (type >= mPowhegGen.size()) { + LOG(warn) << "Available POWHEG generators are:"; + for (int k = 0; k < mPowhegGen.size(); k++) + { + LOG(warn) << "\t" << k << ": " << mPowhegGen[k]; + } + LOG(fatal) << "POWHEG generator type " << type << " not found"; + exit(1); + } else { + LOG(info) << "Running POWHEG using the " << mPowhegGen[type] << " executable"; + // Generate the POWHEG events + mExePOW = mPowhegGen[type] + " &"; + system(mExePOW.c_str()); + } + }; + + Bool_t confMaker(std::string confpath = "pwgpath", bool parallel = false) + { // Check if file exist and is not empty if (std::filesystem::exists(confpath) && std::filesystem::file_size(confpath) > 0) { // Copy the file to the current directory ifstream src(confpath); ofstream dst("powheg.input"); - gRandom->SetSeed(0); int seed = gRandom->Integer(900000000); bool isseed = false; bool isnumevts = false; + if (mCurrFile == mNFiles - 1 && getTotalNEvents() % mNMaxPerJob != 0) { + mNMaxPerJob = getTotalNEvents() % mNMaxPerJob; + } std::string line; - while (std::getline(src, line)) { + while (std::getline(src, line)) + { if (line.find("iseed") != std::string::npos) { // Set the seed to the random number @@ -69,42 +106,129 @@ public: if (line.find("numevts") != std::string::npos) { // Set the number of events to the number of events defined in the configuration - line = "numevts " + std::to_string(nPowhegEvents); + line = "numevts " + std::to_string(mNMaxPerJob); // replace it in the file isnumevts = true; } dst << line << std::endl; } - if (!isseed) { + if (!isseed) + { dst << "iseed " << seed << std::endl; } - if (!isnumevts) { - dst << "numevts " << nPowhegEvents << std::endl; + if (!isnumevts) + { + dst << "numevts " << mNMaxPerJob << std::endl; + } + if (parallel) + { + dst << "manyseeds 1" << std::endl; // Enables the usage of pwgseeds.dat file to set the seed in parallel mode + dst << "parallelstage 4" << std::endl; // Allows event generation based on pre-generated POWHEG configuration files (needed for certain configurations) } src.close(); dst.close(); } else { LOG(fatal) << "POWHEG configuration file not found or empty" << std::endl; - exit(1); + return false; } - // Get POWHEG executable to use - if (type >= mPowhegGen.size()) { - LOG(warn) << "Available POWHEG generators are:"; - for (int k = 0; k < mPowhegGen.size(); k++) - { - LOG(warn) << "\t" << k << ": " << mPowhegGen[k]; + return true; + } + + Bool_t startPOW() + { + if(mCurrFile == 1) { + if (!confMaker(mPowhegConf, true)) { + LOG(fatal) << "Failed to edit POWHEG configuration with parallelisation"; + return false; } - LOG(fatal) << "POWHEG generator type " << type << " not found"; - exit(1); + } + LOG(info) << "Starting POWHEG job " << mCurrFile+1 << " of " << mNFiles; + system(("echo " + std::to_string(mCurrFile - 1) + " | " + mExePOW).c_str()); + return true; + } + + Bool_t checkEOF() { + // Check if the POWHEG generation is done + int result = system(("grep -q /LesHouchesEvents " + mLHEFoutput).c_str()); + if (result == 0) + { + return true; } else { - LOG(info) << "Running POWHEG using the " << mPowhegGen[type] << " executable"; - // Generate the POWHEG events - system(mPowhegGen[type].c_str()); + return false; + } + } + + Bool_t POWchecker() { + // Check if the POWHEG events file exists + LOG(info) << "Waiting for " << mLHEFoutput << " to exist"; + while (!std::filesystem::exists(mLHEFoutput.c_str())) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + LOG(info) << "POWHEG events file for job " << mCurrFile << " found"; + while (!checkEOF()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + LOG(info) << "POWHEG events ready"; + return true; + } + + // Check for the POWHEG events file existance + Bool_t Init() override + { + // Check if the POWHEG events file exists + if(POWchecker()) { + return GeneratorPythia8::Init(); + } else { + return false; + } + }; + + // Set Generator ReadEvent to wait for the POWHEG events + Bool_t generateEvent() override + { + /** Reinitialise when EOF is reached **/ + if (mPythia.info.atEndOfFile()) + { + if(mCurrFile == 0) + { + mPythia.readString("Beams:newLHEFsameInit = on"); + // Create pwgseeds.dat file with a random seed for each line + std::ofstream seedfile("pwgseeds.dat"); + for (int i = 0; i < mNFiles - 1; i++) + { + seedfile << gRandom->Integer(900000000) << std::endl; + } + seedfile.close(); + } + mCurrFile++; + mLHEFoutput = Form("pwgevents-%04d.lhe", mCurrFile - 1); + mPythia.readString(Form("Beams:LHEF = %s", mLHEFoutput.c_str())); + if(!startPOW()) + { + return false; + } + if (POWchecker()) { + // If Pythia fails to initialize, exit with error. + if (!mPythia.init()) + { + LOG(fatal) << "Failed to init \'Pythia8\': init returned with error"; + return false; + } + } } + return GeneratorPythia8::generateEvent(); }; private: const std::vector mPowhegGen = {"pwhg_main_hvq", "pwhg_main_W", "pwhg_main_Z", "pwhg_main_dijet", "pwhg_main_directphoton"}; // POWHEG executables + short int mNFiles = 1; + short int mCurrFile = 0; + std::string mExePOW = ""; + std::string mPowhegConf = ""; + std::string mLHEFoutput = "pwgevents.lhe"; + int mNMaxPerJob = 50; }; } // namespace eventgen @@ -112,13 +236,13 @@ private: /** generator instance and settings **/ -FairGenerator *getGeneratorJEPythia8POWHEG(std::string powhegconf = "pwgpath", std::string pythia8conf = "", short int type = 0) +FairGenerator *getGeneratorJEPythia8POWHEG(std::string powhegconf = "pwgpath", std::string pythia8conf = "", short int type = 0, int maxEventsPerJob = 1e4) { using namespace o2::eventgen; // Expand paths for the POWHEG configuration file powhegconf = o2::utils::expandShellVarsInFileName(powhegconf); LOG(info) << "Using POWHEG configuration file: " << powhegconf; - auto myGen = new GeneratorJEPythia8POWHEG(powhegconf, type); + auto myGen = new GeneratorJEPythia8POWHEG(powhegconf, type, maxEventsPerJob); if(GeneratorPythia8Param::Instance().config.empty() && pythia8conf.empty()) { LOG(fatal) << "No configuration provided for Pythia8"; } @@ -131,4 +255,4 @@ FairGenerator *getGeneratorJEPythia8POWHEG(std::string powhegconf = "pwgpath", s myGen->setConfig(pythia8conf); } return myGen; -} \ No newline at end of file +} diff --git a/test/run_generator_tests.sh b/test/run_generator_tests.sh index 551f61261..c977f49c1 100755 --- a/test/run_generator_tests.sh +++ b/test/run_generator_tests.sh @@ -161,8 +161,11 @@ add_ini_files_from_macros() # given a list of macros, collect all INI files which contain at least one of them local macro_files=$@ for mf in ${macro_files} ; do - # if any, strip the leading O2DPG_ROOT path to only grep for the relative trailing path - mf=${mf##${O2DPG_ROOT}/} + # Strip anything before MC/config/, if any, to get the macro relative path + if [[ "${mf}" == *"MC/config"* ]] ; then + mf=${mf#*MC/config/} + mf="MC/config/${mf}" + fi local other_ini_files=$(grep -r -l ${mf} | grep ".ini$") # so this macro is not included in any of the INI file, # maybe it is included by another macro which is then included in an INI file