1+ #!/usr/bin/env python3
2+
3+ import argparse
4+ import os
5+ import json
6+ from os .path import join , abspath , exists
7+ import re
8+ import subprocess
9+ import ROOT as root
10+
11+
12+ def clean (dir ,name ,printtext ):
13+ if printtext :
14+ print ("==> Delete simulation artefacts in the output directory\n " )
15+ for f in os .listdir (dir ):
16+ pattern_del = name + "_|o2simtopology|MCStepLogger"
17+ #pattern_keep="Kine.root|serverlog|mergerlog|workerlog"
18+ pattern_keep = "Kine.root|serverlog"
19+ if (re .search (pattern_del , f )) and (not re .search (pattern_keep , f ) ):
20+ os .remove (abspath (join (dir ,f )))
21+
22+
23+ def read_sim_config (args ):
24+ IniName = "GeneratorEMCocktail.ini"
25+ generatorName = "${O2DPG_ROOT}/MC/config/PWGEM/external/generator/GeneratorEMCocktailV2.C"
26+ generatorFunc = "GenerateEMCocktail"
27+
28+ # matches GeneratorParamEMlibV2.h
29+ GeneratorParamEMlibV2_CollisionSystem = {"kpp900GeV" :0x000 , "kpp2760GeV" :0x64 , "kpp7TeV" :0xC8 , "kpPb" :0x12C , "kPbPb" :0x190 }
30+ GeneratorParamEMlibV2_Centrality = {"kpp" :0x0 , "k0005" :0x1 , "k0510" :0x2 , "k1020" :0x3 , "k2030" :0x4 , "k3040" :0x5 , "k4050" :0x6 , "k5060" :0x7 , "k0010" :0x8 , "k2040" :0x9 , "k4060" :0xA , "k6080" :0xB , "k0020" :0xC , "k0040" :0xD , "k2080" :0xE , "k4080" :0xF , "k2050" :0x10 , "kCentralities" :0x11 }
31+
32+ allKeys = ["collisionSystem" , "centrality" , "decayMode" ,"selectedMothers" , "paramFile" , "paramFileDir" , "numberOfParticles" , "minPt" , "maxPt" , "pythiaErrorTolerance" , "externalDecayer" , "decayLongLived" , "dynamicalPtRange" , "useYWeights" , "paramV2FileDir" , "toFixEP" , "yGenRange" , "useLMeeDecaytable" , "weightingMode" ]
33+ if not exists (args .sim_config_file ):
34+ print ("ERROR: File " + args .sim_config_file + " not found" )
35+ return 1
36+
37+ with open (args .sim_config_file , "r" ) as f :
38+ simConfigs = json .load (f )
39+
40+ print ("==> Reading simualtion configuration from " + abspath (args .sim_config_file ))
41+
42+ missing_key = False
43+ for key in allKeys :
44+ if not key in simConfigs :
45+ print ("ERROR: Missing key \' " + key + "\' " )
46+ missing_key = True
47+ if missing_key :
48+ return 1
49+
50+ collisionSystem = GeneratorParamEMlibV2_CollisionSystem [simConfigs ["collisionSystem" ]]
51+ centrality = GeneratorParamEMlibV2_Centrality [simConfigs ["centrality" ]]
52+ decayMode = simConfigs ["decayMode" ]
53+ selectedMothers = simConfigs ["selectedMothers" ]
54+ paramFile = simConfigs ["paramFile" ]
55+ paramFileDir = simConfigs ["paramFileDir" ]
56+ numberOfParticles = simConfigs ["numberOfParticles" ]
57+ minPt = simConfigs ["minPt" ]
58+ maxPt = simConfigs ["maxPt" ]
59+ pythiaErrorTolerance = simConfigs ["pythiaErrorTolerance" ]
60+ externalDecayer = simConfigs ["externalDecayer" ]
61+ decayLongLived = simConfigs ["decayLongLived" ]
62+ dynamicalPtRange = simConfigs ["dynamicalPtRange" ]
63+ useYWeights = simConfigs ["useYWeights" ]
64+ paramV2FileDir = simConfigs ["paramV2FileDir" ]
65+ toFixEP = simConfigs ["toFixEP" ]
66+ yGenRange = simConfigs ["yGenRange" ]
67+ useLMeeDecaytable = simConfigs ["useLMeeDecaytable" ]
68+ weightingMode = simConfigs ["weightingMode" ]
69+
70+ print (f"collisionSystem\t \t { collisionSystem } \n centrality\t \t { centrality } \n decayMode\t \t { decayMode } \n selectedMothers\t \t { selectedMothers } \n paramFile\t \t { paramFile } \n paramFileDir\t \t { paramFileDir } \n numberOfParticles\t { numberOfParticles } \n minPt\t \t \t { minPt } \n maxPt\t \t \t { maxPt } \n pythiaErrorTolerance\t { pythiaErrorTolerance } " )
71+ print (f"externalDecayer\t \t { externalDecayer } \n decayLongLived\t \t { decayLongLived } \n dynamicalPtRange\t { dynamicalPtRange } \n useYWeights\t \t { useYWeights } \n paramV2FileDir\t { paramV2FileDir } \n toFixEP\t \t \t { toFixEP } \n yGenRange\t \t { yGenRange } " )
72+ print (f"useLMeeDecaytable\t { useLMeeDecaytable } \n weightingMode\t \t { weightingMode } " )
73+ ini_file = abspath (join (args .output ,IniName ))
74+ print ("==> Writing simulation configuration to " + ini_file )
75+ os .makedirs (os .path .dirname (ini_file ), exist_ok = True )
76+ with open (f"{ ini_file } " ,'w' ) as f :
77+ f .write (f"[GeneratorExternal]\n fileName = { generatorName } \n funcName={ generatorFunc } (" )
78+ f .write (f"{ collisionSystem } ,{ centrality } ,{ decayMode } ,{ selectedMothers } ,\" { paramFile } \" ,\" { paramFileDir } \" ,{ numberOfParticles } ,{ minPt } ,{ maxPt } ,{ pythiaErrorTolerance } ,{ externalDecayer } ,{ decayLongLived } ,{ dynamicalPtRange } ,{ useYWeights } ,\" { paramV2FileDir } \" ,{ toFixEP } ,{ yGenRange } ,\" { useLMeeDecaytable } \" ,{ weightingMode } " )
79+ f .write (")" )
80+ return ini_file
81+
82+ def run_only_simulation (args ):
83+ sim_log_name = "simlog"
84+ name = "o2sim"
85+
86+ output_dir = abspath (args .output )
87+ output_name = abspath (join (args .output ,name ))
88+ ini_file = abspath (args .sim_ini_file )
89+ log_file = abspath (join (args .output ,sim_log_name ))
90+
91+ current_dir = os .getcwd ()
92+ if not exists (output_dir ):
93+ os .makedirs (output_dir )
94+ os .chdir (output_dir )
95+
96+
97+ print ("\n ==> Running cocktail simulation" )
98+ command_to_run = ['o2-sim' ,'-n' ,args .nEvents ,'-g' ,'external' ,'-o' ,output_name ,'--configFile' ,ini_file ,'--noGeant' ]
99+ print ("running with: " ,end = '' )
100+ print (* command_to_run )
101+ print ("redirect output to " + log_file )
102+ print ("\n running..." )
103+ f = open (log_file , "w" )
104+ simproc = subprocess .Popen (['o2-sim' ,'-n' ,args .nEvents ,'-g' ,'external' ,'-o' ,output_name ,'--configFile' ,ini_file ,'--noGeant' ], stdout = f , stderr = subprocess .STDOUT )
105+ simproc .communicate ()
106+ f .close ()
107+ print ("...done\n " )
108+
109+ os .chdir (current_dir )
110+
111+ if args .clean :
112+ clean (args .output ,name ,True )
113+
114+ if not simproc .returncode == 0 :
115+ print ("ERROR: o2sim finished with error. See " + abspath (join (args .output ,name + "_serverlog" ))+ " and " + log_file + "for details\n " )
116+ return 1
117+ else :
118+ print ("Cocktail simulation finished successful. Result in " + output_name + "_Kine.root\n " )
119+ return 0
120+
121+
122+ def run_only_analysis_task (args ):
123+ sim_log_name = "readerlog"
124+ ana_log_name = "analog"
125+ name = "kineReader"
126+
127+ print ("\n ==> Running cocktail analysis task" )
128+
129+ output_dir = abspath (args .output )
130+ output_name = join (output_dir ,name )
131+ current_dir = os .getcwd ()
132+
133+ kin_file = abspath (args .input )
134+ json_file = abspath (args .ana_config_file )
135+ sim_log_file = join (output_dir ,sim_log_name )
136+ ana_log_file = join (output_dir ,ana_log_name )
137+
138+ if not exists (output_dir ):
139+ os .makedirs (output_dir )
140+ os .chdir (output_dir )
141+
142+ f = root .TFile (kin_file )
143+ tree = f .Get ("o2sim" )
144+ nEvents = tree .GetEntries ()
145+ print (f"Found { nEvents } events in { kin_file } " )
146+ #MCTrack
147+ #@size
148+
149+ sim_command_to_run = ['o2-sim' ,'-g' ,'extkinO2' ,'-n' ,str (nEvents ),'-o' ,output_name ,'--extKinFile' ,kin_file ,'--noGeant' ,'--forwardKine' ,'--noDiscOutput' ]
150+ print ("running reader with: " ,end = '' )
151+ print (* sim_command_to_run )
152+ print ("redirect reader output to " + sim_log_file )
153+
154+ fs = open (sim_log_file , "w" )
155+ simproc = subprocess .Popen (sim_command_to_run , stdout = fs , stderr = subprocess .STDOUT )
156+
157+ proxy_command_to_run = ['o2-sim-mctracks-proxy' ,'--nevents' ,str (nEvents ),'--o2sim-pid' ,str (simproc .pid )]
158+ ana_command_to_run = ['o2-analysis-em-lmee-lf-cocktail' , '--configuration' ,'json://' + json_file ,'-b' ]
159+ print ("running analysis with: " ,end = '' )
160+ print (* proxy_command_to_run ,end = '' )
161+ print (" | " ,end = '' )
162+ print (* ana_command_to_run )
163+ print ("redirect analysis output to " + ana_log_file )
164+
165+ fa = open (ana_log_file ,"w" )
166+ proxyproc = subprocess .Popen (proxy_command_to_run , stdout = subprocess .PIPE )
167+ anaproc = subprocess .Popen (ana_command_to_run , stdin = proxyproc .stdout , stdout = fa , stderr = subprocess .STDOUT )
168+
169+ print ("\n running...\n " )
170+ simproc .communicate ()
171+ if not simproc .returncode == 0 :
172+ print ("ERROR: Reader finished with error. See " + abspath (join (args .output ,name + "_serverlog" ))+ " and " + sim_log_file + " for details\n " )
173+ proxyproc .kill ();
174+ anaproc .kill ();
175+ proxyproc .communicate ()
176+ anaproc .communicate ()
177+ fs .close ()
178+ fa .close ()
179+ print ("...done\n " )
180+
181+
182+ if not anaproc .returncode == 0 :
183+ print ("ERROR: Analysis task finished with error. See " + abspath (join (args .output ,"analog" ))+ " for details\n " )
184+ else :
185+ print ("Analysis task finished successful. Histograms in " + output_dir + "/AnalysisResults.root\n " )
186+
187+ os .chdir (current_dir )
188+
189+ clean (args .output ,name ,False )
190+
191+ if (not simproc .returncode ) and (not anaproc .returncode ):
192+ return 0
193+ else :
194+ return 1
195+
196+ def run_full (args ):
197+ sim_log_name = "simlog"
198+ ana_log_name = "analog"
199+ name = "o2sim"
200+
201+ print ("\n ==> Running cocktail simulation + analysis task" )
202+
203+ output_dir = abspath (args .output )
204+ output_name = join (output_dir ,name )
205+ current_dir = os .getcwd ()
206+
207+ ini_file = abspath (args .sim_ini_file )
208+ json_file = abspath (args .ana_config_file )
209+ sim_log_file = join (output_dir ,sim_log_name )
210+ ana_log_file = join (output_dir ,ana_log_name )
211+
212+ if not exists (output_dir ):
213+ os .makedirs (output_dir )
214+ os .chdir (output_dir )
215+
216+ sim_command_to_run = ['o2-sim' ,'-g' ,'external' ,'-n' ,args .nEvents ,'-o' ,output_name ,'--configFile' ,ini_file ,'--noGeant' ,'--forwardKine' ]
217+ if not args .save_kine :
218+ sim_command_to_run .append ('--noDiscOutput' )
219+ print ("running simulation with: " ,end = '' )
220+ print (* sim_command_to_run )
221+ print ("redirect sim output to " + sim_log_file )
222+
223+ fs = open (sim_log_file , "w" )
224+ simproc = subprocess .Popen (sim_command_to_run , stdout = fs , stderr = subprocess .STDOUT )
225+
226+ proxy_command_to_run = ['o2-sim-mctracks-proxy' ,'--nevents' ,args .nEvents ,'--o2sim-pid' ,str (simproc .pid )]
227+ ana_command_to_run = ['o2-analysis-em-lmee-lf-cocktail' ,'--configuration' ,'json://' + json_file ,'-b' ]
228+ print ("running analysis with: " ,end = '' )
229+ print (* proxy_command_to_run ,end = '' )
230+ print (" | " ,end = '' )
231+ print (* ana_command_to_run )
232+ print ("redirect analysis output to " + ana_log_file )
233+
234+ fa = open (ana_log_file ,"w" )
235+ proxyproc = subprocess .Popen (proxy_command_to_run , stdout = subprocess .PIPE )
236+ anaproc = subprocess .Popen (ana_command_to_run , stdin = proxyproc .stdout , stdout = fa , stderr = subprocess .STDOUT )
237+
238+ print ("\n running...\n " )
239+ simproc .communicate ()
240+ if not simproc .returncode == 0 :
241+ print ("ERROR: o2sim finished with error. See " + abspath (join (args .output ,name + "_serverlog" ))+ " and " + sim_log_file + " for details\n " )
242+ proxyproc .kill ();
243+ anaproc .kill ();
244+ else :
245+ print ("Cocktail simulation finished successful." ,end = '' )
246+ if args .save_kine :
247+ print (" Output written to " + output_name + "_Kine.root." ,end = '' )
248+ print (" Waiting for analysis task to finish...\n " )
249+ proxyproc .communicate ()
250+ anaproc .communicate ()
251+ fs .close ()
252+ fa .close ()
253+ print ("...done\n " )
254+
255+
256+ if not anaproc .returncode == 0 :
257+ print ("ERROR: Analysis task finished with error. See " + abspath (join (args .output ,"analog" ))+ " for details\n " )
258+ else :
259+ print ("Analysis task finished successful. Histograms in " + output_dir + "/AnalysisResults.root\n " )
260+
261+ os .chdir (current_dir )
262+
263+ if args .clean :
264+ clean (args .output ,name ,True )
265+
266+ if (not simproc .returncode ) and (not anaproc .returncode ):
267+ return 0
268+ else :
269+ return 1
270+
271+ def main ():
272+ parser = argparse .ArgumentParser ()
273+ common_sim_parser = argparse .ArgumentParser (add_help = False )
274+ sim_input = common_sim_parser .add_mutually_exclusive_group (required = True )
275+ common_sim_parser .add_argument ("--nEvents" ,"-n" ,help = "Number of events to be generated" ,required = True )
276+ sim_input .add_argument ("--sim-ini-file" ,dest = "sim_ini_file" ,help = "INI file for the generator" )
277+ sim_input .add_argument ("--sim-config-file" ,dest = "sim_config_file" ,help = "JSON config file for the generator (will be converted to INI file)" )
278+ common_sim_parser .add_argument ("--output" ,"-o" ,default = os .getcwd (),help = "Output directory" )
279+ common_sim_parser .add_argument ("--clean" , dest = "clean" , action = "store_true" ,help = "Delete unwanted files after finishing the job" )
280+ common_ana_parser = argparse .ArgumentParser (add_help = False )
281+ common_ana_parser .add_argument ("--ana-config-file" ,dest = "ana_config_file" ,help = "Config file for the analysis task" ,required = True )
282+ sub_parsers = parser .add_subparsers (dest = "command" ,required = True )
283+ sim_parser = sub_parsers .add_parser ("sim-only" ,parents = [common_sim_parser ])
284+ ana_parser = sub_parsers .add_parser ("ana-only" ,parents = [common_ana_parser ])
285+ ana_parser .add_argument ("--input" ,"-i" ,help = "Analysis input file" ,required = True )
286+ ana_parser .add_argument ("--output" ,"-o" ,default = os .getcwd (),help = "Output directory" )
287+ full_parser = sub_parsers .add_parser ("full" ,parents = [common_sim_parser ,common_ana_parser ])
288+ full_parser .add_argument ("--save-kine" ,dest = "save_kine" ,action = "store_true" ,help = "Write the generator output to o2sim_Kine.root" )
289+ args = parser .parse_args ()
290+
291+ print ("\n ######################" )
292+ print ("## LMee LF Cocktail ##" )
293+ print ("######################" )
294+
295+ print ("\n ==> Running in mode \' " + args .command + "\' \n " )
296+
297+ if ( ((args .command == "full" ) or (args .command == "sim-only" )) and args .sim_config_file ):
298+ args .sim_ini_file = read_sim_config (args )
299+ if args .sim_ini_file == 1 :
300+ return 1
301+
302+ if (args .command == "full" ):
303+ return run_full (args )
304+ if (args .command == "sim-only" ):
305+ return run_only_simulation (args )
306+ if (args .command == "ana-only" ):
307+ return run_only_analysis_task (args )
308+
309+ main ()
0 commit comments