ATLAS Offline Software
MadGraphUtils.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2 
3 # Pythonized version of MadGraph steering executables
4 # written by Zach Marshall <zach.marshall@cern.ch>
5 # updates for aMC@NLO by Josh McFayden <mcfayden@cern.ch>
6 # updates to LHE handling and SUSY functionality by Emma Kuwertz <ekuwertz@cern.ch>
7 # Attempts to remove path-dependence of MadGraph
8 
9 import os,time,subprocess,glob,re,sys
10 # These Import lines are temporary for backwards compatibility of clients.
11 from MCJobOptionUtils.LHAPDFsupport import get_lhapdf_id_and_name # noqa: F401
12 from MCJobOptionUtils.LHAPDFsupport import get_LHAPDF_PATHS # noqa: F401
13 from MCJobOptionUtils.JOsupport import get_physics_short
14 from AthenaCommon import Logging
15 from MadGraphControl.MGC import MGControl
16 mglog = Logging.logging.getLogger('MadGraphUtils')
17 my_MGC_instance = None
18 
19 # Name of python executable
20 python='python'
21 # Magic name of gridpack directory
22 MADGRAPH_GRIDPACK_LOCATION='madevent'
23 # Name for the run (since we only have 1, just needs consistency)
24 MADGRAPH_RUN_NAME='run_01'
25 # For error handling
26 MADGRAPH_CATCH_ERRORS=True
27 # PDF setting (global setting)
28 MADGRAPH_PDFSETTING=None
29 MADGRAPH_COMMAND_STACK = []
30 
31 patched_shutil_loc='/cvmfs/atlas.cern.ch/repo/sw/Generators/madgraph/models/latest/shutil_patch'
32 if 'PYTHONPATH' in os.environ and patched_shutil_loc not in os.environ['PYTHONPATH']:
33  # add shutil_patch in first place so that the patched version of shutil.py is picked up by MG instead of original version
34  # the patched version does not throw the errors that has made running MG and this code impossible on some file systems
35  os.environ['PYTHONPATH'] = patched_shutil_loc+':'+os.environ['PYTHONPATH']
36  MADGRAPH_COMMAND_STACK += ['export PYTHONPATH='+patched_shutil_loc+':${PYTHONPATH}']
37 # we need to remove shutil from modules before we can use our version
38 if 'shutil' in sys.modules:
39  sys.modules.pop('shutil')
40 # make sure this python instance uses fixed shutil
41 sys.path.insert(0,patched_shutil_loc)
42 import shutil
43 
44 from MadGraphControl.MadGraphUtilsHelpers import checkSettingExists,checkSetting,checkSettingIsTrue,getDictFromCard,get_runArgs_info,error_check,setup_path_protection,is_NLO_run,get_default_config_card
45 from MadGraphControl.MadGraphParamHelpers import check_PMG_updates
46 
47 def stack_subprocess(command,**kwargs):
48  global MADGRAPH_COMMAND_STACK
49  MADGRAPH_COMMAND_STACK += [' '.join(command)]
50  return subprocess.Popen(command,**kwargs)
51 
52 
53 
54 
55 
56 def generate_prep(process_dir):
57  global MADGRAPH_COMMAND_STACK
58  if not os.access('Cards_bkup',os.R_OK):
59  shutil.copytree(process_dir+'/Cards','Cards_bkup')
60  shutil.copyfile(process_dir+'/Source/make_opts','Cards_bkup/make_opts_bkup')
61  MADGRAPH_COMMAND_STACK += ['# In case this fails, Cards_bkup should be in your original run directory']
62  MADGRAPH_COMMAND_STACK += ['# And ${MGaMC_PROCESS_DIR} can be replaced with whatever process directory exists in your stand-alone test']
63  MADGRAPH_COMMAND_STACK += ['cp '+os.getcwd()+'/Cards_bkup/*dat ${MGaMC_PROCESS_DIR}/Cards/']
64  MADGRAPH_COMMAND_STACK += ['cp '+os.getcwd()+'/Cards_bkup/make_opts_bkup ${MGaMC_PROCESS_DIR}/Source/make_opts']
65  else:
66  mglog.warning('Found Cards_bkup directory existing. Suggests you are either running generation twice (a little funny) or are not using a clean directory.')
67  bkup_v = 1
68  while os.access('Cards_bkup_'+str(bkup_v),os.R_OK) and bkup_v<100:
69  bkup_v += 1
70  if bkup_v<100:
71  shutil.copytree(process_dir+'/Cards','Cards_bkup_'+str(bkup_v))
72  shutil.copyfile(process_dir+'/Source/make_opts','Cards_bkup_'+str(bkup_v)+'/make_opts_bkup')
73  MADGRAPH_COMMAND_STACK += ['# In case this fails, Cards_bkup should be in your original run directory']
74  MADGRAPH_COMMAND_STACK += ['# And ${MGaMC_PROCESS_DIR} can be replaced with whatever process directory exists in your stand-alone test']
75  MADGRAPH_COMMAND_STACK += ['cp '+os.getcwd()+'/Cards_bkup_'+str(bkup_v)+'/*dat ${MGaMC_PROCESS_DIR}/Cards/']
76  MADGRAPH_COMMAND_STACK += ['cp '+os.getcwd()+'/Cards_bkup_'+str(bkup_v)+'/make_opts_bkup ${MGaMC_PROCESS_DIR}/Source/make_opts']
77  else:
78  mglog.warning('Way too many Cards_bkup* directories found. Giving up -- standalone script may not work.')
79 
80 
81 def new_process(process='generate p p > t t~\noutput -f', plugin=None, keepJpegs=False, usePMGSettings=False):
82  global my_MGC_instance
83  print(process,plugin,keepJpegs,usePMGSettings)
84  my_MGC_instance = MGControl(process, plugin, keepJpegs, usePMGSettings)
85  return my_MGC_instance.process_dir
86 
87 def get_default_runcard(process_dir=MADGRAPH_GRIDPACK_LOCATION):
88  """ Copy the default runcard from one of several locations
89  to a local file with name run_card.tmp.dat"""
90  output_name = 'run_card.tmp.dat'
91 
92  # Get the run card from the installation
93  run_card=process_dir+'/Cards/run_card.dat'
94  if os.access(run_card,os.R_OK):
95  mglog.info('Copying default run_card.dat from '+str(run_card))
96  shutil.copy(run_card,output_name)
97  return output_name
98  else:
99  run_card=process_dir+'/Cards/run_card_default.dat'
100  mglog.info('Fetching default run_card.dat from '+str(run_card))
101  if os.access(run_card,os.R_OK):
102  shutil.copy(run_card,output_name)
103  return output_name
104  else:
105  raise RuntimeError('Cannot find default run_card.dat or run_card_default.dat! I was looking here: %s'%run_card)
106 
107 
108 def generate(process_dir='PROC_mssm_0', grid_pack=False, gridpack_compile=False, extlhapath=None, required_accuracy=0.01, runArgs=None, bias_module=None, requirePMGSettings=False):
109  global my_MGC_instance
110  # Just in case
112 
113  # Set consistent mode and number of jobs
114  mode = 0
115  njobs = 1
116  if 'ATHENA_CORE_NUMBER' in os.environ and int(os.environ['ATHENA_CORE_NUMBER'])>0:
117  njobs = int(os.environ['ATHENA_CORE_NUMBER'])
118  mglog.info('Lucky you - you are running on a full node queue. Will re-configure for '+str(njobs)+' jobs.')
119  mode = 2
120 
121  cluster_type = get_cluster_type(process_dir=process_dir)
122  if cluster_type is not None:
123  mode = 1
124 
126  mglog.info('Running event generation from gridpack (using smarter mode from generate() function)')
127  generate_from_gridpack(runArgs=runArgs,extlhapath=extlhapath,gridpack_compile=gridpack_compile,requirePMGSettings=requirePMGSettings)
128  return
129  else:
130  mglog.info('Did not identify an input gridpack.')
131  if grid_pack:
132  mglog.info('The grid_pack flag is set, so I am expecting to create a gridpack in this job')
133 
134  # Now get a variety of info out of the runArgs
135  beamEnergy,random_seed = get_runArgs_info(runArgs)
136 
137  # Check if process is NLO or LO
138  isNLO=is_NLO_run(process_dir=process_dir)
139 
140  # temporary fix of makefile, needed for 3.3.1., remove in future
141  if isNLO:
142  fix_fks_makefile(process_dir=process_dir)
143 
144  # if f2py not available
145  if get_reweight_card(process_dir=process_dir) is not None:
146  from distutils.spawn import find_executable
147  if find_executable('f2py') is not None:
148  mglog.info('Found f2py, will use it for reweighting')
149  else:
150  raise RuntimeError('Could not find f2py, needed for reweighting')
151  check_reweight_card(process_dir)
152 
153  global MADGRAPH_COMMAND_STACK
154 
155  if grid_pack:
156  #Running in gridpack mode
157  mglog.info('Started generating gridpack at '+str(time.asctime()))
158  mglog.warning(' >>>>>> THIS KIND OF JOB SHOULD ONLY BE RUN LOCALLY - NOT IN GRID JOBS <<<<<<')
159 
160  # Some events required if we specify MadSpin usage!
161  my_settings = {'nevents':'1000'}
162  if isNLO:
163  my_settings['req_acc']=str(required_accuracy)
164  else:
165  # At LO, no events are generated. That means we need to move the MS card aside and back.
166  LO_has_madspin = False
167  if os.access(f'{process_dir}/Cards/madspin_card.dat',os.R_OK):
168  MADGRAPH_COMMAND_STACK += [f'mv {process_dir}/Cards/madspin_card.dat {process_dir}/Cards/madspin_card.tmp.dat']
169  os.rename(f'{process_dir}/Cards/madspin_card.dat',f'{process_dir}/Cards/madspin_card.tmp.dat')
170  LO_has_madspin = True
171  my_settings = {'gridpack':'true'}
172  modify_run_card(process_dir=process_dir,settings=my_settings,skipBaseFragment=True)
173 
174  else:
175  #Running in on-the-fly mode
176  mglog.info('Started generating at '+str(time.asctime()))
177 
178  mglog.info('Run '+MADGRAPH_RUN_NAME+' will be performed in mode '+str(mode)+' with '+str(njobs)+' jobs in parallel.')
179 
180  # Ensure that things are set up normally
181  if not os.access(process_dir,os.R_OK):
182  raise RuntimeError('No process directory found at '+process_dir)
183  if not os.access(process_dir+'/bin/generate_events',os.R_OK):
184  raise RuntimeError('No generate_events module found in '+process_dir)
185 
186  mglog.info('For your information, the libraries available are (should include LHAPDF):')
187  ls_dir(process_dir+'/lib')
188 
189  setupFastjet(process_dir=process_dir)
190  if bias_module is not None:
191  setup_bias_module(bias_module,process_dir)
192 
193  mglog.info('Now I will hack the make files a bit. Apologies, but there seems to be no good way around this.')
194  shutil.copyfile(process_dir+'/Source/make_opts',process_dir+'/Source/make_opts_old')
195  old_opts = open(process_dir+'/Source/make_opts_old','r')
196  new_opts = open(process_dir+'/Source/make_opts','w')
197  for aline in old_opts:
198  if 'FC=g' in aline:
199  mglog.info('Configuring the fancy gfortran compiler instead of g77 / f77')
200  new_opts.write(' FC=gfortran\n')
201  else:
202  new_opts.write(aline)
203  old_opts.close()
204  new_opts.close()
205  mglog.info('Make file hacking complete.')
206 
207  # Change directories
208  currdir=os.getcwd()
209  os.chdir(process_dir)
210  # Record the change
211  MADGRAPH_COMMAND_STACK += [ 'cd ${MGaMC_PROCESS_DIR}' ]
212 
213  # Check the run card
214  run_card_consistency_check(isNLO=isNLO)
215 
216  # Since the consistency check can update some settings, print the cards now
217  print_cards_from_dir(process_dir=os.getcwd())
218 
219  # Check the param card
220  code = check_PMG_updates(process_dir=os.getcwd())
221  if requirePMGSettings and code!=0:
222  raise RuntimeError('Settings are not compliant with PMG defaults! Please use do_PMG_updates function to get PMG default params.')
223 
224  # Build up the generate command
225  # Use the new-style way of passing things: just --name, everything else in config
226  command = [python,'bin/generate_events']
227  if isNLO:
228  command += ['--name='+MADGRAPH_RUN_NAME]
229  mglog.info('Removing Cards/shower_card.dat to ensure we get parton level events only')
230  os.unlink('Cards/shower_card.dat')
231  else:
232  command += [MADGRAPH_RUN_NAME]
233  # Set the number of cores to be used
234  setNCores(process_dir=os.getcwd(), Ncores=njobs)
235  # Special handling for mode 1
236  if mode==1:
237  mglog.info('Setting up cluster running')
238  modify_config_card(process_dir=os.getcwd(),settings={'run_mode':1})
239  if cluster_type=='pbs':
240  mglog.info('Modifying bin/internal/cluster.py for PBS cluster running')
241  os.system("sed -i \"s:text += prog:text += './'+prog:g\" bin/internal/cluster.py")
242  elif mode==2:
243  mglog.info('Setting up multi-core running on '+os.environ['ATHENA_CORE_NUMBER']+' cores')
244  elif mode==0:
245  mglog.info('Setting up serial generation.')
246 
247  generate_prep(process_dir=os.getcwd())
248  global MADGRAPH_CATCH_ERRORS
249  generate = stack_subprocess(command,stdin=subprocess.PIPE, stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
250  (out,err) = generate.communicate()
251  error_check(err,generate.returncode)
252 
253  # Get back to where we came from
254  os.chdir(currdir)
255  MADGRAPH_COMMAND_STACK += [ 'cd -' ]
256 
257  if grid_pack:
258  # Name dictacted by https://twiki.cern.ch/twiki/bin/viewauth/AtlasProtected/PmgMcSoftware
259  energy = '%1.1f'%(beamEnergy*2./1000.)
260  energy = energy.replace('.0','').replace('.','p')
261  gridpack_name='mc_'+energy+'TeV.'+get_physics_short()+'.GRID.tar.gz'
262  mglog.info('Tidying up gridpack '+gridpack_name)
263 
264  if not isNLO:
265  # At LO, no events are generated. That means we need to move the MS card aside and back.
266  if LO_has_madspin:
267  MADGRAPH_COMMAND_STACK += [f'mv {process_dir}/Cards/madspin_card.tmp.dat {process_dir}/Cards/madspin_card.dat']
268  os.rename(f'{process_dir}/Cards/madspin_card.tmp.dat',f'{process_dir}/Cards/madspin_card.dat')
269 
270 
271  MADGRAPH_COMMAND_STACK += ['cp '+glob.glob(process_dir+'/'+MADGRAPH_RUN_NAME+'_*gridpack.tar.gz')[0]+' '+gridpack_name]
272  shutil.copy(glob.glob(process_dir+'/'+MADGRAPH_RUN_NAME+'_*gridpack.tar.gz')[0],gridpack_name)
273 
274  if gridpack_compile:
275  MADGRAPH_COMMAND_STACK += ['mkdir tmp%i/'%os.getpid(),'cd tmp%i/'%os.getpid()]
276  os.mkdir('tmp%i/'%os.getpid())
277  os.chdir('tmp%i/'%os.getpid())
278  mglog.info('untar gridpack')
279  untar = stack_subprocess(['tar','xvzf',('../'+gridpack_name)])
280  untar.wait()
281  mglog.info('compile and clean up')
282  MADGRAPH_COMMAND_STACK += ['cd madevent']
283  os.chdir('madevent/')
284  compilep = stack_subprocess(['./bin/compile'],stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
285  (out,err) = compilep.communicate()
286  error_check(err,compilep.returncode)
287  clean = stack_subprocess(['./bin/clean4grid'],stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
288  (out,err) = clean.communicate()
289  error_check(err,clean.returncode)
290  clean.wait()
291  MADGRAPH_COMMAND_STACK += ['cd ..','rm ../'+gridpack_name]
292  os.chdir('../')
293  mglog.info('remove old tarball')
294  os.unlink('../'+gridpack_name)
295  mglog.info('Package up new tarball')
296  tar = stack_subprocess(['tar','--exclude=SubProcesses/P*/G*/*_results.dat','--exclude=SubProcesses/P*/G*/*.log','--exclude=SubProcesses/P*/G*/*.txt','-cvsf','../'+gridpack_name,'.'])
297  tar.wait()
298  MADGRAPH_COMMAND_STACK += ['cd ..','rm -r tmp%i/'%os.getpid()]
299  os.chdir('../')
300  mglog.info('Remove temporary directory')
301  shutil.rmtree('tmp%i/'%os.getpid())
302  mglog.info('Tidying up complete!')
303 
304  else:
305 
306 
307  mglog.info('Package up process_dir')
308  MADGRAPH_COMMAND_STACK += ['mv '+process_dir+' '+MADGRAPH_GRIDPACK_LOCATION]
309  os.rename(process_dir,MADGRAPH_GRIDPACK_LOCATION)
310  tar = stack_subprocess(['tar','--exclude=Events/*/*events*gz','--exclude=SubProcesses/P*/G*/log*txt','--exclude=SubProcesses/P*/G*/events.lhe*','--exclude=*/*.o','--exclude=*/*/*.o','--exclude=*/*/*/*.o','--exclude=*/*/*/*/*.o','-czf',gridpack_name,MADGRAPH_GRIDPACK_LOCATION])
311  tar.wait()
312  MADGRAPH_COMMAND_STACK += ['mv '+MADGRAPH_GRIDPACK_LOCATION+' '+process_dir]
313  os.rename(MADGRAPH_GRIDPACK_LOCATION,process_dir)
314 
315  mglog.info('Gridpack sucessfully created, exiting the transform')
316  if hasattr(runArgs,'outputTXTFile'):
317  mglog.info('Touching output TXT (LHE) file for the transform')
318  open(runArgs.outputTXTFile, 'w').close()
319  from AthenaCommon.AppMgr import theApp
320  theApp.finalize()
321  theApp.exit()
322 
323  mglog.info('Finished at '+str(time.asctime()))
324  return 0
325 
326 
327 def generate_from_gridpack(runArgs=None, extlhapath=None, gridpack_compile=None, requirePMGSettings=False):
328  global my_MGC_instance
329  # Get of info out of the runArgs
330  beamEnergy,random_seed = get_runArgs_info(runArgs)
331 
332  # Just in case
334 
335  isNLO=is_NLO_run(process_dir=MADGRAPH_GRIDPACK_LOCATION)
336 
337  setupFastjet(process_dir=MADGRAPH_GRIDPACK_LOCATION)
338 
339  # This is hard-coded as a part of MG5_aMC :'(
340  gridpack_run_name = 'GridRun_'+str(random_seed)
341 
342  # Ensure that we only do madspin at the end
343  if os.access(MADGRAPH_GRIDPACK_LOCATION+'/Cards/madspin_card.dat',os.R_OK):
344  os.rename(MADGRAPH_GRIDPACK_LOCATION+'/Cards/madspin_card.dat',MADGRAPH_GRIDPACK_LOCATION+'/Cards/backup_madspin_card.dat')
345  do_madspin=True
346  else:
347  do_madspin=False
348 
349  if get_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION) is not None:
350  check_reweight_card(MADGRAPH_GRIDPACK_LOCATION)
351 
352  # Check the param card
353  code = check_PMG_updates(process_dir=MADGRAPH_GRIDPACK_LOCATION)
354  if requirePMGSettings and code!=0:
355  raise RuntimeError('Settings are not compliant with PMG defaults! Please use do_PMG_updates function to get PMG default params.')
356 
357  # Modify run card, then print
358  settings={'iseed':str(random_seed)}
359  if not isNLO:
360  settings['python_seed']=str(random_seed)
361  modify_run_card(process_dir=MADGRAPH_GRIDPACK_LOCATION,settings=settings,skipBaseFragment=True)
362 
363  mglog.info('Generating events from gridpack')
364 
365  # Ensure that things are set up normally
366  if not os.path.exists(MADGRAPH_GRIDPACK_LOCATION):
367  raise RuntimeError('Gridpack directory not found at '+MADGRAPH_GRIDPACK_LOCATION)
368 
369  nevents = getDictFromCard(MADGRAPH_GRIDPACK_LOCATION+'/Cards/run_card.dat')['nevents']
370  mglog.info('>>>> FOUND GRIDPACK <<<< <- This will be used for generation')
371  mglog.info('Generation of '+str(int(nevents))+' events will be performed using the supplied gridpack with random seed '+str(random_seed))
372  mglog.info('Started generating events at '+str(time.asctime()))
373 
374  #Remove addmasses if it's there
375  if os.access(MADGRAPH_GRIDPACK_LOCATION+'/bin/internal/addmasses.py',os.R_OK):
376  os.remove(MADGRAPH_GRIDPACK_LOCATION+'/bin/internal/addmasses.py')
377 
378  currdir=os.getcwd()
379 
380  # Make sure we've set the number of processes appropriately
381  setNCores(process_dir=MADGRAPH_GRIDPACK_LOCATION)
382  global MADGRAPH_CATCH_ERRORS
383 
384  # Run the consistency check, print some useful info
385  ls_dir(currdir)
386  ls_dir(MADGRAPH_GRIDPACK_LOCATION)
387 
388  # Update the run card according to consistency checks
389  run_card_consistency_check(isNLO=isNLO,process_dir=MADGRAPH_GRIDPACK_LOCATION)
390 
391  # Now all done with updates, so print the cards with the final settings
392  print_cards_from_dir(process_dir=MADGRAPH_GRIDPACK_LOCATION)
393 
394  if isNLO:
395  #turn off systematics for gridpack generation and store settings for standalone run
396  run_card_dict=getDictFromCard(MADGRAPH_GRIDPACK_LOCATION+'/Cards/run_card.dat')
397  systematics_settings=None
398  if checkSetting('systematics_program','systematics',run_card_dict):
399  if not checkSettingIsTrue('store_rwgt_info',run_card_dict):
400  raise RuntimeError('Trying to run NLO systematics but reweight info not stored')
401  if checkSettingExists('systematics_arguments',run_card_dict):
402  systematics_settings=MadGraphControl.MadGraphSystematicsUtils.parse_systematics_arguments(run_card_dict['systematics_arguments'])
403  else:
404  systematics_settings={}
405  mglog.info('Turning off systematics for now, running standalone later')
406  modify_run_card(process_dir=MADGRAPH_GRIDPACK_LOCATION,settings={'systematics_program':'none'},skipBaseFragment=True)
407 
408  global MADGRAPH_COMMAND_STACK
409  if not isNLO:
410 
411  if not os.access(MADGRAPH_GRIDPACK_LOCATION+'/bin/gridrun',os.R_OK):
412  mglog.error('/bin/gridrun not found at '+MADGRAPH_GRIDPACK_LOCATION)
413  raise RuntimeError('Could not find gridrun executable')
414  else:
415  mglog.info('Found '+MADGRAPH_GRIDPACK_LOCATION+'/bin/gridrun, starting generation.')
416  generate_prep(MADGRAPH_GRIDPACK_LOCATION)
417  granularity=1
418  mglog.info("Now generating {} events with random seed {} and granularity {}".format(int(nevents),int(random_seed),granularity))
419  # not sure whether this is needed but it is done in the old "run.sh" script
420  new_ld_path=":".join([os.environ['LD_LIBRARY_PATH'],os.getcwd()+'/'+MADGRAPH_GRIDPACK_LOCATION+'/madevent/lib',os.getcwd()+'/'+MADGRAPH_GRIDPACK_LOCATION+'/HELAS/lib'])
421  os.environ['LD_LIBRARY_PATH']=new_ld_path
422  MADGRAPH_COMMAND_STACK+=["export LD_LIBRARY_PATH="+":".join(['${LD_LIBRARY_PATH}',new_ld_path])]
423  generate = stack_subprocess([python,MADGRAPH_GRIDPACK_LOCATION+'/bin/gridrun',str(int(nevents)),str(int(random_seed)),str(granularity)],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
424  (out,err) = generate.communicate()
425  error_check(err,generate.returncode)
426  gp_events=MADGRAPH_GRIDPACK_LOCATION+"/Events/GridRun_{}/unweighted_events.lhe.gz".format(int(random_seed))
427  if not os.path.exists(gp_events):
428  mglog.error('Error in gp generation, did not find events at '+gp_events)
429 
430  # add reweighting, which is not run automatically from LO GPs
431  reweight_card=get_reweight_card(MADGRAPH_GRIDPACK_LOCATION)
432  if reweight_card is not None:
433  pythonpath_backup=os.environ['PYTHONPATH']
434  # workaround as madevent crashes when path to mg in PYTHONPATH
435  os.environ['PYTHONPATH']=':'.join([p for p in pythonpath_backup.split(':') if 'madgraph5amc' not in p])
436  add_reweighting('GridRun_{}'.format(int(random_seed)))
437  os.environ['PYTHONPATH']=pythonpath_backup
438 
439  shutil.move(gp_events,'events.lhe.gz')
440 
441  else:
442 
443  if not os.access(MADGRAPH_GRIDPACK_LOCATION+'/bin/generate_events',os.R_OK):
444  raise RuntimeError('Could not find generate_events executable at '+MADGRAPH_GRIDPACK_LOCATION)
445  else:
446  mglog.info('Found '+MADGRAPH_GRIDPACK_LOCATION+'/bin/generate_events, starting generation.')
447 
448  ls_dir(MADGRAPH_GRIDPACK_LOCATION+'/Events/')
449  if os.access(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name, os.F_OK):
450  mglog.info('Removing '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+' directory from gridpack generation')
451  MADGRAPH_COMMAND_STACK += ['rm -rf '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name]
452  shutil.rmtree(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name)
453 
454  # Delete events generated when setting up MadSpin during gridpack generation
455  if os.access(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'_decayed_1', os.F_OK):
456  mglog.info('Removing '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'_decayed_1 directory from gridpack generation')
457  MADGRAPH_COMMAND_STACK += ['rm -rf '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'_decayed_1']
458  shutil.rmtree(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'_decayed_1')
459 
460  ls_dir(MADGRAPH_GRIDPACK_LOCATION+'/Events/')
461 
462  if not gridpack_compile:
463  mglog.info('Copying make_opts from Template')
464  shutil.copy(os.environ['MADPATH']+'/Template/LO/Source/make_opts',MADGRAPH_GRIDPACK_LOCATION+'/Source/')
465 
466  generate_prep(MADGRAPH_GRIDPACK_LOCATION)
467  generate = stack_subprocess([python,MADGRAPH_GRIDPACK_LOCATION+'/bin/generate_events','--parton','--nocompile','--only_generation','-f','--name='+gridpack_run_name],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
468  (out,err) = generate.communicate()
469  error_check(err,generate.returncode)
470  else:
471  mglog.info('Allowing recompilation of gridpack')
472  if os.path.islink(MADGRAPH_GRIDPACK_LOCATION+'/lib/libLHAPDF.a'):
473  mglog.info('Unlinking '+MADGRAPH_GRIDPACK_LOCATION+'/lib/libLHAPDF.a')
474  os.unlink(MADGRAPH_GRIDPACK_LOCATION+'/lib/libLHAPDF.a')
475 
476  generate_prep(MADGRAPH_GRIDPACK_LOCATION)
477  generate = stack_subprocess([python,MADGRAPH_GRIDPACK_LOCATION+'/bin/generate_events','--parton','--only_generation','-f','--name='+gridpack_run_name],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
478  (out,err) = generate.communicate()
479  error_check(err,generate.returncode)
480  if isNLO and systematics_settings is not None:
481  # run systematics
482  mglog.info('Running systematics standalone')
483  systematics_path=MADGRAPH_GRIDPACK_LOCATION+'/bin/internal/systematics.py'
484  events_location=MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/events.lhe.gz'
485  syst_cmd=[python,systematics_path]+[events_location]*2+["--"+k+"="+systematics_settings[k] for k in systematics_settings]
486  mglog.info('running: '+' '.join(syst_cmd))
487  systematics = stack_subprocess(syst_cmd)
488  systematics.wait()
489 
490 
491  # See if MG5 did the job for us already
492  if not os.access('events.lhe.gz',os.R_OK):
493  mglog.info('Copying generated events to '+currdir)
494  if not os.path.exists(MADGRAPH_GRIDPACK_LOCATION+'Events/'+gridpack_run_name):
495  shutil.copy(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/events.lhe.gz','events.lhe.gz')
496  else:
497  mglog.info('Events were already in place')
498 
499  ls_dir(currdir)
500 
501  mglog.info('Moving generated events to be in correct format for arrange_output().')
502  mglog.info('Unzipping generated events.')
503  unzip = stack_subprocess(['gunzip','-f','events.lhe.gz'])
504  unzip.wait()
505 
506  mglog.info('Moving file over to '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/unweighted_events.lhe')
507  mkdir = stack_subprocess(['mkdir','-p',(MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name)])
508  mkdir.wait()
509  shutil.move('events.lhe',MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/unweighted_events.lhe')
510 
511  mglog.info('Re-zipping into dataset name '+MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/unweighted_events.lhe.gz')
512  rezip = stack_subprocess(['gzip',MADGRAPH_GRIDPACK_LOCATION+'/Events/'+gridpack_run_name+'/unweighted_events.lhe'])
513  rezip.wait()
514 
515  os.chdir(currdir)
516 
517  # Now consider MadSpin:
518  if do_madspin:
519  # Move card back
520  os.rename(MADGRAPH_GRIDPACK_LOCATION+'/Cards/backup_madspin_card.dat',MADGRAPH_GRIDPACK_LOCATION+'/Cards/madspin_card.dat')
521  mglog.info('Decaying with MadSpin.')
522  add_madspin(process_dir=MADGRAPH_GRIDPACK_LOCATION)
523 
524  mglog.info('Finished at '+str(time.asctime()))
525 
526  return 0
527 
528 
529 def setupFastjet(process_dir=None):
530 
531  isNLO=is_NLO_run(process_dir=process_dir)
532 
533  mglog.info('Path to fastjet install dir: '+os.environ['FASTJETPATH'])
534  fastjetconfig = os.environ['FASTJETPATH']+'/bin/fastjet-config'
535 
536  mglog.info('fastjet-config --version: '+str(subprocess.Popen([fastjetconfig, '--version'],stdout = subprocess.PIPE).stdout.read().strip()))
537  mglog.info('fastjet-config --prefix: '+str(subprocess.Popen([fastjetconfig, '--prefix'],stdout = subprocess.PIPE).stdout.read().strip()))
538 
539  if not isNLO:
540  config_card=process_dir+'/Cards/me5_configuration.txt'
541  else:
542  config_card=process_dir+'/Cards/amcatnlo_configuration.txt'
543 
544  oldcard = open(config_card,'r')
545  newcard = open(config_card+'.tmp','w')
546 
547  for line in oldcard:
548  if 'fastjet = ' in line:
549  newcard.write('fastjet = '+fastjetconfig+'\n')
550  mglog.info('Setting fastjet = '+fastjetconfig+' in '+config_card)
551  else:
552  newcard.write(line)
553  oldcard.close()
554  newcard.close()
555  shutil.move(config_card+'.tmp',config_card)
556 
557  return
558 
559 
560 
561 def setupLHAPDF(process_dir=None, extlhapath=None, allow_links=True):
562  global my_MGC_instance
563 
564  isNLO=is_NLO_run(process_dir=process_dir)
565 
566  origLHAPATH=os.environ['LHAPATH']
567  origLHAPDF_DATA_PATH=os.environ['LHAPDF_DATA_PATH']
568 
569  LHAPATH,LHADATAPATH=get_LHAPDF_PATHS()
570 
571  pdfname=''
572  pdfid=-999
573 
574 
575  run_card=process_dir+'/Cards/run_card.dat'
576  mydict=getDictFromCard(run_card)
577 
578  if mydict["pdlabel"].replace("'","") == 'lhapdf':
579  #Make local LHAPDF dir
580  mglog.info('creating local LHAPDF dir: MGC_LHAPDF/')
581  if os.path.islink('MGC_LHAPDF/'):
582  os.unlink('MGC_LHAPDF/')
583  elif os.path.isdir('MGC_LHAPDF/'):
584  shutil.rmtree('MGC_LHAPDF/')
585 
586  newMGCLHA='MGC_LHAPDF/'
587 
588  mkdir = subprocess.Popen(['mkdir','-p',newMGCLHA])
589  mkdir.wait()
590 
591  pdfs_used=[ int(x) for x in mydict['lhaid'].replace(' ',',').split(',') ]
592  # included systematics pdfs here
593  if 'sys_pdf' in mydict:
594  sys_pdf=mydict['sys_pdf'].replace('&&',' ').split()
595  for s in sys_pdf:
596  if s.isdigit():
597  idx=int(s)
598  if idx>1000: # the sys_pdf syntax is such that small numbers are used to specify the subpdf index
599  pdfs_used.append(idx)
600  else:
601  pdfs_used.append(s)
602  if 'systematics_arguments' in mydict:
603  systematics_arguments=MadGraphControl.MadGraphSystematicsUtils.parse_systematics_arguments(mydict['systematics_arguments'])
604  if 'pdf' in systematics_arguments:
605  sys_pdf=systematics_arguments['pdf'].replace(',',' ').replace('@',' ').split()
606  for s in sys_pdf:
607  if s.isdigit():
608  idx=int(s)
609  if idx>1000: # the sys_pdf syntax is such that small numbers are used to specify the subpdf index
610  pdfs_used.append(idx)
611  else:
612  pdfs_used.append(s)
613  for pdf in pdfs_used:
614  if isinstance(pdf,str) and (pdf.lower()=='errorset' or pdf.lower()=='central'):
615  continue
616  # new function to get both lhapdf id and name
617  pdfid,pdfname=get_lhapdf_id_and_name(pdf)
618  mglog.info("Found LHAPDF ID="+str(pdfid)+", name="+pdfname)
619 
620  if not os.path.exists(newMGCLHA+pdfname) and not os.path.lexists(newMGCLHA+pdfname):
621  if not os.path.exists(LHADATAPATH+'/'+pdfname):
622  mglog.warning('PDF not installed at '+LHADATAPATH+'/'+pdfname)
623  if allow_links:
624  mglog.info('linking '+LHADATAPATH+'/'+pdfname+' --> '+newMGCLHA+pdfname)
625  os.symlink(LHADATAPATH+'/'+pdfname,newMGCLHA+pdfname)
626  else:
627  mglog.info('copying '+LHADATAPATH+'/'+pdfname+' --> '+newMGCLHA+pdfname)
628  shutil.copytree(LHADATAPATH+'/'+pdfname,newMGCLHA+pdfname)
629 
630  if allow_links:
631  mglog.info('linking '+LHADATAPATH+'/pdfsets.index --> '+newMGCLHA+'pdfsets.index')
632  os.symlink(LHADATAPATH+'/pdfsets.index',newMGCLHA+'pdfsets.index')
633 
634  atlasLHADATAPATH=LHADATAPATH.replace('sft.cern.ch/lcg/external/lhapdfsets/current','atlas.cern.ch/repo/sw/Generators/lhapdfsets/current')
635  mglog.info('linking '+atlasLHADATAPATH+'/lhapdf.conf --> '+newMGCLHA+'lhapdf.conf')
636  os.symlink(atlasLHADATAPATH+'/lhapdf.conf',newMGCLHA+'lhapdf.conf')
637  else:
638  mglog.info('copying '+LHADATAPATH+'/pdfsets.index --> '+newMGCLHA+'pdfsets.index')
639  shutil.copy2(LHADATAPATH+'/pdfsets.index',newMGCLHA+'pdfsets.index')
640 
641  atlasLHADATAPATH=LHADATAPATH.replace('sft.cern.ch/lcg/external/lhapdfsets/current','atlas.cern.ch/repo/sw/Generators/lhapdfsets/current')
642  mglog.info('copying '+atlasLHADATAPATH+'/lhapdf.conf -->'+newMGCLHA+'lhapdf.conf')
643  shutil.copy2(atlasLHADATAPATH+'/lhapdf.conf',newMGCLHA+'lhapdf.conf')
644 
645 
646  LHADATAPATH=os.getcwd()+'/MGC_LHAPDF'
647 
648  else:
649  mglog.info('Not using LHAPDF')
650  return (LHAPATH,origLHAPATH,origLHAPDF_DATA_PATH)
651 
652 
653  if isNLO:
654  os.environ['LHAPDF_DATA_PATH']=LHADATAPATH
655 
656  mglog.info('Path to LHAPDF install dir: '+LHAPATH)
657  mglog.info('Path to LHAPDF data dir: '+LHADATAPATH)
658  if not os.path.isdir(LHADATAPATH):
659  raise RuntimeError('LHAPDF data dir not accesible: '+LHADATAPATH)
660  if not os.path.isdir(LHAPATH):
661  raise RuntimeError('LHAPDF path dir not accesible: '+LHAPATH)
662 
663  # Dealing with LHAPDF
664  if extlhapath:
665  lhapdfconfig=extlhapath
666  if not os.access(lhapdfconfig,os.X_OK):
667  raise RuntimeError('Failed to find valid external lhapdf-config at '+lhapdfconfig)
668  LHADATAPATH=subprocess.Popen([lhapdfconfig, '--datadir'],stdout = subprocess.PIPE).stdout.read().strip()
669  mglog.info('Changing LHAPDF_DATA_PATH to '+LHADATAPATH)
670  os.environ['LHAPDF_DATA_PATH']=LHADATAPATH
671  else:
672  getlhaconfig = subprocess.Popen(['get_files','-data','lhapdf-config'])
673  getlhaconfig.wait()
674  #Get custom lhapdf-config
675  if not os.access(os.getcwd()+'/lhapdf-config',os.X_OK):
676  mglog.error('Failed to get lhapdf-config from MadGraphControl')
677  return 1
678  lhapdfconfig = os.getcwd()+'/lhapdf-config'
679 
680  mglog.info('lhapdf-config --version: '+str(subprocess.Popen([lhapdfconfig, '--version'],stdout = subprocess.PIPE).stdout.read().strip()))
681  mglog.info('lhapdf-config --prefix: '+str(subprocess.Popen([lhapdfconfig, '--prefix'],stdout = subprocess.PIPE).stdout.read().strip()))
682  mglog.info('lhapdf-config --libdir: '+str(subprocess.Popen([lhapdfconfig, '--libdir'],stdout = subprocess.PIPE).stdout.read().strip()))
683  mglog.info('lhapdf-config --datadir: '+str(subprocess.Popen([lhapdfconfig, '--datadir'],stdout = subprocess.PIPE).stdout.read().strip()))
684  mglog.info('lhapdf-config --pdfsets-path: '+str(subprocess.Popen([lhapdfconfig, '--pdfsets-path'],stdout = subprocess.PIPE).stdout.read().strip()))
685 
686  modify_config_card(process_dir=process_dir,settings={'lhapdf':lhapdfconfig,'lhapdf_py3':lhapdfconfig})
687 
688  mglog.info('Creating links for LHAPDF')
689  if os.path.islink(process_dir+'/lib/PDFsets'):
690  os.unlink(process_dir+'/lib/PDFsets')
691  elif os.path.isdir(process_dir+'/lib/PDFsets'):
692  shutil.rmtree(process_dir+'/lib/PDFsets')
693  if allow_links:
694  os.symlink(LHADATAPATH,process_dir+'/lib/PDFsets')
695  else:
696  shutil.copytree(LHADATAPATH,process_dir+'/lib/PDFsets')
697  mglog.info('Available PDFs are:')
698  mglog.info( sorted( [ x for x in os.listdir(process_dir+'/lib/PDFsets') if ".tar.gz" not in x ] ) )
699 
700  global MADGRAPH_COMMAND_STACK
701  MADGRAPH_COMMAND_STACK += [ '# Copy the LHAPDF files locally' ]
702  MADGRAPH_COMMAND_STACK += [ 'cp -r '+os.getcwd()+'/MGC_LHAPDF .' ]
703  MADGRAPH_COMMAND_STACK += [ 'cp -r '+process_dir+'/lib/PDFsets ${MGaMC_PROCESS_DIR}/lib/' ]
704 
705  return (LHAPATH,origLHAPATH,origLHAPDF_DATA_PATH)
706 
707 
708 # Function to set the number of cores and the running mode in the run card
709 def setNCores(process_dir, Ncores=None):
710  my_Ncores = Ncores
711  my_runMode = 2 if 'ATHENA_CORE_NUMBER' in os.environ else 0
712  if Ncores is None and 'ATHENA_CORE_NUMBER' in os.environ and int(os.environ['ATHENA_CORE_NUMBER'])>0:
713  my_Ncores = int(os.environ['ATHENA_CORE_NUMBER'])
714  my_runMode = 2
715  if my_Ncores is None:
716  mglog.info('Setting up for serial run')
717  my_Ncores = 1
718 
719  modify_config_card(process_dir=process_dir,settings={'nb_core':my_Ncores,'run_mode':my_runMode,'automatic_html_opening':'False'})
720 
721 
722 
723 
724 
725 
726 
728  madpath=os.environ['MADPATH']
729  if not os.access(madpath+'/bin/mg5_aMC',os.R_OK):
730  raise RuntimeError('mg5_aMC executable not found in '+madpath)
731  return madpath+'/bin/mg5_aMC'
732 
733 
734 def add_lifetimes(process_dir,threshold=None):
735  """ Add lifetimes to the generated LHE file. Should be
736  called after generate_events is called.
737  """
738 
739  me_exec=get_mg5_executable()
740 
741  if len(glob.glob(process_dir+'/Events/*'))<1:
742  mglog.error('Process dir '+process_dir+' does not contain events?')
743  run = glob.glob(process_dir+'/Events/*')[0].split('/')[-1]
744 
745  # Note : This slightly clunky implementation is needed for the time being
746  # See : https://answers.launchpad.net/mg5amcnlo/+question/267904
747 
748  tof_c = open('time_of_flight_exec_card','w')
749  tof_c.write('launch '+process_dir+''' -i
750 add_time_of_flight '''+run+((' --threshold='+str(threshold)) if threshold is not None else ''))
751  tof_c.close()
752 
753  mglog.info('Started adding time of flight info '+str(time.asctime()))
754 
755  global MADGRAPH_CATCH_ERRORS
756  generate = stack_subprocess([python,me_exec,'time_of_flight_exec_card'],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
757  (out,err) = generate.communicate()
758  error_check(err,generate.returncode)
759 
760  mglog.info('Finished adding time of flight information at '+str(time.asctime()))
761 
762  # Re-zip the file if needed
763  lhe_gz = glob.glob(process_dir+'/Events/*/*lhe.gz')[0]
764  if not os.access(lhe_gz,os.R_OK):
765  mglog.info('LHE file needs to be zipped')
766  lhe = glob.glob(process_dir+'/Events/*/*lhe.gz')[0]
767  rezip = stack_subprocess(['gzip',lhe])
768  rezip.wait()
769  mglog.info('Zipped')
770  else:
771  mglog.info('LHE file zipped by MadGraph automatically. Nothing to do')
772 
773  return True
774 
775 
776 def add_madspin(madspin_card=None,process_dir=MADGRAPH_GRIDPACK_LOCATION):
777  """ Run madspin on the generated LHE file. Should be
778  run when you have inputGeneratorFile set.
779  Only requires a simplified process with the same model that you are
780  interested in (needed to set up a process directory for MG5_aMC)
781  """
782 
783  me_exec=get_mg5_executable()
784 
785  if madspin_card is not None:
786  shutil.copyfile(madspin_card,process_dir+'/Cards/madspin_card.dat')
787 
788  if len(glob.glob(process_dir+'/Events/*'))<1:
789  mglog.error('Process dir '+process_dir+' does not contain events?')
790  proc_dir_list = glob.glob(process_dir+'/Events/*')
791  run=None
792  for adir in proc_dir_list:
793  if 'GridRun_' in adir:
794  run=adir.split('/')[-1]
795  break
796  else:
797  run=proc_dir_list[0].split('/')[-1]
798 
799  # Note : This slightly clunky implementation is needed for the time being
800  # See : https://answers.launchpad.net/mg5amcnlo/+question/267904
801 
802  ms_c = open('madspin_exec_card','w')
803  ms_c.write('launch '+process_dir+''' -i
804 decay_events '''+run)
805  ms_c.close()
806 
807  mglog.info('Started running madspin at '+str(time.asctime()))
808 
809  global MADGRAPH_CATCH_ERRORS
810  generate = stack_subprocess([python,me_exec,'madspin_exec_card'],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
811  (out,err) = generate.communicate()
812  error_check(err,generate.returncode)
813  if len(glob.glob(process_dir+'/Events/'+run+'_decayed_*/')) == 0:
814  mglog.error('No '+process_dir+'/Events/'+run+'_decayed_*/ can be found')
815  raise RuntimeError('Problem while running MadSpin')
816 
817  mglog.info('Finished running madspin at '+str(time.asctime()))
818 
819  # Re-zip the file if needed
820  lhe_gz = glob.glob(process_dir+'/Events/*/*lhe.gz')[0]
821  if not os.access(lhe_gz,os.R_OK):
822  mglog.info('LHE file needs to be zipped')
823  lhe = glob.glob(process_dir+'/Events/*/*lhe.gz')[0]
824  rezip = stack_subprocess(['gzip',lhe])
825  rezip.wait()
826  mglog.info('Zipped')
827  else:
828  mglog.info('LHE file zipped by MadGraph automatically. Nothing to do')
829 
830 
831 def madspin_on_lhe(input_LHE,madspin_card,runArgs=None,keep_original=False):
832  """ Run MadSpin on an input LHE file. Takes the process
833  from the LHE file, so you don't need to have a process directory
834  set up in advance. Runs MadSpin and packs the LHE file up appropriately
835  Needs runArgs for the file handling"""
836  if not os.access(input_LHE,os.R_OK):
837  raise RuntimeError('Could not find LHE file '+input_LHE)
838  if not os.access(madspin_card,os.R_OK):
839  raise RuntimeError('Could not find input MadSpin card '+madspin_card)
840  if keep_original:
841  shutil.copy(input_LHE,input_LHE+'.original')
842  mglog.info('Put backup copy of LHE file at '+input_LHE+'.original')
843  # Start writing the card for execution
844  madspin_exec_card = open('madspin_exec_card','w')
845  madspin_exec_card.write('import '+input_LHE+'\n')
846  # Based on the original card
847  input_madspin_card = open(madspin_card,'r')
848  has_launch = False
849  for l in input_madspin_card.readlines():
850  commands = l.split('#')[0].split()
851  # Skip import of a file name that isn't our file
852  if len(commands)>1 and 'import'==commands[0] and not 'model'==commands[1]:
853  continue
854  # Check for a launch command
855  if len(commands)>0 and 'launch' == commands[0]:
856  has_launch = True
857  madspin_exec_card.write(l.strip()+'\n')
858  if not has_launch:
859  madspin_exec_card.write('launch\n')
860  madspin_exec_card.close()
861  input_madspin_card.close()
862  # Now get the madspin executable
863  madpath=os.environ['MADPATH']
864  if not os.access(madpath+'/MadSpin/madspin',os.R_OK):
865  raise RuntimeError('madspin executable not found in '+madpath)
866  mglog.info('Starting madspin at '+str(time.asctime()))
867  global MADGRAPH_CATCH_ERRORS
868  generate = stack_subprocess([python,madpath+'/MadSpin/madspin','madspin_exec_card'],stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
869  (out,err) = generate.communicate()
870  error_check(err,generate.returncode)
871  mglog.info('Done with madspin at '+str(time.asctime()))
872  # Should now have a re-zipped LHE file
873  # We now have to do a shortened version of arrange_output below
874  # Clean up in case a link or file was already there
875  if os.path.exists(os.getcwd()+'/events.lhe'):
876  os.remove(os.getcwd()+'/events.lhe')
877 
878  mglog.info('Unzipping generated events.')
879  unzip = stack_subprocess(['gunzip','-f',input_LHE+'.gz'])
880  unzip.wait()
881 
882  mglog.info('Putting a copy in place for the transform.')
883  mod_output = open(os.getcwd()+'/events.lhe','w')
884 
885  #Removing empty lines in LHE
886  nEmpty=0
887  with open(input_LHE,'r') as fileobject:
888  for line in fileobject:
889  if line.strip():
890  mod_output.write(line)
891  else:
892  nEmpty=nEmpty+1
893  mod_output.close()
894 
895  mglog.info('Removed '+str(nEmpty)+' empty lines from LHEF')
896 
897  # Actually move over the dataset - this first part is horrible...
898  if runArgs is None:
899  raise RuntimeError('Must provide runArgs to madspin_on_lhe')
900 
901  outputDS = runArgs.outputTXTFile if hasattr(runArgs,'outputTXTFile') else 'tmp_LHE_events.tar.gz'
902 
903  mglog.info('Moving file over to '+outputDS.split('.tar.gz')[0]+'.events')
904  shutil.move(os.getcwd()+'/events.lhe',outputDS.split('.tar.gz')[0]+'.events')
905 
906  mglog.info('Re-zipping into dataset name '+outputDS)
907  rezip = stack_subprocess(['tar','cvzf',outputDS,outputDS.split('.tar.gz')[0]+'.events'])
908  rezip.wait()
909 
910  # shortening the outputDS in the case of an output TXT file
911  if hasattr(runArgs,'outputTXTFile') and runArgs.outputTXTFile is not None:
912  outputDS = outputDS.split('.TXT')[0]
913  # Do some fixing up for them
914  if runArgs is not None:
915  mglog.debug('Setting inputGenerator file to '+outputDS)
916  runArgs.inputGeneratorFile=outputDS
917 
918 
919 def arrange_output(process_dir=MADGRAPH_GRIDPACK_LOCATION,lhe_version=None,saveProcDir=False,runArgs=None,fixEventWeightsForBridgeMode=False):
920 
921  # NLO is not *really* the question here, we need to know if we should look for weighted or
922  # unweighted events in the output directory. MadSpin (above) only seems to give weighted
923  # results for now?
924  if len(glob.glob(os.path.join(process_dir, 'Events','*')))<1:
925  mglog.error('Process dir '+process_dir+' does not contain events?')
926  proc_dir_list = glob.glob(os.path.join(process_dir, 'Events', '*'))
927  this_run_name=None
928  # looping over possible directories to find the right one
929  for adir in proc_dir_list:
930  if 'decayed' in adir:# skipping '*decayed*' directories produced by MadSpin, will be picked later if they exist
931  continue
932  else:
933  if 'GridRun_' in adir:
934  this_run_name=adir
935  break # GridRun_* directories have priority
936  elif os.path.join(process_dir, 'Events',MADGRAPH_RUN_NAME) in adir:
937  this_run_name=adir
938  if not os.access(this_run_name,os.R_OK):
939  raise RuntimeError('Unable to locate run directory')
940 
941  hasUnweighted = os.access(this_run_name+'/unweighted_events.lhe.gz',os.R_OK)
942 
943  hasRunMadSpin=False
944  madspinDirs=sorted(glob.glob(this_run_name+'_decayed_*/'))
945  if len(madspinDirs):
946  hasRunMadSpin=True
947  if hasRunMadSpin and not hasUnweighted:
948  # check again:
949  hasUnweighted = os.access(madspinDirs[-1]+'/unweighted_events.lhe.gz',os.R_OK)
950 
951  global MADGRAPH_COMMAND_STACK
952  if hasRunMadSpin:
953  if len(madspinDirs):
954  if hasUnweighted:
955  # so this is a bit of a mess now...
956  # if madspin is run from an NLO grid pack the correct lhe events are at both
957  # madevent/Events/run_01/unweighted_events.lhe.gz
958  # and madevent/Events/run_01_decayed_1/events.lhe.gz
959  # so there are unweighted events but not in the madspinDir...
960  if os.path.exists(madspinDirs[-1]+'/unweighted_events.lhe.gz'):
961  MADGRAPH_COMMAND_STACK += ['mv '+madspinDirs[-1]+'/unweighted_events.lhe.gz'+' '+this_run_name+'/unweighted_events.lhe.gz']
962  shutil.move(madspinDirs[-1]+'/unweighted_events.lhe.gz',this_run_name+'/unweighted_events.lhe.gz')
963  mglog.info('Moving MadSpin events from '+madspinDirs[-1]+'/unweighted_events.lhe.gz to '+this_run_name+'/unweighted_events.lhe.gz')
964  elif os.path.exists(madspinDirs[-1]+'/events.lhe.gz'):
965  MADGRAPH_COMMAND_STACK += ['mv '+madspinDirs[-1]+'/events.lhe.gz'+' '+this_run_name+'/unweighted_events.lhe.gz']
966  shutil.move(madspinDirs[-1]+'/events.lhe.gz',this_run_name+'/unweighted_events.lhe.gz')
967  mglog.info('Moving MadSpin events from '+madspinDirs[-1]+'/events.lhe.gz to '+this_run_name+'/unweighted_events.lhe.gz')
968  else:
969  raise RuntimeError('MadSpin was run but can\'t find files :(')
970 
971  else:
972  MADGRAPH_COMMAND_STACK += ['mv '+madspinDirs[-1]+'/events.lhe.gz '+this_run_name+'/events.lhe.gz']
973  shutil.move(madspinDirs[-1]+'/events.lhe.gz',this_run_name+'/events.lhe.gz')
974  mglog.info('Moving MadSpin events from '+madspinDirs[-1]+'/events.lhe.gz to '+this_run_name+'/events.lhe.gz')
975 
976  else:
977  mglog.error('MadSpin was run but can\'t find output folder '+(this_run_name+'_decayed_1/'))
978  raise RuntimeError('MadSpin was run but can\'t find output folder '+(this_run_name+'_decayed_1/'))
979 
980  if fixEventWeightsForBridgeMode:
981  mglog.info("Fixing event weights after MadSpin... initial checks.")
982 
983  # get the cross section from the undecayed LHE file
984  spinmodenone=False
985  MGnumevents=-1
986  MGintweight=-1
987 
988  if hasUnweighted:
989  eventsfilename="unweighted_events"
990  else:
991  eventsfilename="events"
992  unzip = stack_subprocess(['gunzip','-f',this_run_name+'/%s.lhe.gz' % eventsfilename])
993  unzip.wait()
994 
995  for line in open(process_dir+'/Events/'+MADGRAPH_RUN_NAME+'/%s.lhe'%eventsfilename):
996  if "Number of Events" in line:
997  sline=line.split()
998  MGnumevents=int(sline[-1])
999  elif "Integrated weight (pb)" in line:
1000  sline=line.split()
1001  MGintweight=float(sline[-1])
1002  elif "set spinmode none" in line:
1003  spinmodenone=True
1004  elif "</header>" in line:
1005  break
1006 
1007  if spinmodenone and MGnumevents>0 and MGintweight>0:
1008  mglog.info("Fixing event weights after MadSpin... modifying LHE file.")
1009  newlhe=open(this_run_name+'/%s_fixXS.lhe'%eventsfilename,'w')
1010  initlinecount=0
1011  eventlinecount=0
1012  inInit=False
1013  inEvent=False
1014 
1015  # new default for MG 2.6.1+ (https://its.cern.ch/jira/browse/AGENE-1725)
1016  # but verified from LHE below.
1017  event_norm_setting="average"
1018 
1019  for line in open(this_run_name+'/%s.lhe'%eventsfilename):
1020 
1021  newline=line
1022  if "<init>" in line:
1023  inInit=True
1024  initlinecount=0
1025  elif "</init>" in line:
1026  inInit=False
1027  elif inInit and initlinecount==0:
1028  initlinecount=1
1029  # check event_norm setting in LHE file, deteremines how Pythia interprets event weights
1030  sline=line.split()
1031  if abs(int(sline[-2])) == 3:
1032  event_norm_setting="sum"
1033  elif abs(int(sline[-2])) == 4:
1034  event_norm_setting="average"
1035  elif inInit and initlinecount==1:
1036  sline=line.split()
1037  # update the global XS info
1038  relunc=float(sline[1])/float(sline[0])
1039  sline[0]=str(MGintweight)
1040  sline[1]=str(float(sline[0])*relunc)
1041  if event_norm_setting=="sum":
1042  sline[2]=str(MGintweight/MGnumevents)
1043  elif event_norm_setting=="average":
1044  sline[2]=str(MGintweight)
1045  newline=' '.join(sline)
1046  newline+="\n"
1047  initlinecount+=1
1048  elif inInit and initlinecount>1:
1049  initlinecount+=1
1050  elif "<event>" in line:
1051  inEvent=True
1052  eventlinecount=0
1053  elif "</event>" in line:
1054  inEvent=False
1055  elif inEvent and eventlinecount==0:
1056  sline=line.split()
1057  # next change the per-event weights
1058  if event_norm_setting=="sum":
1059  sline[2]=str(MGintweight/MGnumevents)
1060  elif event_norm_setting=="average":
1061  sline[2]=str(MGintweight)
1062  newline=' '.join(sline)
1063  newline+="\n"
1064  eventlinecount+=1
1065  newlhe.write(newline)
1066  newlhe.close()
1067 
1068  mglog.info("Fixing event weights after MadSpin... cleaning up.")
1069  shutil.copyfile(this_run_name+'/%s.lhe' % eventsfilename,
1070  this_run_name+'/%s_badXS.lhe' % eventsfilename)
1071 
1072  shutil.move(this_run_name+'/%s_fixXS.lhe' % eventsfilename,
1073  this_run_name+'/%s.lhe' % eventsfilename)
1074 
1075  rezip = stack_subprocess(['gzip',this_run_name+'/%s.lhe' % eventsfilename])
1076  rezip.wait()
1077 
1078  rezip = stack_subprocess(['gzip',this_run_name+'/%s_badXS.lhe' % eventsfilename])
1079  rezip.wait()
1080 
1081  # Clean up in case a link or file was already there
1082  if os.path.exists(os.getcwd()+'/events.lhe'):
1083  os.remove(os.getcwd()+'/events.lhe')
1084 
1085  mglog.info('Unzipping generated events.')
1086  if hasUnweighted:
1087  unzip = stack_subprocess(['gunzip','-f',this_run_name+'/unweighted_events.lhe.gz'])
1088  unzip.wait()
1089  else:
1090  unzip = stack_subprocess(['gunzip','-f',this_run_name+'/events.lhe.gz'])
1091  unzip.wait()
1092 
1093  mglog.info('Putting a copy in place for the transform.')
1094  if hasUnweighted:
1095  orig_input = this_run_name+'/unweighted_events.lhe'
1096  mod_output = open(os.getcwd()+'/events.lhe','w')
1097  else:
1098  orig_input = this_run_name+'/events.lhe'
1099  mod_output = open(os.getcwd()+'/events.lhe','w')
1100 
1101  #Removing empty lines and bad comments in LHE
1102  #and check for existence of weights
1103  initrwgt=None
1104  nEmpty=0
1105  lhe_weights=[]
1106  with open(orig_input,'r') as fileobject:
1107  for line in fileobject:
1108  if line.strip():
1109  # search for bad characters (neccessary until at least MG5 2.8.1)
1110  newline=line
1111  if '#' not in newline:
1112  newline=newline
1113  elif '>' not in newline[ newline.find('#'): ]:
1114  newline=newline
1115  else:
1116  mglog.info('Found bad LHE line with an XML mark in a comment: "'+newline.strip()+'"')
1117  newline=newline[:newline.find('#')]+'#'+ (newline[newline.find('#'):].replace('>','-'))
1118  # check for weightnames that should exist, simplify nominal weight names
1119  if initrwgt is False:
1120  pass
1121  elif "</initrwgt>" in newline:
1122  initrwgt=False
1123  elif "<initrwgt>" in newline:
1124  initrwgt=True
1125  elif initrwgt is not None:
1126  newline=newline.replace('_DYNSCALE-1','')
1127  if '</weight>' in newline:
1128  iend=newline.find('</weight>')
1129  istart=newline[:iend].rfind('>')
1130  lhe_weights+=[newline[istart+1:iend].strip()]
1131  mod_output.write(newline)
1132  else:
1133  nEmpty=nEmpty+1
1134  mod_output.close()
1135  mglog.info('Removed '+str(nEmpty)+' empty lines from LHEF')
1136 
1137  mglog.info("The following "+str(len(lhe_weights))+" weights have been written to the LHE file: "+",".join(lhe_weights))
1138  expected_weights=get_expected_reweight_names(get_reweight_card(process_dir))
1139  expected_weights+=get_expected_systematic_names(MADGRAPH_PDFSETTING)
1140  mglog.info("Checking whether the following expected weights are in LHE file: "+",".join(expected_weights))
1141  for w in expected_weights:
1142  if w not in lhe_weights:
1143  raise RuntimeError("Did not find expected weight "+w+" in lhe file. Did the reweight or systematics module crash?")
1144  mglog.info("Found all required weights!")
1145 
1146  if lhe_version:
1147  mod_output2 = open(os.getcwd()+'/events.lhe','r')
1148  test=mod_output2.readline()
1149  if 'version="' in test:
1150  mglog.info('Applying LHE version hack')
1151  final_file = open(os.getcwd()+'/events.lhe.copy','w')
1152  final_file.write('<LesHouchesEvents version="%i.0">\n'%lhe_version)
1153  shutil.copyfileobj(mod_output2, final_file)
1154  final_file.close()
1155  shutil.copy(os.getcwd()+'/events.lhe.copy',os.getcwd()+'/events.lhe')
1156  # Clean up after ourselves
1157  os.remove(os.getcwd()+'/events.lhe.copy')
1158  mod_output2.close()
1159 
1160  # Actually move over the dataset
1161  if runArgs is None:
1162  raise RuntimeError('Must provide runArgs to arrange_output')
1163 
1164  if hasattr(runArgs,'outputTXTFile'):
1165  outputDS = runArgs.outputTXTFile
1166  else:
1167  outputDS = 'tmp_LHE_events.tar.gz'
1168 
1169  mglog.info('Moving file over to '+outputDS.split('.tar.gz')[0]+'.events')
1170 
1171  shutil.move(os.getcwd()+'/events.lhe',outputDS.split('.tar.gz')[0]+'.events')
1172 
1173  mglog.info('Re-zipping into dataset name '+outputDS)
1174  rezip = stack_subprocess(['tar','cvzf',outputDS,outputDS.split('.tar.gz')[0]+'.events'])
1175  rezip.wait()
1176 
1177  if not saveProcDir:
1178  mglog.info('Removing the process directory')
1179  shutil.rmtree(process_dir,ignore_errors=True)
1180 
1181  if os.path.isdir('MGC_LHAPDF/'):
1182  shutil.rmtree('MGC_LHAPDF/',ignore_errors=True)
1183 
1184  # shortening the outputDS in the case of an output TXT file
1185  if hasattr(runArgs,'outputTXTFile') and runArgs.outputTXTFile is not None:
1186  outputDS = outputDS.split('.TXT')[0]
1187  # Do some fixing up for them
1188  if runArgs is not None:
1189  mglog.debug('Setting inputGenerator file to '+outputDS)
1190  runArgs.inputGeneratorFile=outputDS
1191 
1192  mglog.info('All done with output arranging!')
1193  return outputDS
1194 
1195 def get_expected_reweight_names(reweight_card_loc):
1196  if reweight_card_loc is None:
1197  return []
1198  names=[]
1199  f_rw=open(reweight_card_loc)
1200  for line in f_rw:
1201  if 'launch' not in line:
1202  continue
1203  match=re.match(r'launch.*--rwgt_info\s*=\s*(\S+).*',line.strip())
1204  if len(match.groups())!=1:
1205  raise RuntimeError('Unexpected format of reweight card in line'+line)
1206  else:
1207  names+=[match.group(1)]
1208  f_rw.close()
1209  return names
1210 
1212  names=[]
1213  if syst_setting is None or 'central_pdf' not in syst_setting:
1214  mglog.warning("Systematics have not been defined via base fragment or 'MADGRAPH_PDFSETTING', cannot check for expected weights")
1215  return []
1216  if 'pdf_variations' in syst_setting and isinstance(syst_setting['pdf_variations'],list):
1217  names+=[MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO%{'mur':1.0,'muf':1.0,'pdf':syst_setting['central_pdf']}]
1218  for pdf in syst_setting['pdf_variations']:
1219  names+=[MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO%{'mur':1.0,'muf':1.0,'pdf':pdf+1}]
1220  if 'alternative_pdfs' in syst_setting and isinstance(syst_setting['alternative_pdfs'],list):
1221  for pdf in syst_setting['alternative_pdfs']:
1222  names+=[MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO%{'mur':1.0,'muf':1.0,'pdf':pdf}]
1223  if 'scale_variations' in syst_setting and isinstance(syst_setting['scale_variations'],list):
1224  for mur in syst_setting['scale_variations']:
1225  for muf in syst_setting['scale_variations']:
1226  names+=[MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO%{'mur':mur,'muf':muf,'pdf':syst_setting['central_pdf']}]
1227  return names
1228 
1229 def setup_bias_module(bias_module,process_dir):
1230  run_card = process_dir+'/Cards/run_card.dat'
1231  if isinstance(bias_module,tuple):
1232  mglog.info('Using bias module '+bias_module[0])
1233  the_run_card = open(run_card,'r')
1234  for line in the_run_card:
1235  if 'bias_module' in line and not bias_module[0] in line:
1236  raise RuntimeError('You need to add the bias module '+bias_module[0]+' to the run card to actually run it')
1237  the_run_card.close()
1238  if len(bias_module)!=3:
1239  raise RuntimeError('Please give a 3-tuple of strings containing bias module name, bias module, and makefile. Alternatively, give path to bias module tarball.')
1240  bias_module_newpath=process_dir+'/Source/BIAS/'+bias_module[0]
1241  os.makedirs(bias_module_newpath)
1242  bias_module_file=open(bias_module_newpath+'/'+bias_module[0]+'.f','w')
1243  bias_module_file.write(bias_module[1])
1244  bias_module_file.close()
1245  bias_module_make_file=open(bias_module_newpath+'/Makefile','w')
1246  bias_module_make_file.write(bias_module[2])
1247  bias_module_make_file.close()
1248  else:
1249  mglog.info('Using bias module '+bias_module)
1250  bias_module_name=bias_module.split('/')[-1].replace('.gz','')
1251  bias_module_name=bias_module_name.replace('.tar','')
1252  the_run_card = open(run_card,'r')
1253  for line in the_run_card:
1254  if 'bias_module' in line and bias_module_name not in line:
1255  raise RuntimeError('You need to add the bias module '+bias_module_name+' to the run card to actually run it')
1256  the_run_card.close()
1257 
1258  if os.path.exists(bias_module+'.tar.gz'):
1259  bias_module_path=bias_module+'.tar.gz'
1260  elif os.path.exists(bias_module+'.gz'):
1261  bias_module_path=bias_module+'.gz'
1262  elif os.path.exists(bias_module):
1263  bias_module_path=bias_module
1264  else:
1265  mglog.error('Did not find bias module '+bias_module+' , this path should point to folder or tarball. Alternatively give a tuple of strings containing module name, module, and makefile')
1266  return 1
1267  bias_module_newpath=process_dir+'/Source/BIAS/'+bias_module_path.split('/')[-1]
1268  mglog.info('Copying bias module into place: '+bias_module_newpath)
1269  shutil.copy(bias_module_path,bias_module_newpath)
1270  mglog.info('Unpacking bias module')
1271  if bias_module_newpath.endswith('.tar.gz'):
1272  untar = stack_subprocess(['tar','xvzf',bias_module_newpath,'--directory='+process_dir+'/Source/BIAS/'])
1273  untar.wait()
1274  elif bias_module_path.endswith('.gz'):
1275  gunzip = stack_subprocess(['gunzip',bias_module_newpath])
1276  gunzip.wait()
1277 
1278 
1279 def get_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION):
1280  if os.access(process_dir+'/Cards/reweight_card.dat',os.R_OK):
1281  return process_dir+'/Cards/reweight_card.dat'
1282  return None
1283 
1284 
1285 def check_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION):
1286  reweight_card=get_reweight_card(process_dir=process_dir)
1287  shutil.move(reweight_card,reweight_card+'.old')
1288  oldcard = open(reweight_card+'.old','r')
1289  newcard = open(reweight_card,'w')
1290  changed = False
1291  info_expression=r'launch.*--rwgt_info\s*=\s*(\S+).*'
1292  name_expression=info_expression.replace('info','name')
1293  goodname_expression=r'^[A-Za-z0-9_\-.]+$'
1294  for line in oldcard:
1295  # we are only interested in the 'launch' line
1296  if not line.strip().startswith('launch') :
1297  newcard.write(line)
1298  else:
1299  rwgt_name_match=re.match(name_expression,line.strip())
1300  rwgt_info_match=re.match(info_expression,line.strip())
1301  if rwgt_name_match is None and rwgt_info_match is None:
1302  raise RuntimeError('Every reweighting should have a --rwgt_info (see https://cp3.irmp.ucl.ac.be/projects/madgraph/wiki/Reweight), please update your reweight_card accordingly. Line to fix: '+line)
1303  for match in [rwgt_info_match,rwgt_name_match]:
1304  if match is None:
1305  continue
1306  if len(match.groups())!=1:
1307  raise RuntimeError('Unexpected format of reweight card in line: '+line)
1308  if not re.match(goodname_expression,match.group(1)):
1309  raise RuntimeError('No special character in reweighting info/name, only allowing '+goodname_expression)
1310  if rwgt_info_match is not None:
1311  newcard.write(line)
1312  elif rwgt_name_match is not None:
1313  newcard.write(line.strip()+' --rwgt_info={0}\n'.format(rwgt_name_match.group(1)))
1314  changed=True
1315  if changed:
1316  mglog.info('Updated reweight_card')
1317  newcard.close()
1318  oldcard.close()
1319 
1320 
1321 def update_lhe_file(lhe_file_old,param_card_old=None,lhe_file_new=None,masses={},delete_old_lhe=True):
1322  """Build a new LHE file from an old one and an updated param card.
1323  The masses of some particles can be changed via the masses dictionary. No particles that appear in the events
1324  may have their masses changed.
1325  If the param card is provided, the decay block in the LHE file will be replaced with the one in the param card.
1326  By default, the old LHE file is removed.
1327  If None is provided as a new LHE file name, the new file will replace the old one."""
1328  # If we want to just use a temp file, then put in a little temp holder
1329  lhe_file_new_tmp = lhe_file_new if lhe_file_new is not None else lhe_file_old+'.tmp'
1330  # Make sure the LHE file is there
1331  if not os.access(lhe_file_old,os.R_OK):
1332  raise RuntimeError('Could not access old LHE file at '+str(lhe_file_old)+'. Please check the file location.')
1333  # Grab the old param card
1334  if param_card_old is not None:
1335  paramcard = subprocess.Popen(['get_files','-data',param_card_old])
1336  paramcard.wait()
1337  if not os.access(param_card_old,os.R_OK):
1338  raise RuntimeError('Could not get param card '+param_card_old)
1339  # Don't overwrite old param cards
1340  if os.access(lhe_file_new_tmp,os.R_OK):
1341  raise RuntimeError('Old file at'+str(lhe_file_new_tmp)+' in the current directory. Dont want to clobber it. Please move it first.')
1342 
1343  newlhe = open(lhe_file_new_tmp,'w')
1344  blockName = None
1345  decayEdit = False
1346  eventRead = False
1347  particles_in_events = []
1348  # Decay block ends with </slha>
1349 
1350  with open(lhe_file_old,'r') as fileobject:
1351  for line in fileobject:
1352  if decayEdit and '</slha>' not in line:
1353  continue
1354  if decayEdit and '</slha>' in line:
1355  decayEdit = False
1356  if line.strip().upper().startswith('BLOCK') or line.strip().upper().startswith('DECAY')\
1357  and len(line.strip().split()) > 1:
1358  pos = 0 if line.strip().startswith('DECAY') else 1
1359  blockName = line.strip().upper().split()[pos]
1360 
1361  akey = None
1362  if blockName != 'DECAY' and len(line.strip().split()) > 0:
1363  akey = line.strip().split()[0]
1364  elif blockName == 'DECAY' and len(line.strip().split()) > 1:
1365  akey = line.strip().split()[1]
1366 
1367  # Replace the masses with those in the dictionary
1368  if akey is not None and blockName == 'MASS' and akey in masses:
1369  newlhe.write(' '+akey+' '+str(masses[akey])+' # \n')
1370  mglog.info(' '+akey+' '+str(masses[akey])+' #')
1371  decayEdit = False
1372  continue
1373 
1374  # Replace the entire decay section of the LHE file with the one from the param card
1375  if blockName == 'DECAY' and param_card_old is not None:
1376  # We are now reading the decay blocks! Take them from the param card
1377  oldparam = open(param_card_old,'r')
1378  newDecays = False
1379  for old_line in oldparam.readlines():
1380  newBlockName = None
1381  if old_line.strip().upper().startswith('DECAY') and len(old_line.strip().split()) > 1:
1382  newBlockName = line.strip().upper().split()[pos]
1383  if newDecays:
1384  newlhe.write(old_line)
1385  elif newBlockName == 'DECAY':
1386  newDecays = True
1387  newlhe.write(old_line)
1388  oldparam.close()
1389  # Done adding the decays
1390  decayEdit = True
1391  blockName = None
1392  continue
1393 
1394  # Keep a record of the particles that are in the events
1395  if not eventRead and '<event>' in line:
1396  eventRead = True
1397  if eventRead:
1398  if len(line.split())==11:
1399  aparticle = line.split()[0]
1400  if aparticle not in particles_in_events:
1401  particles_in_events += [aparticle]
1402 
1403  # Otherwise write the line again
1404  newlhe.write(line)
1405 
1406  # Check that none of the particles that we were setting the masses of appear in the LHE events
1407  for akey in masses:
1408  if akey in particles_in_events:
1409  mglog.error('Attempted to change mass of a particle that was in an LHE event! This is not allowed!')
1410  return -1
1411 
1412  # Close up and return
1413  newlhe.close()
1414 
1415  # Move the new file to the old file location
1416  if lhe_file_new is None:
1417  os.remove(lhe_file_old)
1418  shutil.move(lhe_file_new_tmp,lhe_file_old)
1419  lhe_file_new_tmp = lhe_file_old
1420  # Delete the old file if requested
1421  elif delete_old_lhe:
1422  os.remove(lhe_file_old)
1423 
1424  return lhe_file_new_tmp
1425 
1426 
1427 
1428 def find_key_and_update(akey,dictionary):
1429  """ Helper function when looking at param cards
1430  In some cases it's tricky to match keys - they may differ
1431  only in white space. This tries to sort out when we have
1432  a match, and then uses the one in blockParams afterwards.
1433  In the case of no match, it returns the original key.
1434  """
1435  test_key = ' '.join(akey.strip().replace('\t',' ').split())
1436  for key in dictionary:
1437  mod_key = ' '.join(key.strip().replace('\t',' ').split())
1438  if mod_key==test_key:
1439  return key
1440  return akey
1441 
1442 
1443 def modify_param_card(param_card_input=None,param_card_backup=None,process_dir=MADGRAPH_GRIDPACK_LOCATION,params={},output_location=None):
1444  """Build a new param_card.dat from an existing one.
1445  Params should be a dictionary of dictionaries. The first key is the block name, and the second in the param name.
1446  Keys can include MASS (for masses) and DECAY X (for decays of particle X)"""
1447  # Grab the old param card and move it into place
1448 
1449  # Check for the default run card location
1450  if param_card_input is None:
1451  param_card_input=process_dir+'/Cards/param_card.dat'
1452  elif param_card_input is not None and not os.access(param_card_input,os.R_OK):
1453  paramcard = subprocess.Popen(['get_files','-data',param_card_input])
1454  paramcard.wait()
1455  if not os.access(param_card_input,os.R_OK):
1456  raise RuntimeError('Could not get param card '+param_card_input)
1457  mglog.info('Using input param card at '+param_card_input)
1458 
1459  #ensure all blocknames and paramnames are upper case
1460  paramsUpper = {}
1461  for blockName in list(params.keys()):
1462  paramsUpper[blockName.upper()] = {}
1463  for paramName in list(params[blockName].keys()):
1464  paramsUpper[blockName.upper()][paramName.upper()] = params[blockName][paramName]
1465 
1466  if param_card_backup is not None:
1467  mglog.info('Keeping backup of original param card at '+param_card_backup)
1468  param_card_old = param_card_backup
1469  else:
1470  param_card_old = param_card_input+'.old_to_be_deleted'
1471  if os.path.isfile(param_card_old):
1472  os.unlink(param_card_old) # delete old backup
1473  os.rename(param_card_input, param_card_old) # change name of original card
1474 
1475  oldcard = open(param_card_old,'r')
1476  param_card_location= process_dir+'/Cards/param_card.dat' if output_location is None else output_location
1477  newcard = open(param_card_location,'w')
1478  decayEdit = False #only becomes true in a DECAY block when specifying the BR
1479  blockName = ""
1480  doneParams = {} #tracks which params have been done
1481  for linewithcomment in oldcard:
1482  line=linewithcomment.split('#')[0]
1483  if line.strip().upper().startswith('BLOCK') or line.strip().upper().startswith('DECAY')\
1484  and len(line.strip().split()) > 1:
1485  if decayEdit and blockName == 'DECAY':
1486  decayEdit = False # Start a new DECAY block
1487  pos = 0 if line.strip().startswith('DECAY') else 1
1488  if blockName=='MASS' and 'MASS' in paramsUpper:
1489  # Any residual masses to set?
1490  if "MASS" in doneParams:
1491  leftOvers = [ x for x in paramsUpper['MASS'] if x not in doneParams['MASS'] ]
1492  else:
1493  leftOvers = [ x for x in paramsUpper['MASS'] ]
1494 
1495  for pdg_id in leftOvers:
1496  mglog.warning('Adding mass line for '+str(pdg_id)+' = '+str(paramsUpper['MASS'][pdg_id])+' which was not in original param card')
1497  newcard.write(' '+str(pdg_id)+' '+str(paramsUpper['MASS'][pdg_id])+'\n')
1498  doneParams['MASS'][pdg_id]=True
1499  if blockName=='DECAY' and 'DECAY' not in line.strip().upper() and 'DECAY' in paramsUpper:
1500  # Any residual decays to include?
1501  leftOvers = [ x for x in paramsUpper['DECAY'] if x not in doneParams['DECAY'] ]
1502  for pdg_id in leftOvers:
1503  mglog.warning('Adding decay for pdg id '+str(pdg_id)+' which was not in the original param card')
1504  newcard.write( paramsUpper['DECAY'][pdg_id].strip()+'\n' )
1505  doneParams['DECAY'][pdg_id]=True
1506  blockName = line.strip().upper().split()[pos]
1507  if decayEdit:
1508  continue #skipping these lines because we are in an edit of the DECAY BR
1509 
1510  akey = None
1511  if blockName != 'DECAY' and len(line.strip().split()) > 0:
1512  # The line is already without the comment.
1513  # In the case of mixing matrices this is a bit tricky
1514  if len(line.split())==2:
1515  akey = line.upper().strip().split()[0]
1516  else:
1517  # Take everything but the last word
1518  akey = line.upper().strip()[:line.strip().rfind(' ')].strip()
1519  elif blockName == 'DECAY' and len(line.strip().split()) > 1:
1520  akey = line.strip().split()[1]
1521  if akey is None:
1522  newcard.write(linewithcomment)
1523  continue
1524 
1525  #check if we have params for this block
1526  if blockName not in paramsUpper:
1527  newcard.write(linewithcomment)
1528  continue
1529  blockParams = paramsUpper[blockName]
1530  # Check the spacing in the key
1531  akey = find_key_and_update(akey,blockParams)
1532 
1533  # look for a string key, which would follow a #
1534  stringkey = None
1535  if '#' in linewithcomment: #ignores comment lines
1536  stringkey = linewithcomment[linewithcomment.find('#')+1:].strip()
1537  if len(stringkey.split()) > 0:
1538  stringkey = stringkey.split()[0].upper()
1539 
1540  if akey not in blockParams and not (stringkey is not None and stringkey in blockParams):
1541  newcard.write(linewithcomment)
1542  continue
1543 
1544  if akey in blockParams and (stringkey is not None and stringkey in blockParams):
1545  raise RuntimeError('Conflicting use of numeric and string keys '+akey+' and '+stringkey)
1546 
1547  theParam = blockParams.get(akey,blockParams[stringkey] if stringkey in blockParams else None)
1548  if blockName not in doneParams:
1549  doneParams[blockName] = {}
1550  if akey in blockParams:
1551  doneParams[blockName][akey]=True
1552  elif stringkey is not None and stringkey in blockParams:
1553  doneParams[blockName][stringkey]=True
1554 
1555  #do special case of DECAY block
1556  if blockName=="DECAY":
1557  if theParam.splitlines()[0].split()[0].upper()=="DECAY":
1558  #specifying the full decay block
1559  for newline in theParam.splitlines():
1560  newcard.write(newline+'\n')
1561  mglog.info(newline)
1562  decayEdit = True
1563  else: #just updating the total width
1564  newcard.write('DECAY '+akey+' '+str(theParam)+' # '+(linewithcomment[linewithcomment.find('#')+1:].strip() if linewithcomment.find('#')>0 else "")+'\n')
1565  mglog.info('DECAY '+akey+' '+str(theParam)+' # '+(linewithcomment[linewithcomment.find('#')+1:].strip() if linewithcomment.find('#')>0 else "")+'\n')
1566  # second special case of QNUMBERS
1567  elif blockName=='QNUMBERS':
1568  #specifying the full QNUMBERS block
1569  for newline in theParam.splitlines():
1570  newcard.write(newline+'\n')
1571  mglog.info(newline)
1572  decayEdit = True
1573  else: #just updating the parameter
1574  newcard.write(' '+akey+' '+str(theParam)+' # '+(linewithcomment[linewithcomment.find('#')+1:].strip() if linewithcomment.find('#')>0 else "")+'\n')
1575  mglog.info(' '+akey+' '+str(theParam)+' # '+(linewithcomment[linewithcomment.find('#')+1:].strip() if linewithcomment.find('#')>0 else "")+'\n')
1576  # Done editing the line!
1577 
1578  #check that all specified parameters have been updated (helps to catch typos)
1579  for blockName in paramsUpper:
1580  if blockName not in doneParams and len(paramsUpper[blockName].keys())>0:
1581  raise RuntimeError('Did not find any of the parameters for block '+blockName+' in param_card')
1582  for paramName in paramsUpper[blockName]:
1583  if paramName not in doneParams[blockName]:
1584  raise RuntimeError('Was not able to replace parameter '+paramName+' in param_card')
1585 
1586  # Close up and return
1587  oldcard.close()
1588  newcard.close()
1589 
1590 
1591 
1592 def print_cards_from_dir(process_dir=MADGRAPH_GRIDPACK_LOCATION):
1593  card_dir=process_dir+'/Cards/'
1594  print_cards(proc_card=card_dir+'proc_card_mg5.dat',run_card=card_dir+'run_card.dat',param_card=card_dir+'param_card.dat',\
1595  madspin_card=card_dir+'madspin_card.dat',reweight_card=card_dir+'reweight_card.dat',warn_on_missing=False)
1596 
1597 
1598 def print_cards(proc_card='proc_card_mg5.dat',run_card=None,param_card=None,madspin_card=None,reweight_card=None,warn_on_missing=True):
1599  if os.access(proc_card,os.R_OK):
1600  mglog.info("proc_card:")
1601  procCard = subprocess.Popen(['cat',proc_card])
1602  procCard.wait()
1603  elif warn_on_missing:
1604  mglog.warning('No proc_card: '+proc_card+' found')
1605 
1606  if run_card is not None and os.access(run_card,os.R_OK):
1607  mglog.info("run_card:")
1608  runCard = subprocess.Popen(['cat',run_card])
1609  runCard.wait()
1610  elif run_card is not None and warn_on_missing:
1611  mglog.warning('No run_card: '+run_card+' found')
1612  else:
1613  mglog.info('Default run card in use')
1614 
1615  if param_card is not None and os.access(param_card,os.R_OK):
1616  mglog.info("param_card:")
1617  paramCard = subprocess.Popen(['cat',param_card])
1618  paramCard.wait()
1619  elif param_card is not None and warn_on_missing:
1620  mglog.warning('No param_card: '+param_card+' found')
1621  else:
1622  mglog.info('Default param card in use')
1623 
1624  if madspin_card is not None and os.access(madspin_card,os.R_OK):
1625  mglog.info("madspin_card:")
1626  madspinCard = subprocess.Popen(['cat',madspin_card])
1627  madspinCard.wait()
1628  elif madspin_card is not None and warn_on_missing:
1629  mglog.warning('No madspin_card: '+madspin_card+' found')
1630  else:
1631  mglog.info('No madspin card in use')
1632 
1633  if reweight_card is not None and os.access(reweight_card,os.R_OK):
1634  mglog.info("reweight_card:")
1635  madspinCard = subprocess.Popen(['cat',reweight_card])
1636  madspinCard.wait()
1637  elif reweight_card is not None and warn_on_missing:
1638  mglog.warning('No reweight_card: '+reweight_card+' found')
1639  else:
1640  mglog.info('No reweight card in use')
1641 
1642 
1644  """ Simple function for checking if there is a grid pack.
1645  Relies on the specific location of the unpacked gridpack (madevent)
1646  which is here set as a global variable. The gridpack is untarred by
1647  the transform (Gen_tf.py) and no sign is sent to the job itself
1648  that there is a gridpack in use except the file's existence"""
1649  if os.access(MADGRAPH_GRIDPACK_LOCATION,os.R_OK):
1650  mglog.info('Located input grid pack area')
1651  return True
1652  return False
1653 
1654 
1655 
1656 def modify_run_card(run_card_input=None,run_card_backup=None,process_dir=MADGRAPH_GRIDPACK_LOCATION,runArgs=None,settings={},skipBaseFragment=False):
1657  """Build a new run_card.dat from an existing one.
1658  This function can get a fresh runcard from DATAPATH or start from the process directory.
1659  Settings is a dictionary of keys (no spaces needed) and values to replace.
1660  """
1661 
1662  # Operate on lower case settings, and choose the capitalization MG5 has as the default (or all lower case)
1663  settings_lower = {}
1664  for s in list(settings.keys()):
1665  settings_lower[s.lower()] = settings[s]
1666 
1667  # Check for the default run card location
1668  if run_card_input is None:
1669  run_card_input=get_default_runcard(process_dir)
1670  elif run_card_input is not None and not os.access(run_card_input,os.R_OK):
1671  runcard = subprocess.Popen(['get_files','-data',run_card_input])
1672  runcard.wait()
1673  if not os.access(run_card_input,os.R_OK):
1674  raise RuntimeError('Could not get run card '+run_card_input)
1675 
1676  # guess NLO
1677  isNLO=is_NLO_run(process_dir=process_dir)
1678  # add gobal PDF and scale uncertainty config to extras, except PDF or weights for syscal config are explictly set
1679  if not skipBaseFragment:
1680  MadGraphControl.MadGraphSystematicsUtils.setup_pdf_and_systematic_weights(MADGRAPH_PDFSETTING,settings_lower,isNLO)
1681 
1682  # Get some info out of the runArgs
1683  if runArgs is not None:
1684  beamEnergy,rand_seed = get_runArgs_info(runArgs)
1685  if 'iseed' not in settings_lower:
1686  settings_lower['iseed']=rand_seed
1687  if not isNLO and 'python_seed' not in settings_lower:
1688  settings_lower['python_seed']=rand_seed
1689  if 'beamenergy' in settings_lower:
1690  mglog.warning('Do not set beam energy in MG settings. The variables are ebeam1 and ebeam2. Will use your setting of '+str(settings_lower['beamenergy']))
1691  beamEnergy=settings_lower['beamenergy']
1692  settings_lower.pop('beamenergy')
1693  if 'ebeam1' not in settings_lower:
1694  settings_lower['ebeam1']=beamEnergy
1695  if 'ebeam2' not in settings_lower:
1696  settings_lower['ebeam2']=beamEnergy
1697  # Make sure nevents is an integer
1698  if 'nevents' in settings_lower:
1699  settings_lower['nevents'] = int(settings_lower['nevents'])
1700 
1701  # Normalise custom_fcts early so the rewritten run_card uses the full path
1702  if 'custom_fcts' in settings_lower and settings_lower['custom_fcts']:
1703  raw_name = str(settings_lower['custom_fcts']).split()[0]
1704  # Determine jobConfig directory
1705  if runArgs is not None and hasattr(runArgs, 'jobConfig'):
1706  cfgdir = runArgs.jobConfig[0] if isinstance(runArgs.jobConfig, (list, tuple)) else runArgs.jobConfig
1707  # Build full path and make absolute
1708  full_path = os.path.join(cfgdir, raw_name)
1709  settings_lower['custom_fcts'] = os.path.abspath(full_path)
1710  print(f"Using custom function(s), specified in custom_fcts with path: {settings_lower['custom_fcts']}")
1711  else:
1712  # For internal tests, where jobConfig is not set
1713  settings_lower['custom_fcts'] = os.path.abspath(raw_name)
1714 
1715  mglog.info('Modifying run card located at '+run_card_input)
1716  if run_card_backup is not None:
1717  mglog.info('Keeping backup of original run card at '+run_card_backup)
1718  run_card_old = run_card_backup
1719  else:
1720  run_card_old = run_card_input+'.old_to_be_deleted'
1721  mglog.debug('Modifying runcard settings: '+str(settings_lower))
1722  if os.path.isfile(run_card_old):
1723  os.unlink(run_card_old) # delete old backup
1724  os.rename(run_card_input, run_card_old) # change name of original card
1725 
1726  oldCard = open(run_card_old, 'r')
1727  newCard = open(process_dir+'/Cards/run_card.dat', 'w')
1728  used_settings = []
1729  for line in iter(oldCard):
1730  if not line.strip().startswith('#'): # line commented out
1731  command = line.split('!', 1)[0]
1732  comment = line.split('!', 1)[1] if '!' in line else ''
1733  if '=' in command:
1734  setting = command.split('=')[-1] #.strip()
1735  stripped_setting = setting.strip()
1736  oldValue = '='.join(command.split('=')[:-1])
1737  if stripped_setting.lower() in settings_lower:
1738  # if setting set to 'None' it will be removed from run_card
1739  if settings_lower[stripped_setting.lower()] is None:
1740  line=''
1741  mglog.info('Removing '+stripped_setting+'.')
1742  used_settings += [ stripped_setting.lower() ]
1743  else:
1744  if stripped_setting.lower() == 'custom_fcts':
1745  # Overwrite completely to avoid duplicating in custom_fcts, else MadGraph will crash
1746  line = ' '+str(settings_lower[stripped_setting.lower()])+' = '+setting
1747  if comment != '':
1748  line += ' !'+comment
1749  else:
1750  line = oldValue.replace(oldValue.strip(), str(settings_lower[stripped_setting.lower()]))+'='+setting
1751  if comment != '':
1752  line += ' !' + comment
1753  mglog.info('Setting '+stripped_setting+' = '+str(settings_lower[stripped_setting.lower()]))
1754  used_settings += [ stripped_setting.lower() ]
1755  newCard.write(line.strip()+'\n')
1756 
1757  # Check whether mcatnlo_delta is applied to setup pythia8 path
1758  if 'mcatnlo_delta' in settings_lower:
1759  if settings_lower['mcatnlo_delta'] == 'True':
1760  modify_config_card(process_dir=process_dir,settings={'pythia8_path':os.getenv("PY8PATH")})
1761 
1762  # Clean up unused options
1763  for asetting in settings_lower:
1764  if asetting in used_settings:
1765  continue
1766  if settings_lower[asetting] is None:
1767  continue
1768  mglog.info('Option '+asetting+' was not in the default run_card (normal for hidden options). Adding by hand a setting to '+str(settings_lower[asetting]) )
1769  newCard.write( ' '+str(settings_lower[asetting])+' = '+str(asetting)+'\n')
1770  # close files
1771  oldCard.close()
1772  newCard.close()
1773  mglog.info('Finished modification of run card.')
1774  if run_card_backup is None:
1775  os.unlink(run_card_old)
1776 
1777 
1778 def modify_config_card(config_card_backup=None,process_dir=MADGRAPH_GRIDPACK_LOCATION,settings={},set_commented=True):
1779  """Build a new configuration from an existing one.
1780  This function can get a fresh runcard from DATAPATH or start from the process directory.
1781  Settings is a dictionary of keys (no spaces needed) and values to replace.
1782  """
1783  # Check for the default config card location
1784  config_card=get_default_config_card(process_dir=process_dir)
1785 
1786  # The format is similar to the run card, but backwards
1787  mglog.info('Modifying config card located at '+config_card)
1788  if config_card_backup is not None:
1789  mglog.info('Keeping backup of original config card at '+config_card_backup)
1790  config_card_old = config_card_backup
1791  else:
1792  config_card_old = config_card+'.old_to_be_deleted'
1793  mglog.debug('Modifying config card settings: '+str(settings))
1794  if os.path.isfile(config_card_old):
1795  os.unlink(config_card_old) # delete old backup
1796  os.rename(config_card, config_card_old) # change name of original card
1797 
1798  oldCard = open(config_card_old, 'r')
1799  newCard = open(config_card, 'w')
1800  used_settings = []
1801  for line in iter(oldCard):
1802  lmod = line if set_commented else line.split('#')[0]
1803  if '=' in lmod:
1804  modified = False
1805  for setting in settings:
1806  if setting not in lmod:
1807  continue
1808  # Assume we hit
1809  mglog.info('Setting '+setting.strip()+' to '+str(settings[setting]))
1810  newCard.write(' '+str(setting.strip())+' = '+str(settings[setting])+'\n')
1811  used_settings += [ setting.strip() ]
1812  modified = True
1813  break
1814  if modified:
1815  continue
1816  newCard.write(line)
1817 
1818  # Clean up unused options
1819  for asetting in settings:
1820  if asetting in used_settings:
1821  continue
1822  if settings[asetting] is None:
1823  continue
1824  mglog.warning('Option '+asetting+' was not in the default config card. Adding by hand a setting to '+str(settings[asetting]) )
1825  newCard.write(' '+str(asetting)+' = '+str(settings[asetting])+'\n')
1826  # close files
1827  oldCard.close()
1828  newCard.close()
1829  mglog.info('Finished modification of config card.')
1830  if config_card_backup is None:
1831  os.unlink(config_card_old)
1832 
1833 
1834 
1835 def get_cluster_type(process_dir=MADGRAPH_GRIDPACK_LOCATION):
1836  card_in = open(get_default_config_card(process_dir=process_dir),'r')
1837  for l in card_in.readlines():
1838  if 'cluster_type' not in l.split('#')[0]:
1839  continue
1840  cluster_type = l.split('#')[0].split('=')[1]
1841  mglog.info('Returning cluster type: '+cluster_type)
1842  return cluster_type
1843  return None
1844 
1845 
1846 
1847 def run_card_consistency_check(isNLO=False,process_dir='.'):
1848  cardpath=process_dir+'/Cards/run_card.dat'
1849  mydict=getDictFromCard(cardpath)
1850  global my_MGC_instance
1851  # We should always use event_norm = average [AGENE-1725] otherwise Pythia cross sections are wrong
1852  # Modification: average or bias is ok; sum is incorrect. Change the test to set sum to average
1853  if checkSetting('event_norm','sum',mydict):
1854  modify_run_card(process_dir=process_dir,settings={'event_norm':'average'},skipBaseFragment=True)
1855  mglog.warning("setting event_norm to average, there is basically no use case where event_norm=sum is a good idea")
1856 
1857  if not isNLO:
1858  #Check CKKW-L setting
1859  if 'ktdurham' in mydict and float(mydict['ktdurham']) > 0 and int(mydict['ickkw']) != 0:
1860  log='Bad combination of settings for CKKW-L merging! ktdurham=%s and ickkw=%s.'%(mydict['ktdurham'],mydict['ickkw'])
1861  mglog.error(log)
1862  raise RuntimeError(log)
1863 
1864  # Check if user is trying to use deprecated syscalc arguments with the other systematics script
1865  if 'systematics_program' not in mydict or mydict['systematics_program']=='systematics':
1866  syscalc_settings=['sys_pdf', 'sys_scalefact', 'sys_alpsfact', 'sys_matchscale']
1867  found_syscalc_setting=False
1868  for s in syscalc_settings:
1869  if s in mydict:
1870  mglog.warning('Using syscalc setting '+s+' with new systematics script. Systematics script is default from 2.6.2 and steered differently (https://cp3.irmp.ucl.ac.be/projects/madgraph/wiki/Systematics#Systematicspythonmodule)')
1871  found_syscalc_setting=True
1872  if found_syscalc_setting:
1873  syst_arguments=MadGraphControl.MadGraphSystematicsUtils.convertSysCalcArguments(mydict)
1874  mglog.info('Converted syscalc arguments to systematics arguments: '+syst_arguments)
1875  syst_settings_update={'systematics_arguments':syst_arguments}
1876  for s in syscalc_settings:
1877  syst_settings_update[s]=None
1878  modify_run_card(process_dir=process_dir,settings=syst_settings_update,skipBaseFragment=True)
1879 
1880 
1881  # usually the pdf and systematics should be set during modify_run_card
1882  # but check again in case the user did not call the function or provides a different card here
1883  mglog.info('Checking PDF and systematics settings')
1884  if not MadGraphControl.MadGraphSystematicsUtils.base_fragment_setup_check(MADGRAPH_PDFSETTING,mydict,isNLO):
1885  # still need to set pdf and systematics
1886  syst_settings=MadGraphControl.MadGraphSystematicsUtils.get_pdf_and_systematic_settings(MADGRAPH_PDFSETTING,isNLO)
1887  modify_run_card(process_dir=process_dir,settings=syst_settings,skipBaseFragment=True)
1888 
1889  mydict_new=getDictFromCard(cardpath)
1890  if 'systematics_arguments' in mydict_new:
1891  systematics_arguments=MadGraphControl.MadGraphSystematicsUtils.parse_systematics_arguments(mydict_new['systematics_arguments'])
1892  if 'weight_info' not in systematics_arguments:
1893  mglog.info('Enforcing systematic weight name convention')
1894  dyn = None
1895  if '--dyn' in systematics_arguments or ' dyn' in systematics_arguments:
1896  if '--dyn' in systematics_arguments:
1897  dyn = systematics_arguments.split('--dyn')[1]
1898  if ' dyn' in systematics_arguments:
1899  dyn = systematics_arguments.split(' dyn')[1]
1900  dyn = dyn.replace('\'',' ').replace('=',' ').split()[0]
1901  if dyn is not None and len(dyn.split(','))>1:
1902  systematics_arguments['weight_info']=MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO_ALTDYNSCALES
1903  else:
1904  systematics_arguments['weight_info']=MadGraphControl.MadGraphSystematicsUtils.SYSTEMATICS_WEIGHT_INFO
1905  modify_run_card(process_dir=process_dir,settings={'systematics_arguments':MadGraphControl.MadGraphSystematicsUtils.write_systematics_arguments(systematics_arguments)},skipBaseFragment=True)
1906 
1907  if not isNLO:
1908  if 'python_seed' not in mydict:
1909  mglog.warning('No python seed set in run_card -- adding one with same value as iseed')
1910  modify_run_card(process_dir=process_dir,settings={'python_seed':mydict['iseed']},skipBaseFragment=True)
1911 
1912  # consistency check of 4/5 flavour shceme settings
1913  FS_updates={}
1914  proton_5flav = False
1915  jet_5flav = False
1916  with open(process_dir+'/Cards/proc_card_mg5.dat', 'r') as file:
1917  content = file.readlines()
1918  for rawline in content:
1919  line = rawline.split('#')[0]
1920  if line.startswith("define p"):
1921  if ('b' in line.split() and 'b~' in line.split()) or ('5' in line.split() and '-5' in line.split()):
1922  proton_5flav = True
1923  if 'j' in line.split() and jet_5flav:
1924  proton_5flav = True
1925  if line.startswith("define j"):
1926  if ('b' in line.split() and 'b~' in line.split()) or ('5' in line.split() and '-5' in line.split()):
1927  jet_5flav = True
1928  if 'p' in line.split() and proton_5flav:
1929  jet_5flav = True
1930  if proton_5flav or jet_5flav:
1931  FS_updates['asrwgtflavor'] = 5
1932  if not proton_5flav:
1933  mglog.warning('Found 5-flavour jets but 4-flavour proton. This is inconsistent - please pick one.')
1934  mglog.warning('Will proceed assuming 5-flavour scheme.')
1935  if not jet_5flav:
1936  mglog.warning('Found 5-flavour protons but 4-flavour jets. This is inconsistent - please pick one.')
1937  mglog.warning('Will proceed assuming 5-flavour scheme.')
1938  else:
1939  FS_updates['asrwgtflavor'] = 4
1940 
1941  if len(FS_updates)==0:
1942  mglog.warning(f'Could not identify 4- or 5-flavor scheme from process card {process_dir}/Cards/proc_card_mg5.dat')
1943 
1944  if 'asrwgtflavor' in mydict or 'maxjetflavor' in mydict or 'pdgs_for_merging_cut' in mydict:
1945  if FS_updates['asrwgtflavor'] == 5:
1946  # Process card says we are in the five-flavor scheme
1947  if ('asrwgtflavor' in mydict and int(mydict['asrwgtflavor']) != 5) or ('maxjetflavor' in mydict and int(mydict['maxjetflavor']) != 5) or ('pdgs_for_merging_cut' in mydict and '5' not in mydict['pdgs_for_merging_cut']):
1948  # Inconsistent setting detected; warn the users and correct the settings
1949  mglog.warning('b and b~ included in p and j for 5-flavor scheme but run card settings are inconsistent; adjusting run card')
1950  run_card_updates = {'asrwgtflavor': 5, 'maxjetflavor': 5, 'pdgs_for_merging_cut': '1, 2, 3, 4, 5, 21'}
1951  modify_run_card(process_dir=process_dir,settings=run_card_updates,skipBaseFragment=True)
1952  modify_param_card(process_dir=process_dir, params={'MASS': {'5': '0.000000e+00'}})
1953  else:
1954  mglog.debug('Consistent 5-flavor scheme setup detected.')
1955  if FS_updates['asrwgtflavor'] == 4:
1956  # Process card says we are in the four-flavor scheme
1957  if ('asrwgtflavor' in mydict and int(mydict['asrwgtflavor']) != 4) or ('maxjetflavor' in mydict and int(mydict['maxjetflavor']) != 4) or ('pdgs_for_merging_cut' in mydict and '5' in mydict['pdgs_for_merging_cut']):
1958  # Inconsistent setting detected; warn the users and correct the settings
1959  mglog.warning('b and b~ not included in p and j (4-flavor scheme) but run card settings are inconsistent; adjusting run card')
1960  run_card_updates = {'asrwgtflavor': 4, 'maxjetflavor': 4, 'pdgs_for_merging_cut': '1, 2, 3, 4, 21'}
1961  modify_run_card(process_dir=process_dir,settings=run_card_updates,skipBaseFragment=True)
1962  modify_param_card(process_dir=process_dir, params={'MASS': {'5': '4.700000e+00'}})
1963  else:
1964  mglog.debug('Consistent 4-flavor scheme setup detected.')
1965  else:
1966  # Flavor scheme setup is missing, adding by hand
1967  if FS_updates['asrwgtflavor'] == 4:
1968  # Warn the users and add the settings according to process card
1969  mglog.warning('Flavor scheme setup is missing, adding by hand according to process card - b and b~ not included in p and j, 4-flavor scheme setup will be used; adjusting run card.')
1970  run_card_updates = {'asrwgtflavor': 4, 'maxjetflavor': 4, 'pdgs_for_merging_cut': '1, 2, 3, 4, 21'}
1971  modify_run_card(process_dir=process_dir,settings=run_card_updates,skipBaseFragment=True)
1972  modify_param_card(process_dir=process_dir, params={'MASS': {'5': '4.700000e+00'}})
1973  elif FS_updates['asrwgtflavor'] == 5:
1974  mglog.warning('Flavor scheme setup is missing, adding by hand according to process card - b and b~ included in p and j, 5-flavor scheme setup will be used; adjusting run card.')
1975  run_card_updates = {'asrwgtflavor': 5, 'maxjetflavor': 5, 'pdgs_for_merging_cut': '1, 2, 3, 4, 5, 21'}
1976  modify_run_card(process_dir=process_dir,settings=run_card_updates,skipBaseFragment=True)
1977  modify_param_card(process_dir=process_dir, params={'MASS': {'5': '0.000000e+00'}})
1978 
1979  mglog.info('Finished checking run card - All OK!')
1980 
1981 def add_reweighting(run_name,reweight_card=None,process_dir=MADGRAPH_GRIDPACK_LOCATION):
1982  mglog.info('Running reweighting module on existing events')
1983  if reweight_card is not None:
1984  mglog.info('Copying new reweight card from '+reweight_card)
1985  shutil.move(reweight_card,process_dir+'/Cards/reweight_card.dat')
1986  reweight_cmd='{}/bin/madevent reweight {} -f'.format(process_dir,run_name)
1987  global MADGRAPH_CATCH_ERRORS
1988  reweight = stack_subprocess([python]+reweight_cmd.split(),stdin=subprocess.PIPE,stderr=subprocess.PIPE if MADGRAPH_CATCH_ERRORS else None)
1989  (out,err) = reweight.communicate()
1990  error_check(err,reweight.returncode)
1991  mglog.info('Finished reweighting')
1992 
1993 
1994 
1995 
1996 def ls_dir(directory):
1997  mglog.info('For your information, ls of '+directory+':')
1998  mglog.info( sorted( os.listdir( directory ) ) )
1999 
2000 # Final import of some code used in these functions
2001 import MadGraphControl.MadGraphSystematicsUtils
2002 
2003 # To be removed once we moved past MG5 3.3.1
2004 def fix_fks_makefile(process_dir):
2005  makefile_fks=process_dir+'/SubProcesses/makefile_fks_dir'
2006  mglog.info('Fixing '+makefile_fks)
2007  shutil.move(makefile_fks,makefile_fks+'_orig')
2008  fin=open(makefile_fks+'_orig')
2009  fout=open(makefile_fks,'w')
2010  edit=False
2011  for line in fin:
2012  if 'FKSParams.mod' in line:
2013  fout.write(line.replace('FKSParams.mod','FKSParams.o'))
2014  edit=True
2015  elif edit and 'driver_mintFO' in line:
2016  fout.write('driver_mintFO.o: weight_lines.o mint_module.o FKSParams.o\n')
2017  elif edit and 'genps_fks.o' in line:
2018  fout.write('genps_fks.o: mint_module.o FKSParams.o\n')
2019  elif edit and 'test_soft_col_limits' in line:
2020  fout.write(line)
2021  fout.write('madfks_plot.o: mint_module.o\n')
2022  fout.write('cluster.o: weight_lines.o\n')
2023  else:
2024  fout.write(line)
2025  fin.close()
2026  fout.close()
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename R::value_type > sorted(const R &r, PROJ proj={})
Helper function to create a sorted vector from an unsorted range.
replace
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition: hcg.cxx:307
python.LHAPDFsupport.get_LHAPDF_PATHS
def get_LHAPDF_PATHS()
Definition: LHAPDFsupport.py:17
createLinkingScheme.iter
iter
Definition: createLinkingScheme.py:62
vtune_athena.format
format
Definition: vtune_athena.py:14
python.MadGraphUtilsHelpers.checkSettingIsTrue
def checkSettingIsTrue(key_, mydict_)
Definition: MadGraphUtilsHelpers.py:52
python.MadGraphUtils.modify_param_card
def modify_param_card(param_card_input=None, param_card_backup=None, process_dir=MADGRAPH_GRIDPACK_LOCATION, params={}, output_location=None)
Definition: MadGraphUtils.py:1443
python.MadGraphUtilsHelpers.is_NLO_run
def is_NLO_run(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtilsHelpers.py:268
python.MadGraphUtils.arrange_output
def arrange_output(process_dir=MADGRAPH_GRIDPACK_LOCATION, lhe_version=None, saveProcDir=False, runArgs=None, fixEventWeightsForBridgeMode=False)
Definition: MadGraphUtils.py:919
python.MadGraphUtilsHelpers.get_runArgs_info
def get_runArgs_info(runArgs)
Definition: MadGraphUtilsHelpers.py:105
python.MadGraphUtils.setupLHAPDF
def setupLHAPDF(process_dir=None, extlhapath=None, allow_links=True)
Definition: MadGraphUtils.py:561
python.MadGraphUtils.ls_dir
def ls_dir(directory)
Definition: MadGraphUtils.py:1996
python.MadGraphUtilsHelpers.checkSetting
def checkSetting(key_, value_, mydict_)
Definition: MadGraphUtilsHelpers.py:44
python.MadGraphUtilsHelpers.setup_path_protection
def setup_path_protection()
Definition: MadGraphUtilsHelpers.py:234
upper
int upper(int c)
Definition: LArBadChannelParser.cxx:49
python.MadGraphUtils.update_lhe_file
def update_lhe_file(lhe_file_old, param_card_old=None, lhe_file_new=None, masses={}, delete_old_lhe=True)
Definition: MadGraphUtils.py:1321
python.MadGraphParamHelpers.check_PMG_updates
def check_PMG_updates(process_dir)
Definition: MadGraphParamHelpers.py:154
python.MadGraphUtils.new_process
def new_process(process='generate p p > t t~\noutput -f', plugin=None, keepJpegs=False, usePMGSettings=False)
Definition: MadGraphUtils.py:81
python.MadGraphUtils.modify_config_card
def modify_config_card(config_card_backup=None, process_dir=MADGRAPH_GRIDPACK_LOCATION, settings={}, set_commented=True)
Definition: MadGraphUtils.py:1778
python.MadGraphUtils.check_reweight_card
def check_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:1285
python.MadGraphUtils.get_default_runcard
def get_default_runcard(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:87
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.MadGraphUtils.generate
def generate(process_dir='PROC_mssm_0', grid_pack=False, gridpack_compile=False, extlhapath=None, required_accuracy=0.01, runArgs=None, bias_module=None, requirePMGSettings=False)
Definition: MadGraphUtils.py:108
python.MadGraphUtils.print_cards
def print_cards(proc_card='proc_card_mg5.dat', run_card=None, param_card=None, madspin_card=None, reweight_card=None, warn_on_missing=True)
Definition: MadGraphUtils.py:1598
python.MadGraphUtils.is_gen_from_gridpack
def is_gen_from_gridpack()
Definition: MadGraphUtils.py:1643
python.MadGraphUtils.get_expected_systematic_names
def get_expected_systematic_names(syst_setting)
Definition: MadGraphUtils.py:1211
print
void print(char *figname, TCanvas *c1)
Definition: TRTCalib_StrawStatusPlots.cxx:26
python.MadGraphUtils.add_madspin
def add_madspin(madspin_card=None, process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:776
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
python.MadGraphUtils.generate_prep
def generate_prep(process_dir)
Definition: MadGraphUtils.py:56
python.MadGraphUtils.modify_run_card
def modify_run_card(run_card_input=None, run_card_backup=None, process_dir=MADGRAPH_GRIDPACK_LOCATION, runArgs=None, settings={}, skipBaseFragment=False)
Definition: MadGraphUtils.py:1656
python.MadGraphUtils.setup_bias_module
def setup_bias_module(bias_module, process_dir)
Definition: MadGraphUtils.py:1229
python.MadGraphUtilsHelpers.get_default_config_card
def get_default_config_card(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtilsHelpers.py:253
python.MadGraphUtils.run_card_consistency_check
def run_card_consistency_check(isNLO=False, process_dir='.')
Definition: MadGraphUtils.py:1847
python.MadGraphUtils.setupFastjet
def setupFastjet(process_dir=None)
Definition: MadGraphUtils.py:529
python.MadGraphUtilsHelpers.error_check
def error_check(errors_a, return_code)
Definition: MadGraphUtilsHelpers.py:119
python.MadGraphUtilsHelpers.checkSettingExists
def checkSettingExists(key_, mydict_)
Definition: MadGraphUtilsHelpers.py:59
Trk::open
@ open
Definition: BinningType.h:40
python.MadGraphUtils.fix_fks_makefile
def fix_fks_makefile(process_dir)
Definition: MadGraphUtils.py:2004
python.CaloAddPedShiftConfig.int
int
Definition: CaloAddPedShiftConfig.py:45
python.MadGraphUtils.setNCores
def setNCores(process_dir, Ncores=None)
Definition: MadGraphUtils.py:709
python.MadGraphUtils.generate_from_gridpack
def generate_from_gridpack(runArgs=None, extlhapath=None, gridpack_compile=None, requirePMGSettings=False)
Definition: MadGraphUtils.py:327
python.JOsupport.get_physics_short
def get_physics_short()
Definition: JOsupport.py:12
python.MadGraphUtils.get_expected_reweight_names
def get_expected_reweight_names(reweight_card_loc)
Definition: MadGraphUtils.py:1195
python.MadGraphUtils.get_mg5_executable
def get_mg5_executable()
Definition: MadGraphUtils.py:727
python.MadGraphUtils.get_cluster_type
def get_cluster_type(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:1835
str
Definition: BTagTrackIpAccessor.cxx:11
python.MadGraphUtils.get_reweight_card
def get_reweight_card(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:1279
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:801
python.MadGraphUtils.madspin_on_lhe
def madspin_on_lhe(input_LHE, madspin_card, runArgs=None, keep_original=False)
Definition: MadGraphUtils.py:831
python.MadGraphUtils.print_cards_from_dir
def print_cards_from_dir(process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:1592
python.LHAPDFsupport.get_lhapdf_id_and_name
def get_lhapdf_id_and_name(pdf)
Definition: LHAPDFsupport.py:33
python.MadGraphUtils.add_reweighting
def add_reweighting(run_name, reweight_card=None, process_dir=MADGRAPH_GRIDPACK_LOCATION)
Definition: MadGraphUtils.py:1981
python.MadGraphUtils.stack_subprocess
def stack_subprocess(command, **kwargs)
Definition: MadGraphUtils.py:47
python.MadGraphUtils.add_lifetimes
def add_lifetimes(process_dir, threshold=None)
Definition: MadGraphUtils.py:734
python.MadGraphUtils.find_key_and_update
def find_key_and_update(akey, dictionary)
Definition: MadGraphUtils.py:1428
Trk::split
@ split
Definition: LayerMaterialProperties.h:38
python.MadGraphUtilsHelpers.getDictFromCard
def getDictFromCard(card_loc, lowercase=False)
Definition: MadGraphUtilsHelpers.py:14
python.LArMinBiasAlgConfig.float
float
Definition: LArMinBiasAlgConfig.py:65