Skip to content

Commit b001ad0

Browse files
authored
Edited POWHEG using parallel generation and manyseeds (#2008)
* Edited POWHEG using parallel generation and manyseeds * Fix macros changes detection * EOF checker for LHE events
1 parent 8edc590 commit b001ad0

File tree

2 files changed

+160
-33
lines changed

2 files changed

+160
-33
lines changed

MC/config/PWGGAJE/external/generator/generator_pythia8_powheg.C

Lines changed: 155 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT)
44
#include "Pythia8/Pythia.h"
55
#include "Generators/GeneratorPythia8Param.h"
66
#include "CommonUtils/FileSystemUtils.h"
7+
#include <thread>
78
// Pythia8 generator with POWHEG
89
//
910
// Author: Marco Giacalone (marco.giacalone@cern.ch)
@@ -13,7 +14,7 @@ R__ADD_INCLUDE_PATH($O2DPG_MC_CONFIG_ROOT)
1314
generator/generator_pythia8_powheg.C;GeneratorExternal.funcName=\
1415
getGeneratorJEPythia8POWHEG(\"powheg.input\",\"${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGGAJE/pythia8/generator/pythia8_powheg.cfg\")"
1516
// or with iniFile
16-
// o2-sim -g external --noGeant -n 2 -j 8 --configFile $O2DPG_MC_CONFIG_ROOT/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG.ini
17+
// o2-sim -g external --noGeant -n 2 -j 8 --configFile $O2DPG_MC_CONFIG_ROOT/MC/config/PWGGAJE/ini/GeneratorPythia8POWHEG_beauty.ini
1718

1819
namespace o2
1920
{
@@ -22,10 +23,11 @@ namespace eventgen
2223

2324
using namespace Pythia8;
2425

25-
// Pythia8 generator using POWHEG data that are generated during the initialization
26-
// of the external generator. The POWHEG configuration file is copied to the current
27-
// directory with the right name and the POWHEG events are generated using the executable
28-
// specified via the type parameter, namely:
26+
// Pythia8 generator using POWHEG data that are generated partially during the initialization
27+
// of the external generator and then during the generateEvent when mNMaxPerJob is reached. The first time
28+
// all the configuration files are created so that the other jobs can be run much faster (and in parallel in the future)
29+
// The POWHEG configuration file is copied to the current directory with the right name and the POWHEG events are generated
30+
// using the executable specified via the type parameter, namely:
2931
// 0: pwhg_main_hvq
3032
// 1: pwhg_main_W
3133
// 2: pwhg_main_Z
@@ -35,31 +37,66 @@ class GeneratorJEPythia8POWHEG : public o2::eventgen::GeneratorPythia8
3537
{
3638
public:
3739
/// default constructor
38-
GeneratorJEPythia8POWHEG(std::string confpath = "pwgpath", short int type = 0)
40+
GeneratorJEPythia8POWHEG(std::string confpath = "pwgpath", short int type = 0, int maxEventsPerJob = 50)
3941
{
4042
// Assign events to generate with POWHEG
4143
unsigned int nPowhegEvents = getTotalNEvents();
4244
if (nPowhegEvents == 0) {
4345
LOG(fatal) << "Number of events not set or set to 0.";
4446
exit(1);
4547
}
46-
// Check on nEvents to generate with POWHEG
47-
// due to integer limit hardcoded in the generator
48-
if (nPowhegEvents > std::numeric_limits<int>::max()) {
49-
LOG(fatal) << "Number of events for POWHEG exceeds the maximum allowed value";
48+
// POWHEG has an integer limit hardcoded for the nEvents, but
49+
// with the multiple jobs setup this is not an issue (an error will automatically be thrown)
50+
mNMaxPerJob = maxEventsPerJob;
51+
if (mNMaxPerJob < 1) {
52+
LOG(fatal) << "Number of events per job are set to 0 or lower.";
5053
exit(1);
5154
}
55+
mNFiles = nPowhegEvents / mNMaxPerJob;
56+
if (nPowhegEvents % mNMaxPerJob != 0)
57+
{
58+
mNFiles++;
59+
}
60+
gRandom->SetSeed(0);
61+
if(!confMaker(confpath))
62+
{
63+
LOG(fatal) << "Failed to edit POWHEG configuration file";
64+
exit(1);
65+
}
66+
mPowhegConf = confpath;
67+
// Get POWHEG executable to use
68+
if (type >= mPowhegGen.size()) {
69+
LOG(warn) << "Available POWHEG generators are:";
70+
for (int k = 0; k < mPowhegGen.size(); k++)
71+
{
72+
LOG(warn) << "\t" << k << ": " << mPowhegGen[k];
73+
}
74+
LOG(fatal) << "POWHEG generator type " << type << " not found";
75+
exit(1);
76+
} else {
77+
LOG(info) << "Running POWHEG using the " << mPowhegGen[type] << " executable";
78+
// Generate the POWHEG events
79+
mExePOW = mPowhegGen[type] + " &";
80+
system(mExePOW.c_str());
81+
}
82+
};
83+
84+
Bool_t confMaker(std::string confpath = "pwgpath", bool parallel = false)
85+
{
5286
// Check if file exist and is not empty
5387
if (std::filesystem::exists(confpath) && std::filesystem::file_size(confpath) > 0) {
5488
// Copy the file to the current directory
5589
ifstream src(confpath);
5690
ofstream dst("powheg.input");
57-
gRandom->SetSeed(0);
5891
int seed = gRandom->Integer(900000000);
5992
bool isseed = false;
6093
bool isnumevts = false;
94+
if (mCurrFile == mNFiles - 1 && getTotalNEvents() % mNMaxPerJob != 0) {
95+
mNMaxPerJob = getTotalNEvents() % mNMaxPerJob;
96+
}
6197
std::string line;
62-
while (std::getline(src, line)) {
98+
while (std::getline(src, line))
99+
{
63100
if (line.find("iseed") != std::string::npos)
64101
{
65102
// Set the seed to the random number
@@ -69,56 +106,143 @@ public:
69106
if (line.find("numevts") != std::string::npos)
70107
{
71108
// Set the number of events to the number of events defined in the configuration
72-
line = "numevts " + std::to_string(nPowhegEvents);
109+
line = "numevts " + std::to_string(mNMaxPerJob);
73110
// replace it in the file
74111
isnumevts = true;
75112
}
76113
dst << line << std::endl;
77114
}
78-
if (!isseed) {
115+
if (!isseed)
116+
{
79117
dst << "iseed " << seed << std::endl;
80118
}
81-
if (!isnumevts) {
82-
dst << "numevts " << nPowhegEvents << std::endl;
119+
if (!isnumevts)
120+
{
121+
dst << "numevts " << mNMaxPerJob << std::endl;
122+
}
123+
if (parallel)
124+
{
125+
dst << "manyseeds 1" << std::endl; // Enables the usage of pwgseeds.dat file to set the seed in parallel mode
126+
dst << "parallelstage 4" << std::endl; // Allows event generation based on pre-generated POWHEG configuration files (needed for certain configurations)
83127
}
84128
src.close();
85129
dst.close();
86130
} else {
87131
LOG(fatal) << "POWHEG configuration file not found or empty" << std::endl;
88-
exit(1);
132+
return false;
89133
}
90-
// Get POWHEG executable to use
91-
if (type >= mPowhegGen.size()) {
92-
LOG(warn) << "Available POWHEG generators are:";
93-
for (int k = 0; k < mPowhegGen.size(); k++)
94-
{
95-
LOG(warn) << "\t" << k << ": " << mPowhegGen[k];
134+
return true;
135+
}
136+
137+
Bool_t startPOW()
138+
{
139+
if(mCurrFile == 1) {
140+
if (!confMaker(mPowhegConf, true)) {
141+
LOG(fatal) << "Failed to edit POWHEG configuration with parallelisation";
142+
return false;
96143
}
97-
LOG(fatal) << "POWHEG generator type " << type << " not found";
98-
exit(1);
144+
}
145+
LOG(info) << "Starting POWHEG job " << mCurrFile+1 << " of " << mNFiles;
146+
system(("echo " + std::to_string(mCurrFile - 1) + " | " + mExePOW).c_str());
147+
return true;
148+
}
149+
150+
Bool_t checkEOF() {
151+
// Check if the POWHEG generation is done
152+
int result = system(("grep -q /LesHouchesEvents " + mLHEFoutput).c_str());
153+
if (result == 0)
154+
{
155+
return true;
99156
} else {
100-
LOG(info) << "Running POWHEG using the " << mPowhegGen[type] << " executable";
101-
// Generate the POWHEG events
102-
system(mPowhegGen[type].c_str());
157+
return false;
158+
}
159+
}
160+
161+
Bool_t POWchecker() {
162+
// Check if the POWHEG events file exists
163+
LOG(info) << "Waiting for " << mLHEFoutput << " to exist";
164+
while (!std::filesystem::exists(mLHEFoutput.c_str()))
165+
{
166+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
167+
}
168+
LOG(info) << "POWHEG events file for job " << mCurrFile << " found";
169+
while (!checkEOF())
170+
{
171+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
172+
}
173+
LOG(info) << "POWHEG events ready";
174+
return true;
175+
}
176+
177+
// Check for the POWHEG events file existance
178+
Bool_t Init() override
179+
{
180+
// Check if the POWHEG events file exists
181+
if(POWchecker()) {
182+
return GeneratorPythia8::Init();
183+
} else {
184+
return false;
185+
}
186+
};
187+
188+
// Set Generator ReadEvent to wait for the POWHEG events
189+
Bool_t generateEvent() override
190+
{
191+
/** Reinitialise when EOF is reached **/
192+
if (mPythia.info.atEndOfFile())
193+
{
194+
if(mCurrFile == 0)
195+
{
196+
mPythia.readString("Beams:newLHEFsameInit = on");
197+
// Create pwgseeds.dat file with a random seed for each line
198+
std::ofstream seedfile("pwgseeds.dat");
199+
for (int i = 0; i < mNFiles - 1; i++)
200+
{
201+
seedfile << gRandom->Integer(900000000) << std::endl;
202+
}
203+
seedfile.close();
204+
}
205+
mCurrFile++;
206+
mLHEFoutput = Form("pwgevents-%04d.lhe", mCurrFile - 1);
207+
mPythia.readString(Form("Beams:LHEF = %s", mLHEFoutput.c_str()));
208+
if(!startPOW())
209+
{
210+
return false;
211+
}
212+
if (POWchecker()) {
213+
// If Pythia fails to initialize, exit with error.
214+
if (!mPythia.init())
215+
{
216+
LOG(fatal) << "Failed to init \'Pythia8\': init returned with error";
217+
return false;
218+
}
219+
}
103220
}
221+
return GeneratorPythia8::generateEvent();
104222
};
105223

106224
private:
107225
const std::vector<std::string> mPowhegGen = {"pwhg_main_hvq", "pwhg_main_W", "pwhg_main_Z", "pwhg_main_dijet", "pwhg_main_directphoton"}; // POWHEG executables
226+
short int mNFiles = 1;
227+
short int mCurrFile = 0;
228+
std::string mExePOW = "";
229+
std::string mPowhegConf = "";
230+
std::string mLHEFoutput = "pwgevents.lhe";
231+
int mNMaxPerJob = 50;
108232
};
109233

110234
} // namespace eventgen
111235
} // namespace o2
112236

113237
/** generator instance and settings **/
114238

115-
FairGenerator *getGeneratorJEPythia8POWHEG(std::string powhegconf = "pwgpath", std::string pythia8conf = "", short int type = 0)
239+
FairGenerator *getGeneratorJEPythia8POWHEG(std::string powhegconf = "pwgpath", std::string pythia8conf = "", short int type = 0, int maxEventsPerJob = 1e4)
116240
{
117241
using namespace o2::eventgen;
118242
// Expand paths for the POWHEG configuration file
119243
powhegconf = o2::utils::expandShellVarsInFileName(powhegconf);
120244
LOG(info) << "Using POWHEG configuration file: " << powhegconf;
121-
auto myGen = new GeneratorJEPythia8POWHEG(powhegconf, type);
245+
auto myGen = new GeneratorJEPythia8POWHEG(powhegconf, type, maxEventsPerJob);
122246
if(GeneratorPythia8Param::Instance().config.empty() && pythia8conf.empty()) {
123247
LOG(fatal) << "No configuration provided for Pythia8";
124248
}
@@ -131,4 +255,4 @@ FairGenerator *getGeneratorJEPythia8POWHEG(std::string powhegconf = "pwgpath", s
131255
myGen->setConfig(pythia8conf);
132256
}
133257
return myGen;
134-
}
258+
}

test/run_generator_tests.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,11 @@ add_ini_files_from_macros()
161161
# given a list of macros, collect all INI files which contain at least one of them
162162
local macro_files=$@
163163
for mf in ${macro_files} ; do
164-
# if any, strip the leading O2DPG_ROOT path to only grep for the relative trailing path
165-
mf=${mf##${O2DPG_ROOT}/}
164+
# Strip anything before MC/config/, if any, to get the macro relative path
165+
if [[ "${mf}" == *"MC/config"* ]] ; then
166+
mf=${mf#*MC/config/}
167+
mf="MC/config/${mf}"
168+
fi
166169
local other_ini_files=$(grep -r -l ${mf} | grep ".ini$")
167170
# so this macro is not included in any of the INI file,
168171
# maybe it is included by another macro which is then included in an INI file

0 commit comments

Comments
 (0)